delimited list
This commit is contained in:
parent
b459d08a74
commit
7b8f351b21
1 changed files with 76 additions and 64 deletions
130
src/fs/mount.rs
130
src/fs/mount.rs
|
@ -376,53 +376,54 @@ impl From<UntypedIdRange> for GidRange {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: finish implementation to solve options delimiter problem
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DelimitedString {
|
||||
inner: Vec<OsString>,
|
||||
delimiter: char,
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct DelimitedList<const D: char, T>(Vec<T>);
|
||||
|
||||
impl DelimitedString {
|
||||
pub fn new<T, I>(inner: I, delimiter: char) -> Self
|
||||
where
|
||||
T: Into<OsString>,
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
Self {
|
||||
inner: inner.into_iter().map(Into::into).collect(),
|
||||
delimiter,
|
||||
impl<const D: char, T: FromStr> FromStr for DelimitedList<D, T> {
|
||||
type Err = T::Err;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(
|
||||
s.split(D)
|
||||
.map(T::from_str)
|
||||
.collect::<Result<Vec<T>, Self::Err>>()?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_str(s: &str, delimiter: char) -> Self {
|
||||
Self::from_iter(s.split(delimiter), delimiter)
|
||||
}
|
||||
|
||||
pub fn from_iter<I, T>(iter: I, delimiter: char) -> Self
|
||||
where
|
||||
T: Into<OsString>,
|
||||
I: IntoIterator<Item = T>,
|
||||
{
|
||||
Self::new(iter, delimiter)
|
||||
impl<const D: char, T: Into<T>> FromIterator<T> for DelimitedList<D, T> {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<OsStr> for DelimitedString {
|
||||
fn as_ref(&self) -> &OsStr {
|
||||
&self.inner.join(self.delimiter)
|
||||
impl<'a, const D: char, T> IntoIterator for &'a DelimitedList<D, T> {
|
||||
type Item = &'a T;
|
||||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for DelimitedString {
|
||||
impl<const D: char, T> IntoIterator for DelimitedList<D, T> {
|
||||
type Item = T;
|
||||
type IntoIter = IntoIter<T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const D: char, T: Display> Display for DelimitedList<D, T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut iter = self.inner.iter();
|
||||
let mut iter = self.0.iter();
|
||||
|
||||
if let Some(head) = iter.next() {
|
||||
write!(f, "{}", head.display())?;
|
||||
write!(f, "{head}")?;
|
||||
|
||||
for item in iter {
|
||||
write!(f, "{}", item.display())?;
|
||||
write!(f, "{D}{item}")?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -537,7 +538,7 @@ impl<'a, T> IntoIterator for &'a Options<T> {
|
|||
type IntoIter = Iter<'a, T>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.options.as_slice().into_iter()
|
||||
self.options.as_slice().iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -554,13 +555,13 @@ impl<T> From<T> for Options<T> {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct Sources(Options<OptionsSource>);
|
||||
pub struct Sources(DelimitedList<',', OptionsSource>);
|
||||
|
||||
impl FromStr for Sources {
|
||||
type Err = ParseOptionsSourceError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(Options::from_str(s)?))
|
||||
Ok(Self(DelimitedList::<',', OptionsSource>::from_str(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,13 +583,13 @@ impl From<Infallible> for ParseUncheckedOptionsError {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct UncheckedOptions(Options<DisplayString>);
|
||||
pub struct UncheckedOptions(DelimitedList<',', DisplayString>);
|
||||
|
||||
impl FromStr for UncheckedOptions {
|
||||
type Err = ParseUncheckedOptionsError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self(Options::<DisplayString>::from_str(s)?))
|
||||
Ok(Self(DelimitedList::<',', DisplayString>::from_str(s)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -606,24 +607,26 @@ impl From<String> for UncheckedOptions {
|
|||
|
||||
impl<T: AsRef<OsStr>> FromIterator<T> for UncheckedOptions {
|
||||
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
|
||||
Self(Options::from_iter(iter.into_iter().map(Into::into)))
|
||||
Self(DelimitedList::<',', DisplayString>::from_iter(
|
||||
iter.into_iter().map(Into::into),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<UncheckedOptions> for Options<DisplayString> {
|
||||
impl From<UncheckedOptions> for DelimitedList<',', DisplayString> {
|
||||
fn from(value: UncheckedOptions) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Options<DisplayString>> for UncheckedOptions {
|
||||
fn as_ref(&self) -> &Options<DisplayString> {
|
||||
impl AsRef<DelimitedList<',', DisplayString>> for UncheckedOptions {
|
||||
fn as_ref(&self) -> &DelimitedList<',', DisplayString> {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Options<DisplayString>> for UncheckedOptions {
|
||||
fn from(value: Options<DisplayString>) -> Self {
|
||||
impl From<DelimitedList<',', DisplayString>> for UncheckedOptions {
|
||||
fn from(value: DelimitedList<',', DisplayString>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
@ -721,7 +724,7 @@ pub enum MountOption {
|
|||
MakeDir(Option<Permissions>),
|
||||
NoMtab,
|
||||
OptionsMode(OptionsMode),
|
||||
OptionsSource(Options<OptionsSource>),
|
||||
OptionsSource(DelimitedList<',', OptionsSource>),
|
||||
OptionsSourceForce,
|
||||
OnlyOnce,
|
||||
Options(UncheckedOptions),
|
||||
|
@ -742,7 +745,7 @@ impl FromStr for MountOption {
|
|||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.trim().trim_start_matches('-');
|
||||
|
||||
let option = match s.split_once(|delim| delim == ' ' || delim == '=') {
|
||||
let option = match s.split_once([' ', '=']) {
|
||||
Some((option, value)) => (option, Some(value)),
|
||||
None => (s, None),
|
||||
};
|
||||
|
@ -764,7 +767,11 @@ impl FromStr for MountOption {
|
|||
)),
|
||||
("n" | "no-mtab", None) => Ok(Self::NoMtab),
|
||||
("options-mode", Some(mode)) => Ok(Self::OptionsMode(OptionsMode::from_str(mode)?)),
|
||||
("options-source", Some(source)) => Ok(Self::OptionsSource(Options::from_str(source)?)),
|
||||
("options-source", Some(source)) => {
|
||||
Ok(Self::OptionsSource(
|
||||
DelimitedList::<',', OptionsSource>::from_str(source)?,
|
||||
))
|
||||
}
|
||||
("options-source-force", None) => Ok(Self::OptionsSourceForce),
|
||||
("onlyonce", None) => Ok(Self::OnlyOnce),
|
||||
("o" | "options", Some(options)) => {
|
||||
|
@ -941,7 +948,7 @@ impl Display for MountOperation {
|
|||
pub struct Mount {
|
||||
device_id: DeviceId,
|
||||
mountpoint: PathBuf,
|
||||
options: Options<MountOption>,
|
||||
options: DelimitedList<',', MountOption>,
|
||||
}
|
||||
|
||||
impl Mount {
|
||||
|
@ -953,7 +960,9 @@ impl Mount {
|
|||
Self {
|
||||
device_id: source.into(),
|
||||
mountpoint: target.into(),
|
||||
options: Options::from_iter(options.into_iter().map(Into::into)),
|
||||
options: DelimitedList::<',', MountOption>::from_iter(
|
||||
options.into_iter().map(Into::into),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -996,10 +1005,13 @@ mod tests {
|
|||
|
||||
use enumflags2::BitFlags;
|
||||
|
||||
use crate::fs::{
|
||||
use crate::{
|
||||
fs::{
|
||||
id_mapping::UntypedIdRange,
|
||||
mount::{ParseDeviceIdTypeError, ParseOptionsSourceError},
|
||||
permission::Permissions,
|
||||
},
|
||||
prelude::DelimitedList,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -1115,18 +1127,18 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn options() {
|
||||
let opts = Options::<KeyValuePair>::from_str("key=value,keyword").unwrap();
|
||||
fn delimited_list() {
|
||||
type Kvp = KeyValuePair;
|
||||
type List = DelimitedList<',', Kvp>;
|
||||
|
||||
let opts = List::from_str("key=value,keyword").unwrap();
|
||||
|
||||
assert_eq!(
|
||||
opts,
|
||||
Options::new(
|
||||
vec![
|
||||
KeyValuePair::new("key", Some("value")),
|
||||
KeyValuePair::new("keyword", None::<&str>)
|
||||
],
|
||||
','
|
||||
)
|
||||
List::from_iter(vec![
|
||||
Kvp::new("key", Some("value")),
|
||||
Kvp::new("keyword", None::<&str>)
|
||||
])
|
||||
);
|
||||
|
||||
assert_eq!(opts.to_string(), "key=value,keyword");
|
||||
|
@ -1289,7 +1301,7 @@ mod tests {
|
|||
.assert(MountOption::OptionsMode(OptionsMode::Ignore));
|
||||
|
||||
Opts::from(("--options-source", "fstab")).assert(MountOption::OptionsSource(
|
||||
vec![OptionsSource::Fstab].into(),
|
||||
DelimitedList::from_iter(vec![OptionsSource::Fstab]),
|
||||
));
|
||||
|
||||
Opts::from("--options-source-force").assert(MountOption::OptionsSourceForce);
|
||||
|
|
Loading…
Add table
Reference in a new issue