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);
809
810impl Parameter for ColorSchemeArg {
811 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
812 let scheme = args.next()?.value;
813 if crate::form::colorscheme_exists(scheme) {
814 Ok((ColorSchemeArg(scheme.to_string()), None))
815 } else {
816 Err(txt!("The colorscheme [a]{scheme}[] was not found"))
817 }
818 }
819
820 fn arg_name() -> Text {
821 txt!("[param]colorscheme")
822 }
823}
824implDeref!(ColorSchemeArg, String);
825
826#[doc(hidden)]
828pub struct ReloadOptions {
829 pub clean: bool,
831 pub update: bool,
833}
834
835impl Parameter for ReloadOptions {
836 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
837 let flags = args.next_as::<Flags>(pa)?;
838
839 if flags
840 .iter()
841 .any(|flag| !flag.is_word("update") && !flag.is_word("clean"))
842 {
843 Err(txt!("Invalid [a]Flag"))
844 } else {
845 Ok((
846 Self {
847 clean: flags.has_word("clean"),
848 update: flags.has_word("update"),
849 },
850 Some(form::id_of!("param.flag")),
851 ))
852 }
853 }
854
855 fn arg_name() -> Text {
856 let punct = form::id_of!("param.punctuation");
857 txt!("[param.flag]--clean{punct}? [param.flag]--update{punct}?")
858 }
859}
860
861#[derive(Clone)]
863pub struct Args<'a> {
864 iter: Peekable<ArgsIter<'a>>,
865 param_range: Range<usize>,
866 has_to_start_param: bool,
867 is_forming_param: bool,
868 arg_n: usize,
873 currently_parsing: Vec<TypeId>,
877 last_parsed: Vec<(usize, TypeId)>,
883}
884
885impl<'arg> Args<'arg> {
886 pub(super) fn new(command: &'arg str) -> Self {
888 Self {
889 iter: ArgsIter::new(command).peekable(),
890 param_range: 0..0,
891 has_to_start_param: false,
892 is_forming_param: false,
893 arg_n: 0,
894 currently_parsing: Vec::new(),
895 last_parsed: Vec::new(),
896 }
897 }
898
899 #[allow(clippy::should_implement_trait)]
901 pub fn next(&mut self) -> Result<Arg<'arg>, Text> {
902 match self.iter.next() {
903 Some((value, range, is_quoted)) => {
904 self.param_range = range.clone();
905 if self.has_to_start_param {
906 self.has_to_start_param = false;
907 self.is_forming_param = true;
908 }
909
910 self.last_parsed.retain(|(start_n, ty)| {
911 self.currently_parsing.contains(ty) || self.arg_n == *start_n
912 });
913
914 self.arg_n += 1;
915
916 Ok(Arg { value, is_quoted })
917 }
918 None => Err(txt!("Wrong argument count")),
919 }
920 }
921
922 pub fn next_as<P: Parameter>(&mut self, pa: &Pass) -> Result<P, Text> {
927 self.next_as_with_form(pa).map(|(param, _)| param)
928 }
929
930 pub fn next_as_with_form<P: Parameter>(
936 &mut self,
937 pa: &Pass,
938 ) -> Result<(P, Option<FormId>), Text> {
939 self.currently_parsing.push(TypeId::of::<P>());
940
941 let initial = (
942 self.iter.clone(),
943 self.param_range.clone(),
944 self.has_to_start_param,
945 self.is_forming_param,
946 self.arg_n,
947 );
948
949 self.has_to_start_param = true;
950 let ret = P::new(pa, self);
951 if ret.is_ok() {
952 self.is_forming_param = false;
953 } else {
954 self.iter = initial.0;
955 self.param_range = initial.1;
956 self.has_to_start_param = initial.2;
957 self.is_forming_param = initial.3;
958 self.arg_n = initial.4;
959 }
960
961 self.currently_parsing.retain(|&ty| ty != TypeId::of::<P>());
962
963 ret
964 }
965
966 pub fn next_else<T: Into<Text>>(&mut self, to_text: T) -> Result<&'arg str, Text> {
968 match self.next() {
969 Ok(arg) => Ok(arg.value),
970 Err(_) => Err(to_text.into()),
971 }
972 }
973
974 pub fn next_start(&mut self) -> Option<usize> {
980 self.iter.peek().map(|(_, r, _)| r.start)
981 }
982
983 pub fn param_range(&self) -> Range<usize> {
989 self.param_range.clone()
990 }
991
992 pub fn last_parsed(&self) -> Vec<TypeId> {
996 self.last_parsed.iter().map(|(_, ty)| *ty).collect()
997 }
998
999 pub fn use_completions_for<P: Parameter>(&mut self) {
1006 self.last_parsed.push((self.arg_n, TypeId::of::<P>()));
1007 }
1008}
1009
1010#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
1012pub struct Arg<'arg> {
1013 pub value: &'arg str,
1015 pub is_quoted: bool,
1021}
1022
1023impl<'arg> std::ops::Deref for Arg<'arg> {
1024 type Target = &'arg str;
1025
1026 fn deref(&self) -> &Self::Target {
1027 &self.value
1028 }
1029}
1030
1031impl<'arg, 'other> PartialEq<&'other str> for Arg<'arg> {
1032 fn eq(&self, other: &&'other str) -> bool {
1033 &self.value == other
1034 }
1035}
1036
1037#[derive(Clone)]
1042pub struct ArgsIter<'a> {
1043 command: &'a str,
1044 chars: std::str::CharIndices<'a>,
1045 start: Option<usize>,
1046 end: Option<usize>,
1047 is_quoting: bool,
1048 last_char: char,
1049}
1050
1051impl<'a> ArgsIter<'a> {
1052 pub fn new(command: &'a str) -> Self {
1054 let mut args_iter = Self {
1055 command,
1056 chars: command.char_indices(),
1057 start: None,
1058 end: None,
1059 is_quoting: false,
1060 last_char: 'a',
1062 };
1063
1064 args_iter.next();
1065 args_iter
1066 }
1067}
1068
1069impl<'a> Iterator for ArgsIter<'a> {
1070 type Item = (&'a str, Range<usize>, bool);
1071
1072 fn next(&mut self) -> Option<Self::Item> {
1073 let mut is_quoted = false;
1074 while let Some((b, char)) = self.chars.next() {
1075 let lc = self.last_char;
1076 self.last_char = char;
1077 if self.start.is_some() && char.is_whitespace() && !self.is_quoting {
1078 self.end = Some(b);
1079 break;
1080 } else if char == '\'' && lc != '\\' {
1081 self.is_quoting = !self.is_quoting;
1082 if !self.is_quoting {
1083 is_quoted = true;
1084 self.end = Some(b);
1085 break;
1086 } else {
1087 self.start = Some(b + 1);
1088 }
1089 } else if !char.is_whitespace() && self.start.is_none() {
1090 self.start = Some(b);
1091 }
1092 }
1093
1094 let e = self.end.take().unwrap_or(self.command.len());
1095 self.start
1096 .take()
1097 .map(|s| (&self.command[s..e], s..e, is_quoted))
1098 }
1099}
1100
1101macro_rules! parse_impl {
1102 ($t:ty, $static_list:expr) => {
1103 impl Parameter for $t {
1104 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
1105 let arg = args.next()?;
1106 let arg = arg.parse().map_err(|_| {
1107 txt!(
1108 "[a]{arg}[] couldn't be parsed as [param.info]{}[]",
1109 stringify!($t)
1110 )
1111 });
1112 arg.map(|arg| (arg, None))
1113 }
1114
1115 fn arg_name() -> Text {
1116 txt!("[param]{}", stringify!($t))
1117 }
1118 }
1119 };
1120}
1121
1122parse_impl!(bool, Some(&["true", "false"]));
1123parse_impl!(u8, None);
1124parse_impl!(u16, None);
1125parse_impl!(u32, None);
1126parse_impl!(u64, None);
1127parse_impl!(u128, None);
1128parse_impl!(usize, None);
1129parse_impl!(i8, None);
1130parse_impl!(i16, None);
1131parse_impl!(i32, None);
1132parse_impl!(i64, None);
1133parse_impl!(i128, None);
1134parse_impl!(isize, None);
1135parse_impl!(f32, None);
1136parse_impl!(f64, None);
1137parse_impl!(path, Some(&[]));
1138
1139#[allow(non_camel_case_types)]
1140type path = std::path::PathBuf;