bash_builtins/
convert.rs

1//! Traits for conversions between types.
2//!
3//! This module implements the trait [`FromWordPointer`] to convert [`CStr`]
4//! instances to another type.
5//!
6//! [`CStr`]: std::ffi::CStr
7
8use std::ffi::CStr;
9use std::fmt;
10use std::str::{FromStr, Utf8Error};
11
12#[cfg(unix)]
13use std::os::unix::ffi::{OsStrExt, OsStringExt};
14
15#[cfg(unix)]
16use std::ffi::{OsStr, OsString};
17
18/// Parse a value from a [`CStr`] instance.
19///
20/// [`CStr`]: std::ffi::CStr
21pub trait FromWordPointer<'a>: Sized + 'a {
22    /// The type returned in the event of a conversion error.
23    type Err: std::fmt::Display;
24
25    /// Parse the value in `s` to return a value of this type.
26    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err>;
27
28    /// Character to build the string for `getopt` to indicate that the option
29    /// has an argument.
30    ///
31    /// This should be overridden only by the impl of `Option`.
32    #[doc(hidden)]
33    const OPTSTR_ARGUMENT: u8 = b':';
34
35    /// Try to extract the value from a raw argument.
36    #[doc(hidden)]
37    fn extract_value(arg: Option<&'a CStr>) -> crate::Result<Self> {
38        match arg {
39            None => {
40                crate::log::missing_argument();
41                Err(crate::Error::Usage)
42            }
43
44            Some(arg) => Self::from_cstr(arg).map_err(|e| {
45                crate::error!("{:?}: {}", arg, e);
46                crate::Error::Usage
47            }),
48        }
49    }
50}
51
52// For non-required arguments.
53impl<'a, T: FromWordPointer<'a>> FromWordPointer<'a> for Option<T> {
54    const OPTSTR_ARGUMENT: u8 = b';';
55
56    type Err = <T as FromWordPointer<'a>>::Err;
57
58    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
59        <T as FromWordPointer<'a>>::from_cstr(s).map(Some)
60    }
61
62    fn extract_value(arg: Option<&'a CStr>) -> crate::Result<Self> {
63        match arg {
64            None => Ok(None),
65
66            Some(arg) => match <T as FromWordPointer<'a>>::from_cstr(arg) {
67                Ok(v) => Ok(Some(v)),
68
69                Err(e) => {
70                    crate::error!("{:?}: {}", arg, e);
71                    Err(crate::Error::Usage)
72                }
73            },
74        }
75    }
76}
77
78// Standard types.
79
80impl<'a> FromWordPointer<'a> for &'a str {
81    type Err = Utf8Error;
82
83    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
84        s.to_str()
85    }
86}
87
88impl<'a> FromWordPointer<'a> for String {
89    type Err = Utf8Error;
90
91    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
92        s.to_str().map(str::to_owned)
93    }
94}
95
96#[cfg(unix)]
97impl<'a> FromWordPointer<'a> for &'a std::path::Path {
98    type Err = std::convert::Infallible;
99
100    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
101        Ok(std::path::Path::new(OsStr::from_bytes(s.to_bytes())))
102    }
103}
104
105#[cfg(unix)]
106impl<'a> FromWordPointer<'a> for std::path::PathBuf {
107    type Err = std::convert::Infallible;
108
109    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
110        Ok(Self::from(OsStr::from_bytes(s.to_bytes())))
111    }
112}
113
114#[cfg(unix)]
115impl<'a> FromWordPointer<'a> for &'a OsStr {
116    type Err = std::convert::Infallible;
117
118    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
119        Ok(OsStr::from_bytes(s.to_bytes()))
120    }
121}
122
123#[cfg(unix)]
124impl<'a> FromWordPointer<'a> for OsString {
125    type Err = std::convert::Infallible;
126
127    fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
128        Ok(OsString::from_vec(s.to_bytes().into()))
129    }
130}
131
132// Types that have to be converted to a string as an intermediate step.
133
134#[doc(hidden)]
135pub enum Utf8OrParseError<T> {
136    Utf8(Utf8Error),
137    Parse(T),
138}
139
140impl<T: fmt::Display> fmt::Display for Utf8OrParseError<T> {
141    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
142        match self {
143            Utf8OrParseError::Utf8(e) => e.fmt(fmt),
144            Utf8OrParseError::Parse(e) => e.fmt(fmt),
145        }
146    }
147}
148
149macro_rules! impl_primitive {
150    ($ty:ty) => {
151        impl<'a> FromWordPointer<'a> for $ty {
152            type Err = Utf8OrParseError<<$ty as FromStr>::Err>;
153
154            fn from_cstr(s: &'a CStr) -> Result<Self, Self::Err> {
155                let s = s.to_str().map_err(Utf8OrParseError::Utf8)?;
156                <$ty as FromStr>::from_str(s).map_err(Utf8OrParseError::Parse)
157            }
158        }
159    };
160}
161
162impl_primitive!(bool);
163impl_primitive!(char);
164impl_primitive!(f32);
165impl_primitive!(f64);
166impl_primitive!(i8);
167impl_primitive!(i16);
168impl_primitive!(i32);
169impl_primitive!(i64);
170impl_primitive!(i128);
171impl_primitive!(isize);
172impl_primitive!(u8);
173impl_primitive!(u16);
174impl_primitive!(u32);
175impl_primitive!(u64);
176impl_primitive!(u128);
177impl_primitive!(usize);