Initial vendor packages
Signed-off-by: Valentin Popov <valentin@popov.link>
This commit is contained in:
		
							
								
								
									
										312
									
								
								vendor/clap_lex/src/ext.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										312
									
								
								vendor/clap_lex/src/ext.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,312 @@
 | 
			
		||||
use std::ffi::OsStr;
 | 
			
		||||
 | 
			
		||||
pub trait OsStrExt: private::Sealed {
 | 
			
		||||
    /// Converts to a string slice.
 | 
			
		||||
    fn try_str(&self) -> Result<&str, std::str::Utf8Error>;
 | 
			
		||||
    /// Returns `true` if the given pattern matches a sub-slice of
 | 
			
		||||
    /// this string slice.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Returns `false` if it does not.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let bananas = std::ffi::OsStr::new("bananas");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert!(bananas.contains("nana"));
 | 
			
		||||
    /// assert!(!bananas.contains("apples"));
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn contains(&self, needle: &str) -> bool;
 | 
			
		||||
    /// Returns the byte index of the first character of this string slice that
 | 
			
		||||
    /// matches the pattern.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Returns [`None`] if the pattern doesn't match.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let s = std::ffi::OsStr::new("Löwe 老虎 Léopard Gepardi");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(s.find("L"), Some(0));
 | 
			
		||||
    /// assert_eq!(s.find("é"), Some(14));
 | 
			
		||||
    /// assert_eq!(s.find("par"), Some(17));
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Not finding the pattern:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let s = std::ffi::OsStr::new("Löwe 老虎 Léopard");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(s.find("1"), None);
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn find(&self, needle: &str) -> Option<usize>;
 | 
			
		||||
    /// Returns a string slice with the prefix removed.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the string starts with the pattern `prefix`, returns substring after the prefix, wrapped
 | 
			
		||||
    /// in `Some`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// If the string does not start with `prefix`, returns `None`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// assert_eq!(OsStr::new("foo:bar").strip_prefix("foo:"), Some(OsStr::new("bar")));
 | 
			
		||||
    /// assert_eq!(OsStr::new("foo:bar").strip_prefix("bar"), None);
 | 
			
		||||
    /// assert_eq!(OsStr::new("foofoo").strip_prefix("foo"), Some(OsStr::new("foo")));
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn strip_prefix(&self, prefix: &str) -> Option<&OsStr>;
 | 
			
		||||
    /// Returns `true` if the given pattern matches a prefix of this
 | 
			
		||||
    /// string slice.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Returns `false` if it does not.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let bananas = std::ffi::OsStr::new("bananas");
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert!(bananas.starts_with("bana"));
 | 
			
		||||
    /// assert!(!bananas.starts_with("nana"));
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn starts_with(&self, prefix: &str) -> bool;
 | 
			
		||||
    /// An iterator over substrings of this string slice, separated by
 | 
			
		||||
    /// characters matched by a pattern.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// Simple patterns:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let v: Vec<_> = OsStr::new("Mary had a little lamb").split(" ").collect();
 | 
			
		||||
    /// assert_eq!(v, [OsStr::new("Mary"), OsStr::new("had"), OsStr::new("a"), OsStr::new("little"), OsStr::new("lamb")]);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let v: Vec<_> = OsStr::new("").split("X").collect();
 | 
			
		||||
    /// assert_eq!(v, [OsStr::new("")]);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let v: Vec<_> = OsStr::new("lionXXtigerXleopard").split("X").collect();
 | 
			
		||||
    /// assert_eq!(v, [OsStr::new("lion"), OsStr::new(""), OsStr::new("tiger"), OsStr::new("leopard")]);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let v: Vec<_> = OsStr::new("lion::tiger::leopard").split("::").collect();
 | 
			
		||||
    /// assert_eq!(v, [OsStr::new("lion"), OsStr::new("tiger"), OsStr::new("leopard")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// If a string contains multiple contiguous separators, you will end up
 | 
			
		||||
    /// with empty strings in the output:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let x = OsStr::new("||||a||b|c");
 | 
			
