Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_syntax/src/arg.rs

5.8 KiB, 50 runs

created by r1870400018:1043, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1use crate::{
2 cmd::{
3 Cmd,
4 CmdConfig,
5 },
6 core::SyntaxPrefs,
7 key::Key,
8 val::Val,
9};
10
11use oxedyne_fe2o3_core::{
12 prelude::*,
13 map::Recursive,
14};
15use std::{
16 collections::BTreeMap,
17 fmt,
18};
19
20
21#[derive(Clone, Debug, Default)]
22pub struct Arg {
23 pub cfg: ArgConfig,
24 pub id: u16, // Binary
25}
26
27#[derive(Clone, Debug, Default)]
28pub struct ArgConfig {
29 pub name: String, // For internal use only.
30 pub vals: Vec<Val>, // Expected values.
31 pub reqd: bool, // The argument is required?
32 // CLI
33 pub hyph1: Option<String>, // Short form switch.
34 pub hyph2: Option<String>, // Long form switch.
35 pub help: Option<String>, // Argument help text.
36 pub prefs: SyntaxPrefs,
37}
38
39impl From<ArgConfig> for Arg {
40 fn from(cfg: ArgConfig) -> Self {
41 Self {
42 cfg: cfg,
43 ..Default::default()
44 }
45 }
46}
47
48impl Arg {
49
50 /// The name of an `Arg` is for internal use only, unlike a `Cmd`.
51 pub fn new<S: Into<String>>(name: S) -> Outcome<Self> {
52 let cfg = ArgConfig {
53 name: name.into(),
54 ..Default::default()
55 };
56 Ok(Self {
57 cfg: cfg,
58 ..Default::default()
59 })
60 }
61
62 pub fn config(&self) -> &ArgConfig { &self.cfg }
63
64 pub fn required(mut self, b: bool) -> Self {
65 self.cfg.reqd = b;
66 self
67 }
68
69 pub fn canonical_name(&self) -> String {
70 self.cfg.name.clone()
71 }
72
73 /// The argument's shortest name as it is written on a command line, hyphens
74 /// and all: the short form, else the long form, else the canonical name.
75 ///
76 /// This is what a message prints itself with, because the canonical name is
77 /// the one it is filed under and not one a reader could type back.
78 pub fn short_name(&self) -> String {
79 match (&self.cfg.hyph1, &self.cfg.hyph2) {
80 (Some(h1), _) => fmt!("{}{}", self.cfg.prefs.arg_hyph1_pfx, h1),
81 (None, Some(h2)) => fmt!("{}{}", self.cfg.prefs.arg_hyph2_pfx, h2),
82 (None, None) => self.cfg.name.clone(),
83 }
84 }
85
86 /// The argument's longest hyphenated name: the long form, else the short form, else the
87 /// canonical name.
88 pub fn long_name(&self) -> String {
89 match (&self.cfg.hyph1, &self.cfg.hyph2) {
90 (_, Some(h2)) => fmt!("{}{}", self.cfg.prefs.arg_hyph2_pfx, h2),
91 (Some(h1), None) => fmt!("{}{}", self.cfg.prefs.arg_hyph1_pfx, h1),
92 (None, None) => self.cfg.name.clone(),
93 }
94 }
95
96 /// Every hyphenated name the argument answers to, short form first.
97 pub fn hyphenated_names(&self) -> Vec<String> {
98 let mut names = Vec::new();
99 if let Some(h1) = &self.cfg.hyph1 {
100 names.push(fmt!("{}{}", self.cfg.prefs.arg_hyph1_pfx, h1));
101 }
102 if let Some(h2) = &self.cfg.hyph2 {
103 names.push(fmt!("{}{}", self.cfg.prefs.arg_hyph2_pfx, h2));
104 }
105 names
106 }
107
108 pub fn hyphen_check(&self, s: &str) -> Outcome<()> {
109 if s.starts_with('-') || s.contains(' ') {
110 return Err(err!(
111 "Single hyphen name '{}' for argument '{}' should not start with a \
112 hyphen or contain spaces",
113 s, self.config().name;
114 Input, Invalid));
115 }
116 Ok(())
117 }
118
119 pub fn hyph1<S: Into<String>>(mut self, s: S) -> Outcome<Self> {
120 let s = s.into();
121 res!(self.hyphen_check(s.as_str()));
122 self.cfg.hyph1 = Some(s);
123 Ok(self)
124 }
125
126 pub fn hyph2<S: Into<String>>(mut self, s: S) -> Outcome<Self> {
127 let s = s.into();
128 res!(self.hyphen_check(s.as_str()));
129 self.cfg.hyph2 = Some(s);
130 Ok(self)
131 }
132
133 pub fn expected_vals<V: Into<Val>>(mut self, vals: Vec<V>) -> Self {
134 self.cfg.vals = vals.into_iter().map(|v| v.into()).collect();
135 self
136 }
137
138 /// Allows user to attach a help string to the `Arg`.
139 pub fn help<S: Into<String>>(mut self, s: S) -> Self {
140 self.cfg.help = Some(s.into());
141 self
142 }
143
144 pub fn attach_arg(
145 self,
146 map: &mut BTreeMap<Key, Recursive<Key, Arg>>,
147 rargs: &mut Vec<String>
148 )
149 -> Outcome<()>
150 {
151 let required = self.config().reqd;
152 res!(Val::check_shape(&self.config().vals, &fmt!("argument '{}'", self.config().name)));
153 // Complete possible many-to-one mappings use the Arg before it gets moved when inserted.
154 // A missing short form registers nothing: it used to register a bare "-".
155 let mut keys = self.hyphenated_names();
156 keys.push(self.canonical_name());
157 for k in keys {
158 let k = Key::Str(k);
159 if let Some(Recursive::Key(Key::Id(other))) = map.get(&k) {
160 if *other != self.id {
161 return Err(err!(
162 "The argument '{}' would answer to '{}', which another argument \
163 already answers to.", self.config().name, k;
164 Input, Exists));
165 }
166 }
167 map.insert(k, Recursive::Key(Key::Id(self.id)));
168 }
169 if required {
170 rargs.push(self.canonical_name());
171 }
172 map.insert(Key::Id(self.id), Recursive::Val(self));
173 Ok(())
174 }
175}
176
177impl fmt::Display for Arg {
178 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179 let names = self.hyphenated_names();
180 if names.is_empty() {
181 ok!(write!(f, "'{}'", self.config().name));
182 }
183 for (i, name) in names.iter().enumerate() {
184 if i > 0 { ok!(write!(f, ", ")); }
185 ok!(write!(f, "'{}'", name));
186 }
187 Ok(())
188 }
189}
190
191impl From<CmdConfig> for Cmd {
192 fn from(cfg: CmdConfig) -> Self {
193 Self {
194 cfg: cfg,
195 ..Default::default()
196 }
197 }
198}
199