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