		||||
    /// let d: Vec<_> = x.split("|").collect();
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(d, &[OsStr::new(""), OsStr::new(""), OsStr::new(""), OsStr::new(""), OsStr::new("a"), OsStr::new(""), OsStr::new("b"), OsStr::new("c")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Contiguous separators are separated by the empty string.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let x = OsStr::new("(///)");
 | 
			
		||||
    /// let d: Vec<_> = x.split("/").collect();
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(d, &[OsStr::new("("), OsStr::new(""), OsStr::new(""), OsStr::new(")")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Separators at the start or end of a string are neighbored
 | 
			
		||||
    /// by empty strings.
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let d: Vec<_> = OsStr::new("010").split("0").collect();
 | 
			
		||||
    /// assert_eq!(d, &[OsStr::new(""), OsStr::new("1"), OsStr::new("")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// When the empty string is used as a separator, it panics
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```should_panic
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let f: Vec<_> = OsStr::new("rust").split("").collect();
 | 
			
		||||
    /// assert_eq!(f, &[OsStr::new(""), OsStr::new("r"), OsStr::new("u"), OsStr::new("s"), OsStr::new("t"), OsStr::new("")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Contiguous separators can lead to possibly surprising behavior
 | 
			
		||||
    /// when whitespace is used as the separator. This code is correct:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// let x = OsStr::new("    a  b c");
 | 
			
		||||
    /// let d: Vec<_> = x.split(" ").collect();
 | 
			
		||||
    ///
 | 
			
		||||
    /// assert_eq!(d, &[OsStr::new(""), OsStr::new(""), OsStr::new(""), OsStr::new(""), OsStr::new("a"), OsStr::new(""), OsStr::new("b"), OsStr::new("c")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// It does _not_ give you:
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```,ignore
 | 
			
		||||
    /// assert_eq!(d, &[OsStr::new("a"), OsStr::new("b"), OsStr::new("c")]);
 | 
			
		||||
    /// ```
 | 
			
		||||
    ///
 | 
			
		||||
    /// Use [`split_whitespace`] for this behavior.
 | 
			
		||||
    ///
 | 
			
		||||
    /// [`split_whitespace`]: str::split_whitespace
 | 
			
		||||
    fn split<'s, 'n>(&'s self, needle: &'n str) -> Split<'s, 'n>;
 | 
			
		||||
    /// Splits the string on the first occurrence of the specified delimiter and
 | 
			
		||||
    /// returns prefix before delimiter and suffix after delimiter.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Examples
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```
 | 
			
		||||
    /// use std::ffi::OsStr;
 | 
			
		||||
    /// use clap_lex::OsStrExt as _;
 | 
			
		||||
    /// assert_eq!(OsStr::new("cfg").split_once("="), None);
 | 
			
		||||
    /// assert_eq!(OsStr::new("cfg=").split_once("="), Some((OsStr::new("cfg"), OsStr::new(""))));
 | 
			
		||||
    /// assert_eq!(OsStr::new("cfg=foo").split_once("="), Some((OsStr::new("cfg"), OsStr::new("foo"))));
 | 
			
		||||
    /// assert_eq!(OsStr::new("cfg=foo=bar").split_once("="), Some((OsStr::new("cfg"), OsStr::new("foo=bar"))));
 | 
			
		||||
    /// ```
 | 
			
		||||
    fn split_once(&self, needle: &'_ str) -> Option<(&OsStr, &OsStr)>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl OsStrExt for OsStr {
 | 
			
		||||
    fn try_str(&self) -> Result<&str, std::str::Utf8Error> {
 | 
			
		||||
        let bytes = to_bytes(self);
 | 
			
		||||
        std::str::from_utf8(bytes)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn contains(&self, needle: &str) -> bool {
 | 
			
		||||
        self.find(needle).is_some()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn find(&self, needle: &str) -> Option<usize> {
 | 
			
		||||
        let bytes = to_bytes(self);
 | 
			
		||||
        (0..=self.len().checked_sub(needle.len())?)
 | 
			
		||||
            .find(|&x| bytes[x..].starts_with(needle.as_bytes()))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn strip_prefix(&self, prefix: &str) -> Option<&OsStr> {
 | 
			
		||||
        let bytes = to_bytes(self);
 | 
			
		||||
        bytes.strip_prefix(prefix.as_bytes()).map(|s| {
 | 
			
		||||
            // SAFETY:
 | 
			
		||||
            // - This came from `to_bytes`
 | 
			
		||||
            // - Since `prefix` is `&str`, any split will be along UTF-8 boundarie
 | 
			
		||||
            unsafe { to_os_str_unchecked(s) }
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    fn starts_with(&self, prefix: &str) -> bool {
 | 
			
		||||
        let bytes = to_bytes(self);
 | 
			
		||||
        bytes.starts_with(prefix.as_bytes())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn split<'s, 'n>(&'s self, needle: &'n str) -> Split<'s, 'n> {
 | 
			
		||||
        assert_ne!(needle, "");
 | 
			
		||||
        Split {
 | 
			
		||||
            haystack: Some(self),
 | 
			
		||||
            needle,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn split_once(&self, needle: &'_ str) -> Option<(&OsStr, &OsStr)> {
 | 
			
		||||
        let start = self.find(needle)?;
 | 
			
		||||
        let end = start + needle.len();
 | 
			
		||||
        let haystack = to_bytes(self);
 | 
			
		||||
        let first = &haystack[0..start];
 | 
			
		||||
        let second = &haystack[end..];
 | 
			
		||||
        // SAFETY:
 | 
			
		||||
        // - This came from `to_bytes`
 | 
			
		||||
        // - Since `needle` is `&str`, any split will be along UTF-8 boundarie
 | 
			
		||||
        unsafe { Some((to_os_str_unchecked(first), to_os_str_unchecked(second))) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mod private {
 | 
			
		||||
    pub trait Sealed {}
 | 
			
		||||
 | 
			
		||||
    impl Sealed for std::ffi::OsStr {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Allow access to raw bytes
 | 
			
		||||
///
 | 
			
		||||
/// As the non-UTF8 encoding is not defined, the bytes only make sense when compared with
 | 
			
		||||
/// 7-bit ASCII or `&str`
 | 
			
		||||
///
 | 
			
		||||
/// # Compatibility
 | 
			
		||||
///
 | 
			
		||||
/// There is no guarantee how non-UTF8 bytes will be encoded, even within versions of this crate
 | 
			
		||||
/// (since its dependent on rustc)
 | 
			
		||||
fn to_bytes(s: &OsStr) -> &[u8] {
 | 
			
		||||
    // SAFETY:
 | 
			
		||||
    // - Lifetimes are the same
 | 
			
		||||
    // - Types are compatible (`OsStr` is effectively a transparent wrapper for `[u8]`)
 | 
			
		||||
    // - The primary contract is that the encoding for invalid surrogate code points is not
 | 
			
		||||
    //   guaranteed which isn't a problem here
 | 
			
		||||
    //
 | 
			
		||||
    // There is a proposal to support this natively (https://github.com/rust-lang/rust/pull/95290)
 | 
			
		||||
    // but its in limbo
 | 
			
		||||
    unsafe { std::mem::transmute(s) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Restore raw bytes as `OsStr`
 | 
			
		||||
///
 | 
			
		||||
/// # Safety
 | 
			
		||||
///
 | 
			
		||||
/// - `&[u8]` must either by a `&str` or originated with `to_bytes` within the same binary
 | 
			
		||||
/// - Any splits of the original `&[u8]` must be done along UTF-8 boundaries
 | 
			
		||||
unsafe fn to_os_str_unchecked(s: &[u8]) -> &OsStr {
 | 
			
		||||
    // SAFETY:
 | 
			
		||||
    // - Lifetimes are the same
 | 
			
		||||
    // - Types are compatible (`OsStr` is effectively a transparent wrapper for `[u8]`)
 | 
			
		||||
    // - The primary contract is that the encoding for invalid surrogate code points is not
 | 
			
		||||
    //   guaranteed which isn't a problem here
 | 
			
		||||
    //
 | 
			
		||||
    // There is a proposal to support this natively (https://github.com/rust-lang/rust/pull/95290)
 | 
			
		||||
    // but its in limbo
 | 
			
		||||
    std::mem::transmute(s)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub struct Split<'s, 'n> {
 | 
			
		||||
    haystack: Option<&'s OsStr>,
 | 
			
		||||
    needle: &'n str,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'s, 'n> Iterator for Split<'s, 'n> {
 | 
			
		||||
    type Item = &'s OsStr;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        let haystack = self.haystack?;
 | 
			
		||||
        match haystack.split_once(self.needle) {
 | 
			
		||||
            Some((first, second)) => {
 | 
			
		||||
                if !haystack.is_empty() {
 | 
			
		||||
                    debug_assert_ne!(haystack, second);
 | 
			
		||||
                }
 | 
			
		||||
                self.haystack = Some(second);
 | 
			
		||||
                Some(first)
 | 
			
		||||
            }
 | 
			
		||||
            None => {
 | 
			
		||||
                self.haystack = None;
 | 
			
		||||
                Some(haystack)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Split an `OsStr`
 | 
			
		||||
///
 | 
			
		||||
/// # Safety
 | 
			
		||||
///
 | 
			
		||||
/// `index` must be at a valid UTF-8 boundary
 | 
			
		||||
pub(crate) unsafe fn split_at(os: &OsStr, index: usize) -> (&OsStr, &OsStr) {
 | 
			
		||||
    let bytes = to_bytes(os);
 | 
			
		||||
    let (first, second) = bytes.split_at(index);
 | 
			
		||||
    (to_os_str_unchecked(first), to_os_str_unchecked(second))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										504
									
								
								vendor/clap_lex/src/lib.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										504
									
								
								vendor/clap_lex/src/lib.rs
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,504 @@
 | 
			
		||||
//! Minimal, flexible command-line parser
 | 
			
		||||
//!
 | 
			
		||||
//! As opposed to a declarative parser, this processes arguments as a stream of tokens.  As lexing
 | 
			
		||||
//! a command-line is not context-free, we rely on the caller to decide how to interpret the
 | 
			
		||||
//! arguments.
 | 
			
		||||
//!
 | 
			
		||||
//! # Examples
 | 
			
		||||
//!
 | 
			
		||||
//! ```rust
 | 
			
		||||
//! use std::path::PathBuf;
 | 
			
		||||
//! use std::ffi::OsStr;
 | 
			
		||||
//!
 | 
			
		||||
//! type BoxedError = Box<dyn std::error::Error + Send + Sync>;
 | 
			
		||||
//!
 | 
			
		||||
//! #[derive(Debug)]
 | 
			
		||||
//! struct Args {
 | 
			
		||||
//!     paths: Vec<PathBuf>,
 | 
			
		||||
//!     color: Color,
 | 
			
		||||
//!     verbosity: usize,
 | 
			
		||||
//! }
 | 
			
		||||
//!
 | 
			
		||||
//! #[derive(Debug)]
 | 
			
		||||
//! enum Color {
 | 
			
		||||
//!     Always,
 | 
			
		||||
//!     Auto,
 | 
			
		||||
//!     Never,
 | 
			
		||||
//! }
 | 
			
		||||
//!
 | 
			
		||||
//! impl Color {
 | 
			
		||||
//!     fn parse(s: Option<&OsStr>) -> Result<Self, BoxedError> {
 | 
			
		||||
//!         let s = s.map(|s| s.to_str().ok_or(s));
 | 
			
		||||
//!         match s {
 | 
			
		||||
//!             Some(Ok("always")) | Some(Ok("")) | None => {
 | 
			
		||||
//!                 Ok(Color::Always)
 | 
			
		||||
//!             }
 | 
			
		||||
//!             Some(Ok("auto")) => {
 | 
			
		||||
//!                 Ok(Color::Auto)
 | 
			
		||||
//!             }
 | 
			
		||||
//!             Some(Ok("never")) => {
 | 
			
		||||
//!                 Ok(Color::Never)
 | 
			
		||||
//!             }
 | 
			
		||||
//!             Some(invalid) => {
 | 
			
		||||
//!                 Err(format!("Invalid value for `--color`, {invalid:?}").into())
 | 
			
		||||
//!             }
 | 
			
		||||
//!         }
 | 
			
		||||
//!     }
 | 
			
		||||
//! }
 | 
			
		||||
//!
 | 
			
		||||
//! fn parse_args(
 | 
			
		||||
//!     raw: impl IntoIterator<Item=impl Into<std::ffi::OsString>>
 | 
			
		||||
//! ) -> Result<Args, BoxedError> {
 | 
			
		||||
//!     let mut args = Args {
 | 
			
		||||
//!         paths: Vec::new(),
 | 
			
		||||
//!         color: Color::Auto,
 | 
			
		||||
//!         verbosity: 0,
 | 
			
		||||
//!     };
 | 
			
		||||
//!
 | 
			
		||||
//!     let raw = clap_lex::RawArgs::new(raw);
 | 
			
		||||
//!     let mut cursor = raw.cursor();
 | 
			
		||||
//!     raw.next(&mut cursor);  // Skip the bin
 | 
			
		||||
//!     while let Some(arg) = raw.next(&mut cursor) {
 | 
			
		||||
//!         if arg.is_escape() {
 | 
			
		||||
//!             args.paths.extend(raw.remaining(&mut cursor).map(PathBuf::from));
 | 
			
		||||
//!         } else if arg.is_stdio() {
 | 
			
		||||
//!             args.paths.push(PathBuf::from("-"));
 | 
			
		||||
//!         } else if let Some((long, value)) = arg.to_long() {
 | 
			
		||||
//!             match long {
 | 
			
		||||
//!                 Ok("verbose") => {
 | 
			
		||||
//!                     if let Some(value) = value {
 | 
			
		||||
//!                         return Err(format!("`--verbose` does not take a value, got `{value:?}`").into());
 | 
			
		||||
//!                     }
 | 
			
		||||
//!                     args.verbosity += 1;
 | 
			
		||||
//!                 }
 | 
			
		||||
//!                 Ok("color") => {
 | 
			
		||||
//!                     args.color = Color::parse(value)?;
 | 
			
		||||
//!                 }
 | 
			
		||||
//!                 _ => {
 | 
			
		||||
//!                     return Err(
 | 
			
		||||
//!                         format!("Unexpected flag: --{}", arg.display()).into()
 | 
			
		||||
//!                     );
 | 
			
		||||
//!                 }
 | 
			
		||||
//!             }
 | 
			
		||||
//!         } else if let Some(mut shorts) = arg.to_short() {
 | 
			
		||||
//!             while let Some(short) = shorts.next_flag() {
 | 
			
		||||
//!                 match short {
 | 
			
		||||
//!                     Ok('v') => {
 | 
			
		||||
//!                         args.verbosity += 1;
 | 
			
		||||
//!                     }
 | 
			
		||||
//!                     Ok('c') => {
 | 
			
		||||
//!                         let value = shorts.next_value_os();
 | 
			
		||||
//!                         args.color = Color::parse(value)?;
 | 
			
		||||
//!                     }
 | 
			
		||||
//!                     Ok(c) => {
 | 
			
		||||
//!                         return Err(format!("Unexpected flag: -{c}").into());
 | 
			
		||||
//!                     }
 | 
			
		||||
//!                     Err(e) => {
 | 
			
		||||
//!                         return Err(format!("Unexpected flag: -{}", e.to_string_lossy()).into());
 | 
			
		||||
//!                     }
 | 
			
		||||
//!                 }
 | 
			
		||||
//!             }
 | 
			
		||||
//!         } else {
 | 
			
		||||
//!             args.paths.push(PathBuf::from(arg.to_value_os().to_owned()));
 | 
			
		||||
//!         }
 | 
			
		||||
//!     }
 | 
			
		||||
//!
 | 
			
		||||
//!     Ok(args)
 | 
			
		||||
//! }
 | 
			
		||||
//!
 | 
			
		||||
//! let args = parse_args(["bin", "--hello", "world"]);
 | 
			
		||||
//! println!("{args:?}");
 | 
			
		||||
//! ```
 | 
			
		||||
 | 
			
		||||
mod ext;
 | 
			
		||||
 | 
			
		||||
use std::ffi::OsStr;
 | 
			
		||||
use std::ffi::OsString;
 | 
			
		||||
 | 
			
		||||
pub use std::io::SeekFrom;
 | 
			
		||||
 | 
			
		||||
pub use ext::OsStrExt;
 | 
			
		||||
 | 
			
		||||
/// Command-line arguments
 | 
			
		||||
#[derive(Default, Clone, Debug, PartialEq, Eq)]
 | 
			
		||||
pub struct RawArgs {
 | 
			
		||||
    items: Vec<OsString>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RawArgs {
 | 
			
		||||
    //// Create an argument list to parse
 | 
			
		||||
    ///
 | 
			
		||||
    /// **NOTE:** The argument returned will be the current binary.
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust,no_run
 | 
			
		||||
    /// # use std::path::PathBuf;
 | 
			
		||||
    /// let raw = clap_lex::RawArgs::from_args();
 | 
			
		||||
    /// let mut cursor = raw.cursor();
 | 
			
		||||
    /// let _bin = raw.next_os(&mut cursor);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::<Vec<_>>();
 | 
			
		||||
    /// println!("{paths:?}");
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn from_args() -> Self {
 | 
			
		||||
        Self::new(std::env::args_os())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //// Create an argument list to parse
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust,no_run
 | 
			
		||||
    /// # use std::path::PathBuf;
 | 
			
		||||
    /// let raw = clap_lex::RawArgs::new(["bin", "foo.txt"]);
 | 
			
		||||
    /// let mut cursor = raw.cursor();
 | 
			
		||||
    /// let _bin = raw.next_os(&mut cursor);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::<Vec<_>>();
 | 
			
		||||
    /// println!("{paths:?}");
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn new(iter: impl IntoIterator<Item = impl Into<std::ffi::OsString>>) -> Self {
 | 
			
		||||
        let iter = iter.into_iter();
 | 
			
		||||
        Self::from(iter)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Create a cursor for walking the arguments
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust,no_run
 | 
			
		||||
    /// # use std::path::PathBuf;
 | 
			
		||||
    /// let raw = clap_lex::RawArgs::new(["bin", "foo.txt"]);
 | 
			
		||||
    /// let mut cursor = raw.cursor();
 | 
			
		||||
    /// let _bin = raw.next_os(&mut cursor);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::<Vec<_>>();
 | 
			
		||||
    /// println!("{paths:?}");
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn cursor(&self) -> ArgCursor {
 | 
			
		||||
        ArgCursor::new()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance the cursor, returning the next [`ParsedArg`]
 | 
			
		||||
    pub fn next(&self, cursor: &mut ArgCursor) -> Option<ParsedArg<'_>> {
 | 
			
		||||
        self.next_os(cursor).map(ParsedArg::new)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance the cursor, returning a raw argument value.
 | 
			
		||||
    pub fn next_os(&self, cursor: &mut ArgCursor) -> Option<&OsStr> {
 | 
			
		||||
        let next = self.items.get(cursor.cursor).map(|s| s.as_os_str());
 | 
			
		||||
        cursor.cursor = cursor.cursor.saturating_add(1);
 | 
			
		||||
        next
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return the next [`ParsedArg`]
 | 
			
		||||
    pub fn peek(&self, cursor: &ArgCursor) -> Option<ParsedArg<'_>> {
 | 
			
		||||
        self.peek_os(cursor).map(ParsedArg::new)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return a raw argument value.
 | 
			
		||||
    pub fn peek_os(&self, cursor: &ArgCursor) -> Option<&OsStr> {
 | 
			
		||||
        self.items.get(cursor.cursor).map(|s| s.as_os_str())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Return all remaining raw arguments, advancing the cursor to the end
 | 
			
		||||
    ///
 | 
			
		||||
    /// # Example
 | 
			
		||||
    ///
 | 
			
		||||
    /// ```rust,no_run
 | 
			
		||||
    /// # use std::path::PathBuf;
 | 
			
		||||
    /// let raw = clap_lex::RawArgs::new(["bin", "foo.txt"]);
 | 
			
		||||
    /// let mut cursor = raw.cursor();
 | 
			
		||||
    /// let _bin = raw.next_os(&mut cursor);
 | 
			
		||||
    ///
 | 
			
		||||
    /// let mut paths = raw.remaining(&mut cursor).map(PathBuf::from).collect::<Vec<_>>();
 | 
			
		||||
    /// println!("{paths:?}");
 | 
			
		||||
    /// ```
 | 
			
		||||
    pub fn remaining(&self, cursor: &mut ArgCursor) -> impl Iterator<Item = &OsStr> {
 | 
			
		||||
        let remaining = self.items[cursor.cursor..].iter().map(|s| s.as_os_str());
 | 
			
		||||
        cursor.cursor = self.items.len();
 | 
			
		||||
        remaining
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Adjust the cursor's position
 | 
			
		||||
    pub fn seek(&self, cursor: &mut ArgCursor, pos: SeekFrom) {
 | 
			
		||||
        let pos = match pos {
 | 
			
		||||
            SeekFrom::Start(pos) => pos,
 | 
			
		||||
            SeekFrom::End(pos) => (self.items.len() as i64).saturating_add(pos).max(0) as u64,
 | 
			
		||||
            SeekFrom::Current(pos) => (cursor.cursor as i64).saturating_add(pos).max(0) as u64,
 | 
			
		||||
        };
 | 
			
		||||
        let pos = (pos as usize).min(self.items.len());
 | 
			
		||||
        cursor.cursor = pos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Inject arguments before the [`RawArgs::next`]
 | 
			
		||||
    pub fn insert(
 | 
			
		||||
        &mut self,
 | 
			
		||||
        cursor: &ArgCursor,
 | 
			
		||||
        insert_items: impl IntoIterator<Item = impl Into<OsString>>,
 | 
			
		||||
    ) {
 | 
			
		||||
        self.items.splice(
 | 
			
		||||
            cursor.cursor..cursor.cursor,
 | 
			
		||||
            insert_items.into_iter().map(Into::into),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Any remaining args?
 | 
			
		||||
    pub fn is_end(&self, cursor: &ArgCursor) -> bool {
 | 
			
		||||
        self.peek_os(cursor).is_none()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<I, T> From<I> for RawArgs
 | 
			
		||||
where
 | 
			
		||||
    I: Iterator<Item = T>,
 | 
			
		||||
    T: Into<OsString>,
 | 
			
		||||
{
 | 
			
		||||
    fn from(val: I) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            items: val.map(|x| x.into()).collect(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Position within [`RawArgs`]
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 | 
			
		||||
pub struct ArgCursor {
 | 
			
		||||
    cursor: usize,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ArgCursor {
 | 
			
		||||
    fn new() -> Self {
 | 
			
		||||
        Self { cursor: 0 }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Command-line Argument
 | 
			
		||||
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
 | 
			
		||||
pub struct ParsedArg<'s> {
 | 
			
		||||
    inner: &'s OsStr,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'s> ParsedArg<'s> {
 | 
			
		||||
    fn new(inner: &'s OsStr) -> Self {
 | 
			
		||||
        Self { inner }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Argument is length of 0
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.inner.is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Does the argument look like a stdio argument (`-`)
 | 
			
		||||
    pub fn is_stdio(&self) -> bool {
 | 
			
		||||
        self.inner == "-"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Does the argument look like an argument escape (`--`)
 | 
			
		||||
    pub fn is_escape(&self) -> bool {
 | 
			
		||||
        self.inner == "--"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Does the argument look like a negative number?
 | 
			
		||||
    ///
 | 
			
		||||
    /// This won't parse the number in full but attempts to see if this looks
 | 
			
		||||
    /// like something along the lines of `-3`, `-0.3`, or `-33.03`
 | 
			
		||||
    pub fn is_negative_number(&self) -> bool {
 | 
			
		||||
        self.to_value()
 | 
			
		||||
            .ok()
 | 
			
		||||
            .and_then(|s| Some(is_number(s.strip_prefix('-')?)))
 | 
			
		||||
            .unwrap_or_default()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Treat as a long-flag
 | 
			
		||||
    pub fn to_long(&self) -> Option<(Result<&str, &OsStr>, Option<&OsStr>)> {
 | 
			
		||||
        let raw = self.inner;
 | 
			
		||||
        let remainder = raw.strip_prefix("--")?;
 | 
			
		||||
        if remainder.is_empty() {
 | 
			
		||||
            debug_assert!(self.is_escape());
 | 
			
		||||
            return None;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let (flag, value) = if let Some((p0, p1)) = remainder.split_once("=") {
 | 
			
		||||
            (p0, Some(p1))
 | 
			
		||||
        } else {
 | 
			
		||||
            (remainder, None)
 | 
			
		||||
        };
 | 
			
		||||
        let flag = flag.to_str().ok_or(flag);
 | 
			
		||||
        Some((flag, value))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Can treat as a long-flag
 | 
			
		||||
    pub fn is_long(&self) -> bool {
 | 
			
		||||
        self.inner.starts_with("--") && !self.is_escape()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Treat as a short-flag
 | 
			
		||||
    pub fn to_short(&self) -> Option<ShortFlags<'_>> {
 | 
			
		||||
        if let Some(remainder_os) = self.inner.strip_prefix("-") {
 | 
			
		||||
            if remainder_os.starts_with("-") {
 | 
			
		||||
                None
 | 
			
		||||
            } else if remainder_os.is_empty() {
 | 
			
		||||
                debug_assert!(self.is_stdio());
 | 
			
		||||
                None
 | 
			
		||||
            } else {
 | 
			
		||||
                Some(ShortFlags::new(remainder_os))
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Can treat as a short-flag
 | 
			
		||||
    pub fn is_short(&self) -> bool {
 | 
			
		||||
        self.inner.starts_with("-") && !self.is_stdio() && !self.inner.starts_with("--")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Treat as a value
 | 
			
		||||
    ///
 | 
			
		||||
    /// **NOTE:** May return a flag or an escape.
 | 
			
		||||
    pub fn to_value_os(&self) -> &OsStr {
 | 
			
		||||
        self.inner
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Treat as a value
 | 
			
		||||
    ///
 | 
			
		||||
    /// **NOTE:** May return a flag or an escape.
 | 
			
		||||
    pub fn to_value(&self) -> Result<&str, &OsStr> {
 | 
			
		||||
        self.inner.to_str().ok_or(self.inner)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Safely print an argument that may contain non-UTF8 content
 | 
			
		||||
    ///
 | 
			
		||||
    /// This may perform lossy conversion, depending on the platform. If you would like an implementation which escapes the path please use Debug instead.
 | 
			
		||||
    pub fn display(&self) -> impl std::fmt::Display + '_ {
 | 
			
		||||
        self.inner.to_string_lossy()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Walk through short flags within a [`ParsedArg`]
 | 
			
		||||
#[derive(Clone, Debug)]
 | 
			
		||||
pub struct ShortFlags<'s> {
 | 
			
		||||
    inner: &'s OsStr,
 | 
			
		||||
    utf8_prefix: std::str::CharIndices<'s>,
 | 
			
		||||
    invalid_suffix: Option<&'s OsStr>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'s> ShortFlags<'s> {
 | 
			
		||||
    fn new(inner: &'s OsStr) -> Self {
 | 
			
		||||
        let (utf8_prefix, invalid_suffix) = split_nonutf8_once(inner);
 | 
			
		||||
        let utf8_prefix = utf8_prefix.char_indices();
 | 
			
		||||
        Self {
 | 
			
		||||
            inner,
 | 
			
		||||
            utf8_prefix,
 | 
			
		||||
            invalid_suffix,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Move the iterator forward by `n` short flags
 | 
			
		||||
    pub fn advance_by(&mut self, n: usize) -> Result<(), usize> {
 | 
			
		||||
        for i in 0..n {
 | 
			
		||||
            self.next().ok_or(i)?.map_err(|_| i)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// No short flags left
 | 
			
		||||
    pub fn is_empty(&self) -> bool {
 | 
			
		||||
        self.invalid_suffix.is_none() && self.utf8_prefix.as_str().is_empty()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Does the short flag look like a number
 | 
			
		||||
    ///
 | 
			
		||||
    /// Ideally call this before doing any iterator
 | 
			
		||||
    pub fn is_negative_number(&self) -> bool {
 | 
			
		||||
        self.invalid_suffix.is_none() && is_number(self.utf8_prefix.as_str())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance the iterator, returning the next short flag on success
 | 
			
		||||
    ///
 | 
			
		||||
    /// On error, returns the invalid-UTF8 value
 | 
			
		||||
    pub fn next_flag(&mut self) -> Option<Result<char, &'s OsStr>> {
 | 
			
		||||
        if let Some((_, flag)) = self.utf8_prefix.next() {
 | 
			
		||||
            return Some(Ok(flag));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(suffix) = self.invalid_suffix {
 | 
			
		||||
            self.invalid_suffix = None;
 | 
			
		||||
            return Some(Err(suffix));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Advance the iterator, returning everything left as a value
 | 
			
		||||
    pub fn next_value_os(&mut self) -> Option<&'s OsStr> {
 | 
			
		||||
        if let Some((index, _)) = self.utf8_prefix.next() {
 | 
			
		||||
            self.utf8_prefix = "".char_indices();
 | 
			
		||||
            self.invalid_suffix = None;
 | 
			
		||||
            // SAFETY: `char_indices` ensures `index` is at a valid UTF-8 boundary
 | 
			
		||||
            let remainder = unsafe { ext::split_at(self.inner, index).1 };
 | 
			
		||||
            return Some(remainder);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(suffix) = self.invalid_suffix {
 | 
			
		||||
            self.invalid_suffix = None;
 | 
			
		||||
            return Some(suffix);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        None
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<'s> Iterator for ShortFlags<'s> {
 | 
			
		||||
    type Item = Result<char, &'s OsStr>;
 | 
			
		||||
 | 
			
		||||
    fn next(&mut self) -> Option<Self::Item> {
 | 
			
		||||
        self.next_flag()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn split_nonutf8_once(b: &OsStr) -> (&str, Option<&OsStr>) {
 | 
			
		||||
    match b.try_str() {
 | 
			
		||||
        Ok(s) => (s, None),
 | 
			
		||||
        Err(err) => {
 | 
			
		||||
            // SAFETY: `char_indices` ensures `index` is at a valid UTF-8 boundary
 | 
			
		||||
            let (valid, after_valid) = unsafe { ext::split_at(b, err.valid_up_to()) };
 | 
			
		||||
            let valid = valid.try_str().unwrap();
 | 
			
		||||
            (valid, Some(after_valid))
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn is_number(arg: &str) -> bool {
 | 
			
		||||
    // Return true if this looks like an integer or a float where it's all
 | 
			
		||||
    // digits plus an optional single dot after some digits.
 | 
			
		||||
    //
 | 
			
		||||
    // For floats allow forms such as `1.`, `1.2`, `1.2e10`, etc.
 | 
			
		||||
    let mut seen_dot = false;
 | 
			
		||||
    let mut position_of_e = None;
 | 
			
		||||
    for (i, c) in arg.as_bytes().iter().enumerate() {
 | 
			
		||||
        match c {
 | 
			
		||||
            // Digits are always valid
 | 
			
		||||
            b'0'..=b'9' => {}
 | 
			
		||||
 | 
			
		||||
            // Allow a `.`, but only one, only if it comes before an
 | 
			
		||||
            // optional exponent, and only if it's not the first character.
 | 
			
		||||
            b'.' if !seen_dot && position_of_e.is_none() && i > 0 => seen_dot = true,
 | 
			
		||||
 | 
			
		||||
            // Allow an exponent `e` but only at most one after the first
 | 
			
		||||
            // character.
 | 
			
		||||
            b'e' if position_of_e.is_none() && i > 0 => position_of_e = Some(i),
 | 
			
		||||
 | 
			
		||||
            _ => return false,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Disallow `-1e` which isn't a valid float since it doesn't actually have
 | 
			
		||||
    // an exponent.
 | 
			
		||||
    match position_of_e {
 | 
			
		||||
        Some(i) => i != arg.len() - 1,
 | 
			
		||||
        None => true,
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user