Skip to main content

safe_chains/registry/
types.rs

1use serde::Deserialize;
2
3use crate::policy::FlagStyle;
4use crate::verdict::SafetyLevel;
5
6#[derive(Debug, Deserialize)]
7pub(super) struct TomlFile {
8    pub command: Vec<TomlCommand>,
9}
10
11#[derive(Debug, Deserialize)]
12pub(super) struct TomlCommand {
13    pub name: String,
14    #[serde(default)]
15    pub description: Option<String>,
16    #[serde(default)]
17    pub candidate: Option<bool>,
18    #[serde(default)]
19    pub aliases: Vec<String>,
20    #[serde(default)]
21    pub url: String,
22    #[serde(default)]
23    pub level: Option<TomlLevel>,
24    #[serde(default)]
25    pub bare: Option<bool>,
26    #[serde(default)]
27    pub max_positional: Option<usize>,
28    #[serde(default)]
29    pub positional_style: Option<bool>,
30    #[serde(default)]
31    pub numeric_dash: Option<bool>,
32    #[serde(default)]
33    pub standalone: Vec<String>,
34    #[serde(default)]
35    pub valued: Vec<String>,
36    #[serde(default)]
37    pub bare_flags: Vec<String>,
38    #[serde(default)]
39    pub sub: Vec<TomlSub>,
40    #[serde(default)]
41    pub handler: Option<String>,
42    #[serde(default)]
43    pub doc_body: Option<String>,
44    #[serde(default)]
45    pub require_any: Vec<String>,
46    #[serde(default)]
47    pub first_arg: Vec<String>,
48    #[serde(default)]
49    pub wrapper: Option<TomlWrapper>,
50    #[serde(default)]
51    pub write_flags: Vec<String>,
52    #[serde(default)]
53    pub researched_version: Option<String>,
54}
55
56#[derive(Debug, Deserialize)]
57pub(super) struct TomlWrapper {
58    #[serde(default)]
59    pub standalone: Vec<String>,
60    #[serde(default)]
61    pub valued: Vec<String>,
62    #[serde(default)]
63    pub positional_skip: Option<usize>,
64    #[serde(default)]
65    pub separator: Option<String>,
66    #[serde(default)]
67    pub bare_ok: Option<bool>,
68}
69
70#[derive(Debug, Deserialize)]
71pub(super) struct TomlSub {
72    pub name: String,
73    #[serde(default)]
74    pub candidate: Option<bool>,
75    #[serde(default)]
76    pub level: Option<TomlLevel>,
77    #[serde(default)]
78    pub bare: Option<bool>,
79    #[serde(default)]
80    pub max_positional: Option<usize>,
81    #[serde(default)]
82    pub positional_style: Option<bool>,
83    #[serde(default)]
84    pub numeric_dash: Option<bool>,
85    #[serde(default)]
86    pub standalone: Vec<String>,
87    #[serde(default)]
88    pub valued: Vec<String>,
89    #[serde(default)]
90    pub guard: Option<String>,
91    #[serde(default)]
92    pub guard_short: Option<String>,
93    #[serde(default)]
94    pub allow_all: Option<bool>,
95    #[serde(default)]
96    pub sub: Vec<TomlSub>,
97    #[serde(default)]
98    pub nested_bare: Option<bool>,
99    #[serde(default)]
100    pub require_any: Vec<String>,
101    #[serde(default)]
102    pub first_arg: Vec<String>,
103    #[serde(default)]
104    pub write_flags: Vec<String>,
105    #[serde(default)]
106    pub delegate_after: Option<String>,
107    #[serde(default)]
108    pub delegate_skip: Option<usize>,
109    #[serde(default)]
110    pub handler: Option<String>,
111    #[serde(default)]
112    pub doc_body: Option<String>,
113}
114
115#[derive(Debug, Clone, Copy, Deserialize)]
116pub(super) enum TomlLevel {
117    Inert,
118    SafeRead,
119    SafeWrite,
120}
121
122impl From<TomlLevel> for SafetyLevel {
123    fn from(l: TomlLevel) -> Self {
124        match l {
125            TomlLevel::Inert => SafetyLevel::Inert,
126            TomlLevel::SafeRead => SafetyLevel::SafeRead,
127            TomlLevel::SafeWrite => SafetyLevel::SafeWrite,
128        }
129    }
130}
131
132#[derive(Debug)]
133pub struct CommandSpec {
134    pub name: String,
135    pub description: String,
136    pub aliases: Vec<String>,
137    pub url: String,
138    pub category: String,
139    /// Upstream version of the underlying tool that was researched
140    /// when this spec was last updated. Free-form string — e.g.
141    /// `"1.9.0"`, `"v5.10.3"`, `"2026-05-08 master"`,
142    /// `"@northflank/cli 0.10.15"`. Internal-only: not rendered in
143    /// docs or used at runtime. Surfaces in tests and as a tripwire
144    /// when researching newer versions of the same tool.
145    pub researched_version: Option<String>,
146    pub(super) kind: DispatchKind,
147}
148
149#[derive(Debug, Clone)]
150pub(super) struct SubSpec {
151    pub name: String,
152    pub kind: DispatchKind,
153}
154
155#[derive(Debug, Clone)]
156pub(super) enum DispatchKind {
157    Policy {
158        policy: OwnedPolicy,
159        level: SafetyLevel,
160    },
161    FirstArg {
162        patterns: Vec<String>,
163        level: SafetyLevel,
164    },
165    RequireAny {
166        require_any: Vec<String>,
167        policy: OwnedPolicy,
168        level: SafetyLevel,
169        accept_bare_help: bool,
170    },
171    Branching {
172        subs: Vec<SubSpec>,
173        bare_flags: Vec<String>,
174        bare_ok: bool,
175        pre_standalone: Vec<String>,
176        pre_valued: Vec<String>,
177        first_arg: Vec<String>,
178        first_arg_level: SafetyLevel,
179    },
180    WriteFlagged {
181        policy: OwnedPolicy,
182        base_level: SafetyLevel,
183        write_flags: Vec<String>,
184    },
185    DelegateAfterSeparator {
186        separator: String,
187    },
188    DelegateSkip {
189        skip: usize,
190    },
191    Wrapper {
192        standalone: Vec<String>,
193        valued: Vec<String>,
194        positional_skip: usize,
195        separator: Option<String>,
196        bare_ok: bool,
197    },
198    Custom {
199        #[allow(dead_code)]
200        handler_name: String,
201        doc_body: Option<String>,
202    },
203}
204
205#[derive(Debug, Clone)]
206pub struct OwnedPolicy {
207    pub standalone: Vec<String>,
208    pub valued: Vec<String>,
209    pub bare: bool,
210    pub max_positional: Option<usize>,
211    pub flag_style: FlagStyle,
212    pub numeric_dash: bool,
213}