Skip to main content

qemu_command_builder/
shell_path.rs

1use proptest_derive::Arbitrary;
2use std::fmt::{Display, Formatter};
3use std::ops::Deref;
4use std::path::PathBuf;
5use std::str::FromStr;
6use winnow::prelude::*;
7use winnow::token::take_while;
8
9#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
10/// A shell-facing path or token used when rendering QEMU command arguments.
11///
12/// This type stores the raw path text. Shell escaping is applied later when a
13/// full command line is flattened into a single string.
14///
15/// Use this type for path-like QEMU option values that should remain unquoted
16/// in `argv` form but may need escaping when rendered as a single shell
17/// command.
18pub struct ShellPath {
19    /// The underlying raw path or shell token.
20    ///
21    /// This value is stored exactly as provided.
22    #[proptest(regex = r#"[^,\n:]{1,100}"#)]
23    pub s: String,
24}
25
26impl ShellPath {
27    /// Creates a new [`ShellPath`] from raw path text.
28    pub fn new(s: impl Into<String>) -> Self {
29        Self { s: s.into() }
30    }
31}
32
33impl Display for ShellPath {
34    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
35        write!(f, "{}", self.s)
36    }
37}
38
39impl FromStr for ShellPath {
40    type Err = String;
41    fn from_str(s: &str) -> Result<Self, Self::Err> {
42        Ok(ShellPath { s: s.to_string() })
43    }
44}
45
46impl<'a> From<&'a str> for ShellPath {
47    fn from(s: &'a str) -> Self {
48        ShellPath { s: s.to_string() }
49    }
50}
51
52impl From<PathBuf> for ShellPath {
53    fn from(value: PathBuf) -> Self {
54        let s = value.into_os_string().into_string().expect("ShellPath only supports UTF-8 paths");
55        Self { s }
56    }
57}
58
59impl Deref for ShellPath {
60    type Target = str;
61
62    fn deref(&self) -> &Self::Target {
63        &self.s
64    }
65}
66
67impl AsRef<str> for ShellPath {
68    fn as_ref(&self) -> &str {
69        &self.s
70    }
71}
72
73pub(crate) fn shell_path_until_comma<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
74    take_while(1.., |c: char| c != ',').parse_next(input)
75}