1use std::borrow::Cow;
2use std::ops::Deref;
3use std::ops::DerefMut;
4
5use crate::block::Block;
6use crate::error::Error;
7use crate::format::HelpDisplay;
8use crate::format::HelpPolicy;
9use crate::store::Store;
10
11#[derive(Debug, Default, Clone)]
12pub struct Command<'a> {
13 name: Cow<'a, str>,
14
15 hint: Cow<'a, str>,
16
17 help: Cow<'a, str>,
18
19 foot: Cow<'a, str>,
20
21 head: Cow<'a, str>,
22
23 blocks: Vec<Block<'a, Cow<'a, str>>>,
24
25 stores: Vec<Store<'a>>,
26}
27
28impl<'a> Command<'a> {
29 pub fn new<S: Into<Cow<'a, str>>>(name: S, hint: S, help: S, head: S, foot: S) -> Self {
30 Self {
31 name: name.into(),
32 foot: foot.into(),
33 head: head.into(),
34 hint: hint.into(),
35 help: help.into(),
36 blocks: vec![],
37 stores: vec![],
38 }
39 }
40
41 pub fn name(&self) -> Cow<'a, str> {
42 self.name.clone()
43 }
44
45 pub fn hint(&self) -> Cow<'a, str> {
46 self.hint.clone()
47 }
48
49 pub fn help(&self) -> Cow<'a, str> {
50 self.help.clone()
51 }
52
53 pub fn head(&self) -> Cow<'a, str> {
54 self.head.clone()
55 }
56
57 pub fn foot(&self) -> Cow<'a, str> {
58 self.foot.clone()
59 }
60
61 pub fn block(&self) -> &Vec<Block<'a, Cow<'a, str>>> {
62 &self.blocks
63 }
64
65 pub fn block_mut(&mut self) -> &mut Vec<Block<'a, Cow<'a, str>>> {
66 &mut self.blocks
67 }
68
69 pub fn has_position(&self) -> bool {
70 self.stores.iter().any(|store| store.position())
71 }
72
73 pub fn find_store<S: Into<Cow<'a, str>>>(&self, name: S) -> Option<&Store<'a>> {
74 let name = name.into();
75 self.stores.iter().find(|v| v.name() == name)
76 }
77
78 pub fn find_store_mut<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Option<&mut Store<'a>> {
79 let name = name.into();
80 self.stores.iter_mut().find(|v| v.name() == name)
81 }
82
83 pub fn find_block<S: Into<Cow<'a, str>>>(&self, name: S) -> Option<&Block<'a, Cow<'a, str>>> {
84 let name = name.into();
85 self.blocks.iter().find(|v| v.name() == name)
86 }
87
88 pub fn find_block_mut<S: Into<Cow<'a, str>>>(
89 &mut self,
90 name: S,
91 ) -> Option<&mut Block<'a, Cow<'a, str>>> {
92 let name = name.into();
93 self.blocks.iter_mut().find(|v| v.name() == name)
94 }
95
96 pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
97 self.name = name.into();
98 self
99 }
100
101 pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
102 self.hint = hint.into();
103 self
104 }
105
106 pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
107 self.help = help.into();
108 self
109 }
110
111 pub fn set_foot<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
112 self.foot = help.into();
113 self
114 }
115
116 pub fn set_head<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
117 self.head = help.into();
118 self
119 }
120
121 pub fn add_store<S: Into<Cow<'a, str>>>(
122 &mut self,
123 block: S,
124 store: Store<'a>,
125 ) -> Result<&mut Self, Error> {
126 let name: Cow<'a, str> = block.into();
127
128 if self.find_store(name.clone()).is_some() {
129 Err(Error::DuplicatedStoreName(store.name().to_string()))
130 } else {
131 let block = self
132 .find_block_mut(name.clone())
133 .ok_or_else(|| Error::InvalidBlockName(name.to_string()))?;
134
135 block.attach(store.name());
136 self.stores.push(store);
137 Ok(self)
138 }
139 }
140
141 pub fn add_block(&mut self, block: Block<'a, Cow<'a, str>>) -> Result<&mut Self, Error> {
142 let name = block.name();
143
144 if self.find_block(name.clone()).is_some() {
145 Err(Error::DuplicatedBlockName(name.to_string()))
146 } else {
147 self.blocks.push(block);
148 Ok(self)
149 }
150 }
151
152 pub fn new_store<S: Into<Cow<'a, str>>>(
153 &mut self,
154 block: S,
155 name: S,
156 ) -> Result<AddStore2Block<'a, '_>, Error> {
157 let name = name.into();
158
159 if self.find_store(name.clone()).is_some() {
160 Err(Error::DuplicatedStoreName(name.to_string()))
161 } else {
162 let block = block.into();
163 let block = self
164 .blocks
165 .iter_mut()
166 .find(|v| v.name() == block)
167 .ok_or_else(|| Error::InvalidBlockName(block.to_string()))?;
168 let stores = &mut self.stores;
169
170 Ok(AddStore2Block::new(block, stores, name))
171 }
172 }
173
174 pub fn new_block<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Result<BlockMut<'a, '_>, Error> {
175 let name = name.into();
176
177 if self.find_block(name.clone()).is_some() {
178 Err(Error::DuplicatedBlockName(name.to_string()))
179 } else {
180 Ok(BlockMut::new(self, name))
181 }
182 }
183}
184
185impl<'a> Deref for Command<'a> {
186 type Target = Vec<Store<'a>>;
187
188 fn deref(&self) -> &Self::Target {
189 &self.stores
190 }
191}
192
193impl DerefMut for Command<'_> {
194 fn deref_mut(&mut self) -> &mut Self::Target {
195 &mut self.stores
196 }
197}
198
199impl HelpDisplay for Command<'_> {
200 fn gen_help<'a, P>(&self, policy: &P) -> Option<Cow<'a, str>>
201 where
202 Self: 'a,
203 P: HelpPolicy<'a, Self>,
204 {
205 policy.format(self)
206 }
207}
208
209pub struct AddStore2Block<'a, 'b> {
210 store: Store<'a>,
211
212 block: &'b mut Block<'a, Cow<'a, str>>,
213
214 stores: &'b mut Vec<Store<'a>>,
215
216 added: bool,
217}
218
219impl<'a, 'b> AddStore2Block<'a, 'b> {
220 pub fn new<S: Into<Cow<'a, str>>>(
221 block: &'b mut Block<'a, Cow<'a, str>>,
222 stores: &'b mut Vec<Store<'a>>,
223 name: S,
224 ) -> Self {
225 let mut store = Store::default();
226
227 store.set_optional(true);
228 store.set_name(name);
229 Self {
230 block,
231 store,
232 stores,
233 added: false,
234 }
235 }
236
237 pub fn set_optional(&mut self, optional: bool) -> &mut Self {
238 self.store.set_optional(optional);
239 self
240 }
241
242 pub fn set_position(&mut self, position: bool) -> &mut Self {
243 self.store.set_position(position);
244 self
245 }
246
247 pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
248 self.store.set_hint(hint);
249 self
250 }
251
252 pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
253 self.store.set_help(help);
254 self
255 }
256
257 pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
258 self.store.set_name(name);
259 self
260 }
261
262 pub fn set_type<S: Into<Cow<'a, str>>>(&mut self, type_name: S) -> &mut Self {
263 self.store.set_type(type_name);
264 self
265 }
266
267 pub fn submit(mut self) {
268 if !self.added {
269 let store = std::mem::take(&mut self.store);
270
271 self.block.attach(store.name());
272 self.stores.push(store);
273 self.added = true;
274 }
275 }
276}
277
278impl Drop for AddStore2Block<'_, '_> {
279 fn drop(&mut self) {
280 if !self.added {
281 let store = std::mem::take(&mut self.store);
282
283 self.block.attach(store.name());
284 self.stores.push(store);
285 self.added = true;
286 }
287 }
288}
289
290pub struct BlockMut<'a, 'b> {
291 block: Block<'a, Cow<'a, str>>,
292
293 cmd: &'b mut Command<'a>,
294
295 added: bool,
296}
297
298impl<'a, 'b> BlockMut<'a, 'b> {
299 pub fn new<S: Into<Cow<'a, str>>>(cmd: &'b mut Command<'a>, name: S) -> Self {
300 let mut opt = Block::default();
301
302 opt.set_name(name);
303 Self {
304 cmd,
305 block: opt,
306 added: false,
307 }
308 }
309
310 pub fn attach<S: Into<Cow<'a, str>>>(&mut self, name: S) -> Result<&mut Self, Error> {
311 let name = name.into();
312
313 if self.cmd.find_store(name.clone()).is_none() {
314 Err(Error::InvalidStoreName(name.to_string()))
315 } else if self.block.iter().any(|v| v == &name) {
316 Err(Error::DuplicatedStoreName(name.to_string()))
317 } else {
318 self.block.attach(name);
319 Ok(self)
320 }
321 }
322
323 pub fn set_hint<S: Into<Cow<'a, str>>>(&mut self, hint: S) -> &mut Self {
324 self.block.set_hint(hint);
325 self
326 }
327
328 pub fn set_help<S: Into<Cow<'a, str>>>(&mut self, help: S) -> &mut Self {
329 self.block.set_help(help);
330 self
331 }
332
333 pub fn set_foot<S: Into<Cow<'a, str>>>(&mut self, footer: S) -> &mut Self {
334 self.block.set_foot(footer);
335 self
336 }
337
338 pub fn set_head<S: Into<Cow<'a, str>>>(&mut self, header: S) -> &mut Self {
339 self.block.set_head(header);
340 self
341 }
342
343 pub fn set_name<S: Into<Cow<'a, str>>>(&mut self, name: S) -> &mut Self {
344 self.block.set_name(name);
345 self
346 }
347
348 pub fn new_store<S: Into<Cow<'a, str>>>(&mut self, store: S) -> AddStore2Block<'a, '_> {
349 let block = &mut self.block;
350 let stores = &mut self.cmd.stores;
351
352 AddStore2Block::new(block, stores, store)
353 }
354
355 pub fn submit(mut self) {
356 if !self.added {
357 let block = std::mem::take(&mut self.block);
358
359 self.cmd.add_block(block).unwrap();
360 self.added = true;
361 }
362 }
363}
364
365impl Drop for BlockMut<'_, '_> {
366 fn drop(&mut self) {
367 if !self.added {
368 let block = std::mem::take(&mut self.block);
369 let cmd_name = self.cmd.name();
370
371 self.cmd
372 .add_block(block)
373 .unwrap_or_else(|_| panic!("Can not add block to Command {cmd_name}"));
374 }
375 }
376}