1use std::{
10 any::TypeId,
11 iter::Peekable,
12 ops::Range,
13 path::PathBuf,
14 sync::atomic::{AtomicBool, Ordering},
15};
16
17use crossterm::style::Color;
18
19use crate::{
20 context::Handle,
21 data::Pass,
22 form::{self, FormId},
23 text::{Text, txt},
24};
25
26macro_rules! implDeref {
27 ($type:ty, $target:ty $(, $($args:tt)+)?) => {
28 impl$(<$($args)+>)? std::ops::Deref for $type$(<$($args)+>)? {
29 type Target = $target;
30
31 fn deref(&self) -> &Self::Target {
32 &self.0
33 }
34 }
35
36 impl$(<$($args)+>)? std::ops::DerefMut for $type$(<$($args)+>)? {
37 fn deref_mut(&mut self) -> &mut Self::Target {
38 &mut self.0
39 }
40 }
41 }
42}
43
44pub trait Parameter: Sized + 'static {
62 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text>;
67
68 fn arg_name() -> Text {
74 txt!("[param]arg")
75 }
76}
77
78pub enum Flag<S: AsRef<str> = String> {
90 Word(S),
96 Blob(S),
103}
104
105impl Parameter for Flag {
106 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
107 let arg = args.next()?;
108 if !arg.is_quoted {
109 if let Some(word) = arg.strip_prefix("--") {
110 Ok((
111 Flag::Word(word.to_string()),
112 Some(form::id_of!("param.flag")),
113 ))
114 } else if let Some(blob_chars) = arg.strip_prefix("-") {
115 let mut blob = String::new();
116 for char in blob_chars.chars() {
117 if !blob.chars().any(|c| c == char) {
118 blob.push(char);
119 }
120 }
121
122 Ok((Flag::Blob(blob), Some(form::id_of!("param.flag"))))
123 } else {
124 Err(txt!("[param.info]Flag[]s must start with `-` or `--`"))
125 }
126 } else {
127 Err(txt!("Quoted arguments can't be [param.info]Flag[]s"))
128 }
129 }
130
131 fn arg_name() -> Text {
132 txt!("[param.flag]flag")
133 }
134}
135
136impl<S: AsRef<str>> Flag<S> {
137 pub fn as_word(self) -> Result<S, Text> {
139 match self {
140 Flag::Word(word) => Ok(word),
141 Flag::Blob(_) => Err(txt!(
142 "[param.info]Flag[] is of type [param.info]blob[], not [param.info]word"
143 )),
144 }
145 }
146
147 pub fn is_word(&self, word: &str) -> bool {
149 self.as_str().as_word().ok().is_some_and(|w| w == word)
150 }
151
152 pub fn as_blob(self) -> Result<S, Text> {
154 match self {
155 Flag::Blob(blob) => Ok(blob),
156 Flag::Word(_) => Err(txt!(
157 "[param.info]Flag[] is of type [param.info]word[], not [param.info]blob"
158 )),
159 }
160 }
161
162 pub fn has_blob(&self, blob: &str) -> bool {
165 self.as_str()
166 .as_blob()
167 .ok()
168 .is_some_and(|b| blob.chars().all(|char| b.chars().any(|c| c == char)))
169 }
170
171 pub fn word_from_list<const N: usize>(self, list: [&str; N]) -> Result<&str, Text> {
178 let word = self.as_word()?;
179 if let Some(word) = list.into_iter().find(|w| w == &word.as_ref()) {
180 Ok(word)
181 } else {
182 Err(txt!("Word not in list of valid options"))
183 }
184 }
185
186 pub fn as_str(&self) -> Flag<&str> {
190 match self {
191 Flag::Word(word) => Flag::Word(word.as_ref()),
192 Flag::Blob(blob) => Flag::Blob(blob.as_ref()),
193 }
194 }
195}
196
197pub struct Flags(pub Vec<Flag>);
207
208impl Parameter for Flags {
209 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
210 let mut list = Vec::new();
211
212 while let Ok(flag) = args.next_as(pa) {
213 list.push(flag);
214 }
215
216 Ok((Self(list), Some(form::id_of!("param.flag"))))
217 }
218
219 fn arg_name() -> Text {
220 txt!("[param.flag]flags...")
221 }
222}
223
224impl Flags {
225 pub fn has_word(&self, word: &str) -> bool {
228 self.0
229 .iter()
230 .any(|flag| flag.as_str().as_word() == Ok(word))
231 }
232
233 pub fn has_blob(&self, blob: &str) -> bool {
236 blob.chars().all(|char| {
237 self.0
238 .iter()
239 .filter_map(|flag| flag.as_str().as_blob().ok())
240 .any(|blob| blob.chars().any(|c| c == char))
241 })
242 }
243}
244
245impl std::ops::Deref for Flags {
246 type Target = Vec<Flag>;
247
248 fn deref(&self) -> &Self::Target {
249 &self.0
250 }
251}
252
253impl std::ops::DerefMut for Flags {
254 fn deref_mut(&mut self) -> &mut Self::Target {
255 &mut self.0
256 }
257}
258
259#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
266pub enum Scope {
267 Local,
275 Global,
283}
284
285impl Parameter for Scope {
286 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
287 if let Ok((flag, form)) = args.next_as_with_form::<Flag>(pa) {
288 if flag.is_word("global") {
289 Ok((Scope::Global, form))
290 } else {
291 Err(txt!("Invalid flag, it can only be [param.info]--global"))
292 }
293 } else {
294 Ok((Scope::Local, None))
295 }
296 }
297
298 fn arg_name() -> Text {
299 txt!("[param.flag]--global[param.punctuation]?")
300 }
301}
302
303impl<P: Parameter> Parameter for Option<P> {
304 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
311 match args.next_as::<P>(pa) {
312 Ok(arg) => Ok((Some(arg), None)),
313 Err(err) if args.is_forming_param => Err(err),
314 Err(_) => Ok((None, None)),
315 }
316 }
317
318 fn arg_name() -> Text {
319 txt!("{}[param.punctuation]?", P::arg_name())
320 }
321}
322
323impl<P: Parameter> Parameter for Vec<P> {
324 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
331 let mut returns = Vec::new();
332
333 loop {
334 match args.next_as::<P>(pa) {
335 Ok(ret) => returns.push(ret),
336 Err(err) if args.is_forming_param => return Err(err),
337 Err(_) => break Ok((returns, None)),
338 }
339 }
340 }
341
342 fn arg_name() -> Text {
343 txt!("{}[param.punctuation]...", P::arg_name())
344 }
345}
346
347impl<const N: usize, P: Parameter> Parameter for [P; N] {
348 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
355 use std::mem::MaybeUninit;
356 let mut returns = [const { MaybeUninit::uninit() }; N];
357
358 for r in returns.iter_mut() {
359 match args.next_as::<P>(pa) {
360 Ok(ret) => *r = MaybeUninit::new(ret),
361 Err(err) => return Err(err),
362 }
363 }
364
365 Ok((returns.map(|ret| unsafe { ret.assume_init() }), None))
366 }
367
368 fn arg_name() -> Text {
369 let punct = form::id_of!("param.punctuation");
370 txt!(
371 "{punct}[[{}{punct}; [param.count]{N}{punct}]]",
372 P::arg_name()
373 )
374 }
375}
376
377pub struct Between<const MIN: usize, const MAX: usize, P>(pub Vec<P>);
384
385impl<const MIN: usize, const MAX: usize, P: Parameter> Parameter for Between<MIN, MAX, P> {
386 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
393 let mut returns = Vec::new();
394
395 for _ in 0..MAX {
396 match args.next_as::<P>(pa) {
397 Ok(ret) => returns.push(ret),
398 Err(err) if args.is_forming_param => return Err(err),
399 Err(_) if returns.len() >= MIN => return Ok((Self(returns), None)),
400 Err(err) => return Err(err),
401 }
402 }
403
404 if returns.len() >= MIN {
405 Ok((Self(returns), None))
406 } else {
407 Err(txt!(
408 "List needed at least [param.info]{MIN}[] elements, got only [a]{}",
409 returns.len()
410 ))
411 }
412 }
413
414 fn arg_name() -> Text {
415 let punct = form::id_of!("param.punctuation");
416 let count = form::id_of!("param.count");
417 txt!(
418 "{punct}[[{}{punct}; {count}{MIN}{punct}..{count}{MAX}{punct}]]",
419 P::arg_name()
420 )
421 }
422}
423
424impl<const MIN: usize, const MAX: usize, P> std::ops::Deref for Between<MIN, MAX, P> {
425 type Target = Vec<P>;
426
427 fn deref(&self) -> &Self::Target {
428 &self.0
429 }
430}
431
432impl<const MIN: usize, const MAX: usize, P> std::ops::DerefMut for Between<MIN, MAX, P> {
433 fn deref_mut(&mut self) -> &mut Self::Target {
434 &mut self.0
435 }
436}
437
438impl Parameter for String {
439 fn new(_: &Pass, args: &mut Args) -> Result<(String, Option<FormId>), Text> {
440 Ok((args.next()?.to_string(), None))
441 }
442
443 fn arg_name() -> Text {
444 txt!("[param]arg")
445 }
446}
447
448pub struct Remainder(pub String);
452
453impl Parameter for Remainder {
454 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
455 let remainder: String = std::iter::from_fn(|| args.next().ok().map(|arg| arg.value))
456 .collect::<Vec<&str>>()
457 .join(" ");
458 if remainder.is_empty() {
459 Err(txt!("There are no more arguments"))
460 } else {
461 Ok((Self(remainder), None))
462 }
463 }
464
465 fn arg_name() -> Text {
466 txt!("[param]args")
467 }
468}
469implDeref!(Remainder, String);
470
471impl Parameter for Handle {
472 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
473 let buffer_name = args.next()?.value;
474 if let Some(handle) = crate::context::windows()
475 .buffers(pa)
476 .into_iter()
477 .find(|handle| handle.read(pa).name() == buffer_name)
478 {
479 Ok((handle, Some(form::id_of!("param.path.open"))))
480 } else {
481 Err(txt!("No buffer called [a]{buffer_name}[] open"))
482 }
483 }
484
485 fn arg_name() -> Text {
486 txt!("[param]buffer")
487 }
488}
489
490pub struct OtherBuffer(pub Handle);
495
496impl Parameter for OtherBuffer {
497 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
498 let handle = args.next_as::<Handle>(pa)?;
499 let cur_handle = crate::context::current_buffer(pa);
500 if cur_handle == handle {
501 Err(txt!("Argument can't be the current buffer"))
502 } else {
503 Ok((Self(handle), Some(form::id_of!("param.path.open"))))
504 }
505 }
506
507 fn arg_name() -> Text {
508 txt!("[param]buffer")
509 }
510}
511implDeref!(OtherBuffer, Handle);
512
513pub struct ValidFilePath(pub PathBuf, bool);
520
521impl Parameter for ValidFilePath {
522 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
523 let path = PathBuf::from(
524 shellexpand::full(args.next()?.value)
525 .map_err(|err| txt!("{err}"))?
526 .into_owned(),
527 );
528
529 let canon_path = path.canonicalize();
530 let path = if let Ok(path) = &canon_path {
531 if !path.is_file() {
532 return Err(txt!("Path is not a buffer"));
533 }
534 path.clone()
535 } else if canon_path.is_err()
536 && let Ok(canon_path) = path.with_file_name(".").canonicalize()
537 {
538 canon_path.join(
539 path.file_name()
540 .ok_or_else(|| txt!("Path has no buffer name"))?,
541 )
542 } else {
543 return Err(txt!("Path was not found"));
544 };
545
546 if let Some(parent) = path.parent()
547 && let Ok(false) | Err(_) = parent.try_exists()
548 {
549 return Err(txt!("Path's parent doesn't exist"));
550 }
551
552 let (form, exists_or_is_open) = if crate::context::windows()
553 .buffers(pa)
554 .into_iter()
555 .map(|handle| handle.read(pa).path())
556 .any(|p| std::path::Path::new(&p) == path)
557 {
558 (form::id_of!("param.path.open"), true)
559 } else if let Ok(true) = path.try_exists() {
560 (form::id_of!("param.path.exists"), true)
561 } else {
562 (form::id_of!("param.path"), false)
563 };
564
565 Ok((Self(path, exists_or_is_open), Some(form)))
566 }
567
568 fn arg_name() -> Text {
569 txt!("[param]path")
570 }
571}
572implDeref!(ValidFilePath, PathBuf);
573
574#[doc(hidden)]
582pub enum PathOrBufferOrCfg {
583 Path(PathBuf),
584 Buffer(Handle),
585 Cfg,
586 CfgManifest,
587}
588
589impl Parameter for PathOrBufferOrCfg {
590 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
591 struct DropGuard;
592 impl Drop for DropGuard {
593 fn drop(&mut self) {
594 ONLY_EXISTING.store(false, Ordering::Relaxed);
595 }
596 }
597
598 let _guard = DropGuard;
599
600 args.use_completions_for::<CfgOrManifest>();
601 args.use_completions_for::<Handle>();
602 args.use_completions_for::<ValidFilePath>();
603
604 if let Ok((cfg_or_manifest, form)) = args.next_as_with_form::<CfgOrManifest>(pa) {
605 match cfg_or_manifest {
606 CfgOrManifest::Cfg => Ok((Self::Cfg, form)),
607 CfgOrManifest::Manifest => Ok((Self::CfgManifest, form)),
608 }
609 } else if let Ok((handle, form)) = args.next_as_with_form::<Handle>(pa) {
610 Ok((Self::Buffer(handle), form))
611 } else {
612 let (path, form) = args.next_as_with_form::<ValidFilePath>(pa)?;
613 if !path.1 && ONLY_EXISTING.load(Ordering::Relaxed) {
614 Err(txt!("[a]{path}[]: No such file"))
615 } else {
616 Ok((Self::Path(path.0), form))
617 }
618 }
619 }
620
621 fn arg_name() -> Text {
622 let flag = form::id_of!("param.flag");
623 let punct = form::id_of!("param.punctuation");
624 txt!("[param]path{punct}/[param]buffer{punct}/{flag}--cfg{punct}/{flag}--cfg-manifest")
625 }
626}
627
628static ONLY_EXISTING: AtomicBool = AtomicBool::new(false);
629
630pub struct Existing;
632
633impl Parameter for Existing {
634 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
635 let initial = args.clone();
636 let Ok((flag, form)) = args.next_as_with_form::<Flag>(pa) else {
637 return Ok((Self, None));
638 };
639
640 if flag.is_word("existing") {
641 ONLY_EXISTING.store(true, Ordering::Relaxed);
642 } else {
643 *args = initial;
644 }
645
646 Ok((Self, form))
647 }
648
649 fn arg_name() -> Text {
650 txt!("[param.flag]--existing[param.punctuation]?")
651 }
652}
653
654pub enum CfgOrManifest {
659 Cfg,
661 Manifest,
663}
664
665impl Parameter for CfgOrManifest {
666 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
667 if let Ok((flag, form)) = args.next_as_with_form::<Flag>(pa)
668 && let Some(ret) = match flag.as_word()?.as_str() {
669 "cfg" => Some((Self::Cfg, form)),
670 "cfg-manifest" => Some((Self::Manifest, form)),
671 _ => None,
672 }
673 {
674 Ok(ret)
675 } else {
676 Err(txt!(
677 "Invalid flag, pick [param.flag]cfg[] or [param.flag]cfg-manifest"
678 ))
679 }
680 }
681
682 fn arg_name() -> Text {
683 txt!("[param.flag]--cfg[param.punctuation]/[param.flag]--cfg-manifest")
684 }
685}
686
687pub struct F32PercentOfU8(pub f32);
692
693impl Parameter for F32PercentOfU8 {
694 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
695 let arg = args.next()?;
696 if let Some(percentage) = arg.strip_suffix("%") {
697 let percentage: u8 = percentage
698 .parse()
699 .map_err(|_| txt!("[a]{arg}[] is not a valid percentage"))?;
700 if percentage <= 100 {
701 Ok((Self(percentage as f32 / 100.0), None))
702 } else {
703 Err(txt!("[a]{arg}[] is more than [a]100%"))
704 }
705 } else {
706 let byte: u8 = arg
707 .parse()
708 .map_err(|_| txt!("[a]{arg}[] couldn't be parsed"))?;
709 Ok((Self(byte as f32 / 255.0), None))
710 }
711 }
712
713 fn arg_name() -> Text {
714 let punct = form::id_of!("param.punctuation");
715 txt!("[param]u8{punct}/[param]0..=100%")
716 }
717}
718implDeref!(F32PercentOfU8, f32);
719
720impl Parameter for Color {
721 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
722 const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
723 t = if t < 0.0 { t + 1.0 } else { t };
724 t = if t > 1.0 { t - 1.0 } else { t };
725 if t < 1.0 / 6.0 {
726 p + (q - p) * 6.0 * t
727 } else if t < 1.0 / 2.0 {
728 q
729 } else if t < 2.0 / 3.0 {
730 p + (q - p) * (2.0 / 3.0 - t) * 6.0
731 } else {
732 p
733 }
734 }
735
736 let arg = args.next()?.value;
737 if let Some(hex) = arg.strip_prefix("#") {
739 let total = match u32::from_str_radix(hex, 16) {
740 Ok(total) if hex.len() == 6 => total,
741 _ => return Err(txt!("Hexcode does not contain 6 hex values")),
742 };
743 let r = (total >> 16) as u8;
744 let g = (total >> 8) as u8;
745 let b = total as u8;
746 Ok((Color::Rgb { r, g, b }, None))
747 } else if arg == "hsl" {
749 let hue = args.next_as::<F32PercentOfU8>(pa)?.0;
750 let sat = args.next_as::<F32PercentOfU8>(pa)?.0;
751 let lit = args.next_as::<F32PercentOfU8>(pa)?.0;
752 let [r, g, b] = if sat == 0.0 {
753 [lit.round() as u8; 3]
754 } else {
755 let q = if lit < 0.5 {
756 lit * (1.0 + sat)
757 } else {
758 lit + sat - lit * sat
759 };
760 let p = 2.0 * lit - q;
761 let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
762 let g = hue_to_rgb(p, q, hue);
763 let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
764 [r.round() as u8, g.round() as u8, b.round() as u8]
765 };
766 Ok((Color::Rgb { r, g, b }, None))
767 } else {
768 Err(txt!("Color format was not recognized"))
769 }
770 }
771
772 fn arg_name() -> Text {
773 txt!("[param]#{{rgb hex}}[param.punctuation]/[param]hsl {{h}} {{s}} {{l}}")
774 }
775}
776
777pub struct FormName(pub String);
782
783impl Parameter for FormName {
784 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
785 let arg = args.next()?.value;
786 if !arg.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
787 return Err(txt!(
788 "Expected identifiers separated by '.'s, found [a]{arg}"
789 ));
790 }
791 if crate::form::exists(arg) {
792 Ok((Self(arg.to_string()), Some(form::id_of_non_static(arg))))
793 } else {
794 Err(txt!("The form [a]{arg}[] has not been set"))
795 }
796 }
797
798 fn arg_name() -> Text {
799 txt!("[param]form")
800 }
801}
802
803implDeref!(FormName, String);
804
805pub struct ColorSchemeArg(pub String);
807
808impl Parameter for ColorSchemeArg {
809 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
810 let scheme = args.next()?.value;
811 if crate::form::colorscheme_exists(scheme) {
812 Ok((ColorSchemeArg(scheme.to_string()), None))
813 } else {
814 Err(txt!("The colorscheme [a]{scheme}[] was not found"))
815 }
816 }
817
818 fn arg_name() -> Text {
819 txt!("[param]colorscheme")
820 }
821}
822implDeref!(ColorSchemeArg, String);
823
824#[doc(hidden)]
826pub struct ReloadOptions {
827 pub clean: bool,
829 pub update: bool,
831}
832
833impl Parameter for ReloadOptions {
834 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
835 let flags = args.next_as::<Flags>(pa)?;
836
837 if flags
838 .iter()
839 .any(|flag| !flag.is_word("update") && !flag.is_word("clean"))
840 {
841 Err(txt!("Invalid [a]Flag"))
842 } else {
843 Ok((
844 Self {
845 clean: flags.has_word("clean"),
846 update: flags.has_word("update"),
847 },
848 Some(form::id_of!("param.flag")),
849 ))
850 }
851 }
852
853 fn arg_name() -> Text {
854 let punct = form::id_of!("param.punctuation");
855 txt!("[param.flag]--clean{punct}? [param.flag]--update{punct}?")
856 }
857}
858
859#[derive(Clone)]
861pub struct Args<'a> {
862 iter: Peekable<ArgsIter<'a>>,
863 param_range: Range<usize>,
864 has_to_start_param: bool,
865 is_forming_param: bool,
866 arg_n: usize,
871 currently_parsing: Vec<TypeId>,
875 last_parsed: Vec<(usize, TypeId)>,
881}
882
883impl<'arg> Args<'arg> {
884 pub(super) fn new(command: &'arg str) -> Self {
886 Self {
887 iter: ArgsIter::new(command).peekable(),
888 param_range: 0..0,
889 has_to_start_param: false,
890 is_forming_param: false,
891 arg_n: 0,
892 currently_parsing: Vec::new(),
893 last_parsed: Vec::new(),
894 }
895 }
896
897 #[allow(clippy::should_implement_trait)]
899 pub fn next(&mut self) -> Result<Arg<'arg>, Text> {
900 match self.iter.next() {
901 Some((value, range, is_quoted)) => {
902 self.param_range = range.clone();
903 if self.has_to_start_param {
904 self.has_to_start_param = false;
905 self.is_forming_param = true;
906 }
907
908 self.last_parsed.retain(|(start_n, ty)| {
909 self.currently_parsing.contains(ty) || self.arg_n == *start_n
910 });
911
912 self.arg_n += 1;
913
914 Ok(Arg { value, is_quoted })
915 }
916 None => Err(txt!("Wrong argument count")),
917 }
918 }
919
920 pub fn next_as<P: Parameter>(&mut self, pa: &Pass) -> Result<P, Text> {
925 self.next_as_with_form(pa).map(|(param, _)| param)
926 }
927
928 pub fn next_as_with_form<P: Parameter>(
934 &mut self,
935 pa: &Pass,
936 ) -> Result<(P, Option<FormId>), Text> {
937 self.currently_parsing.push(TypeId::of::<P>());
938
939 let initial = (
940 self.iter.clone(),
941 self.param_range.clone(),
942 self.has_to_start_param,
943 self.is_forming_param,
944 self.arg_n,
945 );
946
947 self.has_to_start_param = true;
948 let ret = P::new(pa, self);
949 if ret.is_ok() {
950 self.is_forming_param = false;
951 } else {
952 self.iter = initial.0;
953 self.param_range = initial.1;
954 self.has_to_start_param = initial.2;
955 self.is_forming_param = initial.3;
956 self.arg_n = initial.4;
957 }
958
959 self.currently_parsing.retain(|&ty| ty != TypeId::of::<P>());
960
961 ret
962 }
963
964 pub fn next_else<T: Into<Text>>(&mut self, to_text: T) -> Result<&'arg str, Text> {
966 match self.next() {
967 Ok(arg) => Ok(arg.value),
968 Err(_) => Err(to_text.into()),
969 }
970 }
971
972 pub fn next_start(&mut self) -> Option<usize> {
978 self.iter.peek().map(|(_, r, _)| r.start)
979 }
980
981 pub fn param_range(&self) -> Range<usize> {
987 self.param_range.clone()
988 }
989
990 pub fn last_parsed(&self) -> Vec<TypeId> {
994 self.last_parsed.iter().map(|(_, ty)| *ty).collect()
995 }
996
997 pub fn use_completions_for<P: Parameter>(&mut self) {
1004 self.last_parsed.push((self.arg_n, TypeId::of::<P>()));
1005 }
1006}
1007
1008#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1010pub struct Arg<'arg> {
1011 pub value: &'arg str,
1013 pub is_quoted: bool,
1019}
1020
1021impl<'arg> std::ops::Deref for Arg<'arg> {
1022 type Target = &'arg str;
1023
1024 fn deref(&self) -> &Self::Target {
1025 &self.value
1026 }
1027}
1028
1029impl<'arg, 'other> PartialEq<&'other str> for Arg<'arg> {
1030 fn eq(&self, other: &&'other str) -> bool {
1031 &self.value == other
1032 }
1033}
1034
1035#[derive(Clone)]
1040pub struct ArgsIter<'a> {
1041 command: &'a str,
1042 chars: std::str::CharIndices<'a>,
1043 start: Option<usize>,
1044 end: Option<usize>,
1045 is_quoting: bool,
1046 last_char: char,
1047}
1048
1049impl<'a> ArgsIter<'a> {
1050 pub fn new(command: &'a str) -> Self {
1052 let mut args_iter = Self {
1053 command,
1054 chars: command.char_indices(),
1055 start: None,
1056 end: None,
1057 is_quoting: false,
1058 last_char: 'a',
1060 };
1061
1062 args_iter.next();
1063 args_iter
1064 }
1065}
1066
1067impl<'a> Iterator for ArgsIter<'a> {
1068 type Item = (&'a str, Range<usize>, bool);
1069
1070 fn next(&mut self) -> Option<Self::Item> {
1071 let mut is_quoted = false;
1072 while let Some((b, char)) = self.chars.next() {
1073 let lc = self.last_char;
1074 self.last_char = char;
1075 if self.start.is_some() && char.is_whitespace() && !self.is_quoting {
1076 self.end = Some(b);
1077 break;
1078 } else if char == '\'' && lc != '\\' {
1079 self.is_quoting = !self.is_quoting;
1080 if !self.is_quoting {
1081 is_quoted = true;
1082 self.end = Some(b);
1083 break;
1084 } else {
1085 self.start = Some(b + 1);
1086 }
1087 } else if !char.is_whitespace() && self.start.is_none() {
1088 self.start = Some(b);
1089 }
1090 }
1091
1092 let e = self.end.take().unwrap_or(self.command.len());
1093 self.start
1094 .take()
1095 .map(|s| (&self.command[s..e], s..e, is_quoted))
1096 }
1097}
1098
1099macro_rules! parse_impl {
1100 ($t:ty, $static_list:expr) => {
1101 impl Parameter for $t {
1102 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
1103 let arg = args.next()?;
1104 let arg = arg.parse().map_err(|_| {
1105 txt!(
1106 "[a]{arg}[] couldn't be parsed as [param.info]{}[]",
1107 stringify!($t)
1108 )
1109 });
1110 arg.map(|arg| (arg, None))
1111 }
1112
1113 fn arg_name() -> Text {
1114 txt!("[param]{}", stringify!($t))
1115 }
1116 }
1117 };
1118}
1119
1120parse_impl!(bool, Some(&["true", "false"]));
1121parse_impl!(u8, None);
1122parse_impl!(u16, None);
1123parse_impl!(u32, None);
1124parse_impl!(u64, None);
1125parse_impl!(u128, None);
1126parse_impl!(usize, None);
1127parse_impl!(i8, None);
1128parse_impl!(i16, None);
1129parse_impl!(i32, None);
1130parse_impl!(i64, None);
1131parse_impl!(i128, None);
1132parse_impl!(isize, None);
1133parse_impl!(f32, None);
1134parse_impl!(f64, None);
1135parse_impl!(path, Some(&[]));
1136
1137#[allow(non_camel_case_types)]
1138type path = std::path::PathBuf;