bashkit 0.8.0

Awesomely fast virtual sandbox with bash and file system
Documentation
// GENERATED by bashkit-coreutils-port. DO NOT EDIT.
//
// Source: uutils/coreutils@39364b6 src/uucore/src/lib/features/format/argument.rs
// Regenerate: cargo run -p bashkit-coreutils-port -- port-module <UUTILS_DIR> format <REV>
//
// Original uutils licensed MIT; see THIRD_PARTY_LICENSES.

use super::ExtendedBigDecimal;
use crate::builtins::generated::format::spec::ArgumentLocation;
use crate::builtins::generated::format_support::QuotingStyle;
use crate::builtins::generated::format_support::locale_aware_escape_name;
use crate::builtins::generated::format_support::os_str_as_bytes;
use crate::builtins::generated::format_support::set_exit_code;
use crate::builtins::generated::format_support::show_error;
use crate::builtins::generated::format_support::show_warning;
use crate::builtins::generated::num_parser::ExtendedParser;
use crate::builtins::generated::num_parser::ExtendedParserError;
use os_display::Quotable;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::num::NonZero;
#[derive(Clone, Debug, PartialEq)]
pub enum FormatArgument {
    Char(char),
    String(OsString),
    UnsignedInt(u64),
    SignedInt(i64),
    Float(ExtendedBigDecimal),
    /// Special argument that gets coerced into the other variants
    Unparsed(OsString),
}
#[derive(Debug, PartialEq)]
pub struct FormatArguments<'a> {
    args: &'a [FormatArgument],
    next_arg_position: usize,
    highest_arg_position: Option<usize>,
    current_offset: usize,
}
impl<'a> FormatArguments<'a> {
    /// Create a new FormatArguments from a slice of FormatArgument
    pub fn new(args: &'a [FormatArgument]) -> Self {
        Self {
            args,
            next_arg_position: 0,
            highest_arg_position: None,
            current_offset: 0,
        }
    }
    /// Get the next argument that would be used
    pub fn peek_arg(&self) -> Option<&'a FormatArgument> {
        self.args.get(self.next_arg_position)
    }
    /// Check if all arguments have been consumed
    pub fn is_exhausted(&self) -> bool {
        self.current_offset >= self.args.len()
    }
    pub fn start_next_batch(&mut self) {
        self.current_offset = self
            .next_arg_position
            .max(self.highest_arg_position.map_or(0, |x| x.saturating_add(1)));
        self.next_arg_position = self.current_offset;
    }
    pub fn next_char(&mut self, position: ArgumentLocation) -> u8 {
        match self.next_arg(position) {
            Some(FormatArgument::Char(c)) => *c as u8,
            Some(FormatArgument::Unparsed(os)) => match os_str_as_bytes(os) {
                Ok(bytes) => bytes.first().copied().unwrap_or(b'\0'),
                Err(_) => b'\0',
            },
            _ => b'\0',
        }
    }
    pub fn next_string(&mut self, position: ArgumentLocation) -> &'a OsStr {
        match self.next_arg(position) {
            Some(FormatArgument::Unparsed(os) | FormatArgument::String(os)) => os,
            _ => "".as_ref(),
        }
    }
    pub fn next_i64(&mut self, position: ArgumentLocation) -> i64 {
        match self.next_arg(position) {
            Some(FormatArgument::SignedInt(n)) => *n,
            Some(FormatArgument::Unparsed(os)) => Self::get_num::<i64>(os),
            _ => 0,
        }
    }
    pub fn next_u64(&mut self, position: ArgumentLocation) -> u64 {
        match self.next_arg(position) {
            Some(FormatArgument::UnsignedInt(n)) => *n,
            Some(FormatArgument::Unparsed(os)) => Self::get_num::<u64>(os),
            _ => 0,
        }
    }
    pub fn next_extended_big_decimal(&mut self, position: ArgumentLocation) -> ExtendedBigDecimal {
        match self.next_arg(position) {
            Some(FormatArgument::Float(n)) => n.clone(),
            Some(FormatArgument::Unparsed(os)) => Self::get_num::<ExtendedBigDecimal>(os),
            _ => ExtendedBigDecimal::zero(),
        }
    }
    fn parse_quote_start<T>(os: &OsStr) -> Result<T, ExtendedParserError<T>>
    where
        T: ExtendedParser + From<u8> + From<u32> + Default,
    {
        let Ok(s) = os_str_as_bytes(os) else {
            return Err(ExtendedParserError::NotNumeric);
        };
        let (Some((b'"', bytes)) | Some((b'\'', bytes))) = s.split_first() else {
            debug_assert!(false);
            return Err(ExtendedParserError::NotNumeric);
        };
        if bytes.is_empty() {
            return Err(ExtendedParserError::NotNumeric);
        }
        let (val, len) = if let Some(c) = bytes
            .utf8_chunks()
            .next()
            .expect("bytes should not be empty")
            .valid()
            .chars()
            .next()
        {
            ((c as u32).into(), c.len_utf8())
        } else {
            (bytes[0].into(), 1)
        };
        if bytes.len() > len {
            return Err(ExtendedParserError::PartialMatch(
                val,
                String::from_utf8_lossy(&bytes[len..]).into_owned(),
            ));
        }
        Ok(val)
    }
    fn get_num<T>(os: &OsStr) -> T
    where
        T: ExtendedParser + From<u8> + From<u32> + Default,
    {
        let s = os.to_string_lossy();
        let first = s.as_bytes().first().copied();
        let quote_start = first == Some(b'"') || first == Some(b'\'');
        let parsed = if quote_start {
            Self::parse_quote_start(os)
        } else {
            T::extended_parse(&s)
        };
        extract_value(parsed, &s, quote_start)
    }
    fn get_at_relative_position(&mut self, pos: NonZero<usize>) -> Option<&'a FormatArgument> {
        let pos: usize = pos.into();
        let pos = (pos - 1).saturating_add(self.current_offset);
        self.highest_arg_position = Some(self.highest_arg_position.map_or(pos, |x| x.max(pos)));
        self.args.get(pos)
    }
    fn next_arg(&mut self, position: ArgumentLocation) -> Option<&'a FormatArgument> {
        match position {
            ArgumentLocation::NextArgument => {
                let arg = self.args.get(self.next_arg_position);
                self.next_arg_position += 1;
                arg
            }
            ArgumentLocation::Position(pos) => self.get_at_relative_position(pos),
        }
    }
}
fn extract_value<T: Default>(
    p: Result<T, ExtendedParserError<T>>,
    input: &str,
    quote_start: bool,
) -> T {
    match p {
        Ok(v) => v,
        Err(e) => {
            set_exit_code(1);
            let input = locale_aware_escape_name(OsStr::new(input), QuotingStyle::C_NO_QUOTES);
            match e {
                ExtendedParserError::Overflow(v) => {
                    show_error!("{}: Numerical result out of range", input.quote());
                    v
                }
                ExtendedParserError::Underflow(v) => {
                    show_error!("{}: Numerical result out of range", input.quote());
                    v
                }
                ExtendedParserError::NotNumeric => {
                    show_error!("{}: expected a numeric value", input.quote());
                    Default::default()
                }
                ExtendedParserError::PartialMatch(v, rest) => {
                    if quote_start {
                        set_exit_code(0);
                        show_warning!(
                            "{rest}: character(s) following character constant have been ignored"
                        );
                    } else {
                        show_error!("{}: value not completely converted", input.quote());
                    }
                    v
                }
            }
        }
    }
}