git_policy_cli/
config_loading.rs1use super::global_arguments::{GlobalOutcome, global_layout};
15use std::ffi::OsString;
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
19pub enum ConfigLoading {
20 Skip,
22 Required,
24}
25
26const INSPECTION_COMMANDS: &[&[u8]] = &[
28 b"annotate",
29 b"blame",
30 b"cat-file",
31 b"count-objects",
32 b"describe",
33 b"diff",
34 b"diff-files",
35 b"diff-index",
36 b"diff-tree",
37 b"for-each-ref",
38 b"grep",
39 b"help",
40 b"log",
41 b"ls-files",
42 b"ls-remote",
43 b"ls-tree",
44 b"merge-base",
45 b"name-rev",
46 b"rev-list",
47 b"rev-parse",
48 b"shortlog",
49 b"show",
50 b"show-branch",
51 b"show-ref",
52 b"status",
53 b"version",
54 b"whatchanged",
55];
56
57fn mutating_long(name: &[u8], branch: bool) -> bool {
59 if branch {
60 return [
61 b"--copy".as_slice(),
62 b"--delete",
63 b"--delete-merged",
64 b"--edit-description",
65 b"--force",
66 b"--move",
67 b"--set-upstream",
68 b"--set-upstream-to",
69 b"--unset-upstream",
70 b"--create-reflog",
71 ]
72 .contains(&name);
73 }
74 return [
75 b"--annotate".as_slice(),
76 b"--delete",
77 b"--edit",
78 b"--force",
79 b"--sign",
80 b"--local-user",
81 b"--message",
82 b"--file",
83 b"--trailer",
84 b"--create-reflog",
85 ]
86 .contains(&name);
87}
88
89fn mixed_command(arguments: &[OsString], branch: bool) -> ConfigLoading {
91 let mut listing: bool = false;
92 let mut positional: bool = false;
93 let mut after_separator: bool = false;
94 let mut is_value: bool = false;
96 for (index, argument) in arguments.iter().enumerate() {
98 if is_value {
99 is_value = false;
100 continue;
101 }
102 let token: &[u8] = argument.as_encoded_bytes();
103 if after_separator {
104 positional = true;
105 continue;
106 }
107 if token == b"--" {
108 after_separator = true;
109 continue;
110 }
111 if token.starts_with(b"--") {
112 let assignment: Option<usize> = token.iter().position(is_equals);
114 let name: &[u8] = if let Some(at) = assignment {
115 &token[..at]
116 } else {
117 token
118 };
119 if mutating_long(name, branch) {
120 return ConfigLoading::Required;
121 }
122 if name == b"--list"
123 || (branch && name == b"--show-current")
124 || (!branch && name == b"--verify")
125 {
126 listing = true;
127 continue;
128 }
129 if [b"--color".as_slice(), b"--column", b"--abbrev"].contains(&name) {
131 continue;
132 }
133 if [
134 b"--contains".as_slice(),
135 b"--no-contains",
136 b"--with",
137 b"--without",
138 b"--merged",
139 b"--no-merged",
140 b"--points-at",
141 b"--format",
142 b"--sort",
143 ]
144 .contains(&name)
145 {
146 if assignment.is_none() {
147 if index + 1 == arguments.len() {
148 return if name == b"--format" || name == b"--sort" {
150 ConfigLoading::Required
151 } else {
152 ConfigLoading::Skip
153 };
154 }
155 is_value = true;
156 }
157 if ![b"--format".as_slice(), b"--sort"].contains(&name) {
158 listing = true;
159 }
160 continue;
161 }
162 if [
163 b"--verbose".as_slice(),
164 b"--quiet",
165 b"--remotes",
166 b"--all",
167 b"--ignore-case",
168 b"--omit-empty",
169 b"--no-color",
170 b"--no-column",
171 ]
172 .contains(&name)
173 {
174 continue;
175 }
176 return ConfigLoading::Required;
178 }
179 if token.starts_with(b"-") && token.len() > 1 {
180 let mut numeric_lines: bool = false;
181 for letter in &token[1..] {
182 if numeric_lines {
183 if letter.is_ascii_digit() {
184 continue;
185 }
186 return ConfigLoading::Required;
187 }
188 let mutations: &[u8] = if branch { b"cCdDfmMut" } else { b"adefsumF" };
189 if mutations.contains(letter) {
190 return ConfigLoading::Required;
191 }
192 if *letter == b'l' || (!branch && *letter == b'v') {
193 listing = true;
194 } else if !branch && *letter == b'n' {
195 listing = true;
196 numeric_lines = true;
197 } else if (branch && b"vqrai".contains(letter)) || (!branch && *letter == b'i') {
198 continue;
199 } else {
200 return ConfigLoading::Required;
201 }
202 }
203 continue;
204 }
205 positional = true;
206 }
207 if !positional || listing {
208 return ConfigLoading::Skip;
209 }
210 return ConfigLoading::Required;
211}
212
213fn is_equals(byte: &u8) -> bool {
215 return *byte == b'=';
216}
217
218pub fn classify_config_loading(arguments: &[OsString]) -> ConfigLoading {
222 let layout = global_layout(arguments);
223 if layout.outcome == GlobalOutcome::Query
224 || layout.outcome == GlobalOutcome::InvalidOption
225 || layout.outcome == GlobalOutcome::MissingValue
226 {
227 return ConfigLoading::Skip;
228 }
229 if layout.outcome == GlobalOutcome::NoCommand {
230 return ConfigLoading::Required;
231 }
232 let command: &[u8] = arguments[layout.prefix_len].as_encoded_bytes();
233 if INSPECTION_COMMANDS.contains(&command) {
234 return ConfigLoading::Skip;
235 }
236 if command == b"branch" || command == b"tag" {
237 return mixed_command(&arguments[layout.prefix_len + 1..], command == b"branch");
238 }
239 return ConfigLoading::Required;
240}
241
242#[cfg(test)]
244#[path = "config_loading_tests.rs"]
245mod tests;