agcodex_execpolicy/
arg_type.rs

1#![allow(clippy::needless_lifetimes)]
2
3use crate::error::Error;
4use crate::error::Result;
5use crate::sed_command::parse_sed_command;
6use allocative::Allocative;
7use derive_more::derive::Display;
8use serde::Serialize;
9use starlark::any::ProvidesStaticType;
10use starlark::values::StarlarkValue;
11use starlark::values::starlark_value;
12
13#[derive(Debug, Clone, Display, Eq, PartialEq, ProvidesStaticType, Allocative, Serialize)]
14#[display("{}", self)]
15pub enum ArgType {
16    Literal(String),
17    /// We cannot say what this argument represents, but it is *not* a file path.
18    OpaqueNonFile,
19    /// A file (or directory) that can be expected to be read as part of this command.
20    ReadableFile,
21    /// A file (or directory) that can be expected to be written as part of this command.
22    WriteableFile,
23    /// Positive integer, like one that is required for `head -n`.
24    PositiveInteger,
25    /// Bespoke arg type for a safe sed command.
26    SedCommand,
27    /// Type is unknown: it may or may not be a file.
28    Unknown,
29}
30
31impl ArgType {
32    pub fn validate(&self, value: &str) -> Result<()> {
33        match self {
34            ArgType::Literal(literal_value) => {
35                if value != *literal_value {
36                    Err(Error::LiteralValueDidNotMatch {
37                        expected: literal_value.clone(),
38                        actual: value.to_string(),
39                    })
40                } else {
41                    Ok(())
42                }
43            }
44            ArgType::ReadableFile => {
45                if value.is_empty() {
46                    Err(Error::EmptyFileName {})
47                } else {
48                    Ok(())
49                }
50            }
51            ArgType::WriteableFile => {
52                if value.is_empty() {
53                    Err(Error::EmptyFileName {})
54                } else {
55                    Ok(())
56                }
57            }
58            ArgType::OpaqueNonFile | ArgType::Unknown => Ok(()),
59            ArgType::PositiveInteger => match value.parse::<u64>() {
60                Ok(0) => Err(Error::InvalidPositiveInteger {
61                    value: value.to_string(),
62                }),
63                Ok(_) => Ok(()),
64                Err(_) => Err(Error::InvalidPositiveInteger {
65                    value: value.to_string(),
66                }),
67            },
68            ArgType::SedCommand => parse_sed_command(value),
69        }
70    }
71
72    pub const fn might_write_file(&self) -> bool {
73        match self {
74            ArgType::WriteableFile | ArgType::Unknown => true,
75            ArgType::Literal(_)
76            | ArgType::OpaqueNonFile
77            | ArgType::PositiveInteger
78            | ArgType::ReadableFile
79            | ArgType::SedCommand => false,
80        }
81    }
82}
83
84#[starlark_value(type = "ArgType")]
85impl<'v> StarlarkValue<'v> for ArgType {
86    type Canonical = ArgType;
87}