Skip to main content

qemu_command_builder/args/
runwith.rs

1use crate::common::OnOff;
2use crate::parsers::{ARG_RUN_WITH, DELIM_COLON, DELIM_COMMA};
3use crate::shell_path::{ShellPath, shell_path_until_comma};
4use crate::shell_string::{ShellString, ShellStringError, shell_string_until_comma};
5use crate::to_command::ToCommand;
6use crate::{pco0, qao};
7use bon::Builder;
8use proptest_derive::Arbitrary;
9use std::str::FromStr;
10use winnow::Result;
11use winnow::ascii::{alphanumeric1, dec_uint};
12use winnow::combinator::{alt, opt};
13use winnow::prelude::*;
14use winnow::token::literal;
15
16const KEY_ASYNC_TEARDOWN: &str = "async-teardown=";
17const KEY_CHROOT: &str = "chroot=";
18const KEY_EXIT_WITH_PARENT: &str = "exit-with-parent=";
19const KEY_USER: &str = "user=";
20
21#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
22pub enum UserOrIds {
23    User(ShellString),
24    Id { uid: usize, gid: usize },
25}
26#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
27pub struct RunWith {
28    async_teardown: Option<OnOff>,
29    chroot: Option<ShellPath>,
30    exit_with_parent: Option<OnOff>,
31    user: Option<UserOrIds>,
32}
33
34impl ToCommand for RunWith {
35    fn has_args(&self) -> bool {
36        self.async_teardown.is_some() || self.chroot.is_some() || self.exit_with_parent.is_some() || self.user.is_some()
37    }
38    fn command(&self) -> String {
39        ARG_RUN_WITH.to_string()
40    }
41    fn to_args(&self) -> Vec<String> {
42        let mut args = vec![];
43
44        qao!(&self.async_teardown, args, KEY_ASYNC_TEARDOWN);
45        if let Some(chroot) = &self.chroot {
46            args.push(format!("{}{}", KEY_CHROOT, chroot.as_ref()));
47        }
48        qao!(&self.exit_with_parent, args, KEY_EXIT_WITH_PARENT);
49
50        if let Some(user) = &self.user {
51            match user {
52                UserOrIds::User(id) => {
53                    args.push(format!("{}{}", KEY_USER, id));
54                }
55                UserOrIds::Id { uid, gid } => {
56                    args.push(format!("{}{}:{}", KEY_USER, uid, gid));
57                }
58            }
59        }
60        vec![args.join(DELIM_COMMA)]
61    }
62}
63
64impl FromStr for RunWith {
65    type Err = ShellStringError;
66
67    fn from_str(s: &str) -> Result<Self, Self::Err> {
68        run_with.parse(s).map_err(|e| ShellStringError::from_parse(e))
69    }
70}
71
72pco0!(async_teardown, alphanumeric1, OnOff, KEY_ASYNC_TEARDOWN);
73pco0!(chroot, shell_path_until_comma, ShellPath, KEY_CHROOT);
74pco0!(exit_with_parent, alphanumeric1, OnOff, KEY_EXIT_WITH_PARENT);
75
76fn user(s: &mut &str) -> ModalResult<UserOrIds> {
77    let _ = opt(literal(DELIM_COMMA)).parse_next(s)?;
78    let _ = opt(literal(KEY_USER)).parse_next(s)?;
79    alt((user_id, user_name)).parse_next(s)
80}
81
82fn user_name(s: &mut &str) -> ModalResult<UserOrIds> {
83    let name = shell_string_until_comma.parse_to::<ShellString>().parse_next(s)?;
84    Ok(UserOrIds::User(name))
85}
86
87fn user_id(s: &mut &str) -> ModalResult<UserOrIds> {
88    let uid = dec_uint.parse_next(s)?;
89    let _ = literal(DELIM_COLON).parse_next(s)?;
90    let gid = dec_uint.parse_next(s)?;
91    Ok(UserOrIds::Id { uid, gid })
92}
93
94pub fn run_with(s: &mut &str) -> ModalResult<RunWith> {
95    let async_teardown = opt(async_teardown).parse_next(s)?;
96    let chroot = opt(chroot).parse_next(s)?;
97    let exit_with_parent = opt(exit_with_parent).parse_next(s)?;
98    let user = opt(user).parse_next(s)?;
99    Ok(RunWith {
100        async_teardown,
101        chroot,
102        exit_with_parent,
103        user,
104    })
105}