qemu_command_builder/
runwith.rs1use 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_USER: &str = "user=";
19
20#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Arbitrary)]
21pub enum UserOrIds {
22 User(ShellString),
23 Id { uid: usize, gid: usize },
24}
25#[derive(Debug, Clone, Hash, Ord, PartialOrd, Eq, PartialEq, Default, Builder, Arbitrary)]
26pub struct RunWith {
27 async_teardown: Option<OnOff>,
28 chroot: Option<ShellPath>,
29 user: Option<UserOrIds>,
30}
31
32impl ToCommand for RunWith {
33 fn has_args(&self) -> bool {
34 self.async_teardown.is_some() || self.chroot.is_some() || self.user.is_some()
35 }
36 fn command(&self) -> String {
37 ARG_RUN_WITH.to_string()
38 }
39 fn to_args(&self) -> Vec<String> {
40 let mut args = vec![];
41
42 qao!(&self.async_teardown, args, KEY_ASYNC_TEARDOWN);
43 if let Some(chroot) = &self.chroot {
44 args.push(format!("{}{}", KEY_CHROOT, chroot.as_ref()));
45 }
46
47 if let Some(user) = &self.user {
48 match user {
49 UserOrIds::User(id) => {
50 args.push(format!("{}{}", KEY_USER, id));
51 }
52 UserOrIds::Id { uid, gid } => {
53 args.push(format!("{}{}:{}", KEY_USER, uid, gid));
54 }
55 }
56 }
57 vec![args.join(DELIM_COMMA)]
58 }
59}
60
61impl FromStr for RunWith {
62 type Err = ShellStringError;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 run_with.parse(s).map_err(|e| ShellStringError::from_parse(e))
66 }
67}
68
69pco0!(async_teardown, alphanumeric1, OnOff, KEY_ASYNC_TEARDOWN);
70pco0!(chroot, shell_path_until_comma, ShellPath, KEY_CHROOT);
71
72fn user(s: &mut &str) -> ModalResult<UserOrIds> {
73 let _ = opt(literal(DELIM_COMMA)).parse_next(s)?;
74 let _ = opt(literal(KEY_USER)).parse_next(s)?;
75 alt((user_id, user_name)).parse_next(s)
76}
77
78fn user_name(s: &mut &str) -> ModalResult<UserOrIds> {
79 let name = shell_string_until_comma.parse_to::<ShellString>().parse_next(s)?;
80 Ok(UserOrIds::User(name))
81}
82
83fn user_id(s: &mut &str) -> ModalResult<UserOrIds> {
84 let uid = dec_uint.parse_next(s)?;
85 let _ = literal(DELIM_COLON).parse_next(s)?;
86 let gid = dec_uint.parse_next(s)?;
87 Ok(UserOrIds::Id { uid, gid })
88}
89
90pub fn run_with(s: &mut &str) -> ModalResult<RunWith> {
91 let async_teardown = opt(async_teardown).parse_next(s)?;
92 let chroot = opt(chroot).parse_next(s)?;
93 let user = opt(user).parse_next(s)?;
94 Ok(RunWith { async_teardown, chroot, user })
95}