agcodex_execpolicy/
arg_matcher.rs

1#![allow(clippy::needless_lifetimes)]
2
3use crate::arg_type::ArgType;
4use crate::starlark::values::ValueLike;
5use allocative::Allocative;
6use derive_more::derive::Display;
7use starlark::any::ProvidesStaticType;
8use starlark::values::AllocValue;
9use starlark::values::Heap;
10use starlark::values::NoSerialize;
11use starlark::values::StarlarkValue;
12use starlark::values::UnpackValue;
13use starlark::values::Value;
14use starlark::values::starlark_value;
15use starlark::values::string::StarlarkStr;
16
17/// Patterns that lists of arguments should be compared against.
18#[derive(Clone, Debug, Display, Eq, PartialEq, NoSerialize, ProvidesStaticType, Allocative)]
19#[display("{}", self)]
20pub enum ArgMatcher {
21    /// Literal string value.
22    Literal(String),
23
24    /// We cannot say what type of value this should match, but it is *not* a file path.
25    OpaqueNonFile,
26
27    /// Required readable file.
28    ReadableFile,
29
30    /// Required writeable file.
31    WriteableFile,
32
33    /// Non-empty list of readable files.
34    ReadableFiles,
35
36    /// Non-empty list of readable files, or empty list, implying readable cwd.
37    ReadableFilesOrCwd,
38
39    /// Positive integer, like one that is required for `head -n`.
40    PositiveInteger,
41
42    /// Bespoke matcher for safe sed commands.
43    SedCommand,
44
45    /// Matches an arbitrary number of arguments without attributing any
46    /// particular meaning to them. Caller is responsible for interpreting them.
47    UnverifiedVarargs,
48}
49
50impl ArgMatcher {
51    pub const fn cardinality(&self) -> ArgMatcherCardinality {
52        match self {
53            ArgMatcher::Literal(_)
54            | ArgMatcher::OpaqueNonFile
55            | ArgMatcher::ReadableFile
56            | ArgMatcher::WriteableFile
57            | ArgMatcher::PositiveInteger
58            | ArgMatcher::SedCommand => ArgMatcherCardinality::One,
59            ArgMatcher::ReadableFiles => ArgMatcherCardinality::AtLeastOne,
60            ArgMatcher::ReadableFilesOrCwd | ArgMatcher::UnverifiedVarargs => {
61                ArgMatcherCardinality::ZeroOrMore
62            }
63        }
64    }
65
66    pub fn arg_type(&self) -> ArgType {
67        match self {
68            ArgMatcher::Literal(value) => ArgType::Literal(value.clone()),
69            ArgMatcher::OpaqueNonFile => ArgType::OpaqueNonFile,
70            ArgMatcher::ReadableFile => ArgType::ReadableFile,
71            ArgMatcher::WriteableFile => ArgType::WriteableFile,
72            ArgMatcher::ReadableFiles => ArgType::ReadableFile,
73            ArgMatcher::ReadableFilesOrCwd => ArgType::ReadableFile,
74            ArgMatcher::PositiveInteger => ArgType::PositiveInteger,
75            ArgMatcher::SedCommand => ArgType::SedCommand,
76            ArgMatcher::UnverifiedVarargs => ArgType::Unknown,
77        }
78    }
79}
80
81pub enum ArgMatcherCardinality {
82    One,
83    AtLeastOne,
84    ZeroOrMore,
85}
86
87impl ArgMatcherCardinality {
88    pub const fn is_exact(&self) -> Option<usize> {
89        match self {
90            ArgMatcherCardinality::One => Some(1),
91            ArgMatcherCardinality::AtLeastOne => None,
92            ArgMatcherCardinality::ZeroOrMore => None,
93        }
94    }
95}
96
97impl<'v> AllocValue<'v> for ArgMatcher {
98    fn alloc_value(self, heap: &'v Heap) -> Value<'v> {
99        heap.alloc_simple(self)
100    }
101}
102
103#[starlark_value(type = "ArgMatcher")]
104impl<'v> StarlarkValue<'v> for ArgMatcher {
105    type Canonical = ArgMatcher;
106}
107
108impl<'v> UnpackValue<'v> for ArgMatcher {
109    type Error = starlark::Error;
110
111    fn unpack_value_impl(value: Value<'v>) -> starlark::Result<Option<Self>> {
112        if let Some(str) = value.downcast_ref::<StarlarkStr>() {
113            Ok(Some(ArgMatcher::Literal(str.as_str().to_string())))
114        } else {
115            Ok(value.downcast_ref::<ArgMatcher>().cloned())
116        }
117    }
118}