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