1use std::{iter::Peekable, ops::Range, path::PathBuf};
10
11use crossterm::style::Color;
12
13use crate::{
14 context::Handle,
15 data::Pass,
16 form::{self, FormId},
17 text::{Text, txt},
18};
19
20macro_rules! implDeref {
21 ($type:ty, $target:ty $(, $($args:tt)+)?) => {
22 impl$(<$($args)+>)? std::ops::Deref for $type$(<$($args)+>)? {
23 type Target = $target;
24
25 fn deref(&self) -> &Self::Target {
26 &self.0
27 }
28 }
29
30 impl$(<$($args)+>)? std::ops::DerefMut for $type$(<$($args)+>)? {
31 fn deref_mut(&mut self) -> &mut Self::Target {
32 &mut self.0
33 }
34 }
35 }
36}
37
38pub trait Parameter: Sized {
56 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text>;
61}
62
63impl<P: Parameter> Parameter for Option<P> {
64 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
71 match args.next_as::<P>(pa) {
72 Ok(arg) => Ok((Some(arg), None)),
73 Err(err) if args.is_forming_param => Err(err),
74 Err(_) => Ok((None, None)),
75 }
76 }
77}
78
79impl<P: Parameter> Parameter for Vec<P> {
80 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
87 let mut returns = Vec::new();
88
89 loop {
90 match args.next_as::<P>(pa) {
91 Ok(ret) => returns.push(ret),
92 Err(err) if args.is_forming_param => return Err(err),
93 Err(_) => break Ok((returns, None)),
94 }
95 }
96 }
97}
98
99impl<const N: usize, P: Parameter> Parameter for [P; N] {
100 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
107 use std::mem::MaybeUninit;
108 let mut returns = [const { MaybeUninit::uninit() }; N];
109
110 for r in returns.iter_mut() {
111 match args.next_as::<P>(pa) {
112 Ok(ret) => *r = MaybeUninit::new(ret),
113 Err(err) => return Err(err),
114 }
115 }
116
117 Ok((returns.map(|ret| unsafe { ret.assume_init() }), None))
118 }
119}
120
121pub struct Between<const MIN: usize, const MAX: usize, P>(pub Vec<P>);
128
129impl<const MIN: usize, const MAX: usize, P: Parameter> Parameter for Between<MIN, MAX, P> {
130 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
137 let mut returns = Vec::new();
138
139 for _ in 0..MAX {
140 match args.next_as::<P>(pa) {
141 Ok(ret) => returns.push(ret),
142 Err(err) if args.is_forming_param => return Err(err),
143 Err(_) if returns.len() >= MIN => return Ok((Self(returns), None)),
144 Err(err) => return Err(err),
145 }
146 }
147
148 if returns.len() >= MIN {
149 Ok((Self(returns), None))
150 } else {
151 Err(txt!(
152 "List needed at least [a]{MIN}[] elements, got only [a]{}",
153 returns.len()
154 ))
155 }
156 }
157}
158
159impl<const MIN: usize, const MAX: usize, P> std::ops::Deref for Between<MIN, MAX, P> {
160 type Target = Vec<P>;
161
162 fn deref(&self) -> &Self::Target {
163 &self.0
164 }
165}
166
167impl<const MIN: usize, const MAX: usize, P> std::ops::DerefMut for Between<MIN, MAX, P> {
168 fn deref_mut(&mut self) -> &mut Self::Target {
169 &mut self.0
170 }
171}
172
173impl Parameter for String {
174 fn new(_: &Pass, args: &mut Args) -> Result<(String, Option<FormId>), Text> {
175 Ok((args.next()?.to_string(), None))
176 }
177}
178
179pub struct Remainder(pub String);
183
184impl Parameter for Remainder {
185 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
186 let remainder: String = std::iter::from_fn(|| args.next().ok())
187 .collect::<Vec<&str>>()
188 .join(" ");
189 if remainder.is_empty() {
190 Err(txt!("There are no more arguments"))
191 } else {
192 Ok((Self(remainder), None))
193 }
194 }
195}
196implDeref!(Remainder, String);
197
198pub struct ColorSchemeArg(pub String);
202
203impl Parameter for ColorSchemeArg {
204 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
205 let scheme = args.next()?;
206 if crate::form::colorscheme_exists(scheme) {
207 Ok((ColorSchemeArg(scheme.to_string()), None))
208 } else {
209 Err(txt!("The colorscheme [a]{scheme}[] was not found"))
210 }
211 }
212}
213implDeref!(ColorSchemeArg, String);
214
215impl Parameter for Handle {
216 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
217 let buffer_name = args.next()?;
218 if let Some(handle) = crate::context::windows()
219 .buffers(pa)
220 .find(|handle| handle.read(pa).name() == buffer_name)
221 {
222 Ok((handle, Some(form::id_of!("param.buffer.open"))))
223 } else {
224 Err(txt!("No buffer called [a]{buffer_name}[] open"))
225 }
226 }
227}
228
229pub struct OtherBuffer(pub Handle);
234
235impl Parameter for OtherBuffer {
236 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
237 let handle = args.next_as::<Handle>(pa)?;
238 let cur_handle = crate::context::current_buffer(pa);
239 if *cur_handle == handle {
240 Err(txt!("Argument can't be the current buffer"))
241 } else {
242 Ok((Self(handle), Some(form::id_of!("param.buffer.open"))))
243 }
244 }
245}
246implDeref!(OtherBuffer, Handle);
247
248pub struct ValidFilePath(pub PathBuf);
255
256impl Parameter for ValidFilePath {
257 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
258 let path = args.next_as::<PathBuf>(pa)?;
259
260 let canon_path = path.canonicalize();
261 let path = if let Ok(path) = &canon_path {
262 if !path.is_file() {
263 return Err(txt!("Path is not a buffer"));
264 }
265 path.clone()
266 } else if canon_path.is_err()
267 && let Ok(canon_path) = path.with_file_name(".").canonicalize()
268 {
269 canon_path.join(
270 path.file_name()
271 .ok_or_else(|| txt!("Path has no buffer name"))?,
272 )
273 } else {
274 return Err(txt!("Path was not found"));
275 };
276
277 if let Some(parent) = path.parent()
278 && let Ok(false) | Err(_) = parent.try_exists()
279 {
280 return Err(txt!("Path's parent doesn't exist"));
281 }
282
283 let form = if crate::context::windows()
284 .buffers(pa)
285 .map(|handle| handle.read(pa).path())
286 .any(|p| std::path::Path::new(&p) == path)
287 {
288 form::id_of!("param.buffer.open")
289 } else if let Ok(true) = path.try_exists() {
290 form::id_of!("param.buffer.exists")
291 } else {
292 form::id_of!("param.buffer")
293 };
294
295 Ok((Self(path), Some(form)))
296 }
297}
298implDeref!(ValidFilePath, PathBuf);
299
300pub(super) enum PathOrBufferOrCfg {
306 Path(PathBuf),
307 Buffer(Handle),
308 Cfg,
309 CfgManifest,
310}
311
312impl Parameter for PathOrBufferOrCfg {
313 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
314 if args.flags.word("cfg") {
315 Ok((Self::Cfg, None))
316 } else if args.flags.word("cfg-manifest") {
317 Ok((Self::CfgManifest, None))
318 } else if let Ok((handle, form)) = args.next_as_with_form::<Handle>(pa) {
319 Ok((Self::Buffer(handle), form))
320 } else {
321 let (path, form) = args.next_as_with_form::<ValidFilePath>(pa)?;
322 Ok((Self::Path(path.0), form))
323 }
324 }
325}
326
327pub struct F32PercentOfU8(pub f32);
332
333impl Parameter for F32PercentOfU8 {
334 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
335 let arg = args.next()?;
336 if let Some(percentage) = arg.strip_suffix("%") {
337 let percentage: u8 = percentage
338 .parse()
339 .map_err(|_| txt!("[a]{arg}[] is not a valid percentage"))?;
340 if percentage <= 100 {
341 Ok((Self(percentage as f32 / 100.0), None))
342 } else {
343 Err(txt!("[a]{arg}[] is more than [a]100%"))
344 }
345 } else {
346 let byte: u8 = arg
347 .parse()
348 .map_err(|_| txt!("[a]{arg}[] couldn't be parsed"))?;
349 Ok((Self(byte as f32 / 255.0), None))
350 }
351 }
352}
353implDeref!(F32PercentOfU8, f32);
354
355impl Parameter for Color {
356 fn new(pa: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
357 const fn hue_to_rgb(p: f32, q: f32, mut t: f32) -> f32 {
358 t = if t < 0.0 { t + 1.0 } else { t };
359 t = if t > 1.0 { t - 1.0 } else { t };
360 if t < 1.0 / 6.0 {
361 p + (q - p) * 6.0 * t
362 } else if t < 1.0 / 2.0 {
363 q
364 } else if t < 2.0 / 3.0 {
365 p + (q - p) * (2.0 / 3.0 - t) * 6.0
366 } else {
367 p
368 }
369 }
370
371 let arg = args.next()?;
372 if let Some(hex) = arg.strip_prefix("#") {
374 let total = match u32::from_str_radix(hex, 16) {
375 Ok(total) if hex.len() == 6 => total,
376 _ => return Err(txt!("Hexcode does not contain 6 hex values")),
377 };
378 let r = (total >> 16) as u8;
379 let g = (total >> 8) as u8;
380 let b = total as u8;
381 Ok((Color::Rgb { r, g, b }, None))
382 } else if arg == "rgb" {
384 let r = args.next_as::<u8>(pa)?;
385 let g = args.next_as::<u8>(pa)?;
386 let b = args.next_as::<u8>(pa)?;
387 Ok((Color::Rgb { r, g, b }, None))
388 } else if arg == "hsl" {
390 let hue = args.next_as::<F32PercentOfU8>(pa)?.0;
391 let sat = args.next_as::<F32PercentOfU8>(pa)?.0;
392 let lit = args.next_as::<F32PercentOfU8>(pa)?.0;
393 let [r, g, b] = if sat == 0.0 {
394 [lit.round() as u8; 3]
395 } else {
396 let q = if lit < 0.5 {
397 lit * (1.0 + sat)
398 } else {
399 lit + sat - lit * sat
400 };
401 let p = 2.0 * lit - q;
402 let r = hue_to_rgb(p, q, hue + 1.0 / 3.0);
403 let g = hue_to_rgb(p, q, hue);
404 let b = hue_to_rgb(p, q, hue - 1.0 / 3.0);
405 [r.round() as u8, g.round() as u8, b.round() as u8]
406 };
407 Ok((Color::Rgb { r, g, b }, None))
408 } else {
409 Err(txt!("Color format was not recognized"))
410 }
411 }
412}
413
414pub struct FormName(pub String);
419
420impl Parameter for FormName {
421 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
422 let arg = args.next()?;
423 if !arg.chars().all(|c| c.is_ascii_alphanumeric() || c == '.') {
424 return Err(txt!(
425 "Expected identifiers separated by '.'s, found [a]{arg}"
426 ));
427 }
428 if crate::form::exists(arg) {
429 Ok((Self(arg.to_string()), Some(form::id_of_non_static(arg))))
430 } else {
431 Err(txt!("The form [a]{arg}[] has not been set"))
432 }
433 }
434}
435implDeref!(FormName, String);
436
437impl Parameter for Flags {
438 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
439 Ok((args.flags.clone(), None))
440 }
441}
442
443pub struct Args<'a> {
457 args: Peekable<ArgsIter<'a>>,
458 param_range: Range<usize>,
459 has_to_start_param: bool,
460 is_forming_param: bool,
461 flags: Flags,
462}
463
464impl<'a> Args<'a> {
465 #[allow(clippy::should_implement_trait)]
467 pub fn next(&mut self) -> Result<&'a str, Text> {
468 match self.args.next() {
469 Some((arg, range)) => {
470 self.param_range = range.clone();
471 if self.has_to_start_param {
472 self.has_to_start_param = false;
473 self.is_forming_param = true;
474 }
475 Ok(arg)
476 }
477 None => Err(txt!("Wrong argument count")),
478 }
479 }
480
481 pub fn next_as<P: Parameter>(&mut self, pa: &Pass) -> Result<P, Text> {
486 let initial_args = self.args.clone();
487 self.has_to_start_param = true;
488 let ret = P::new(pa, self);
489 if ret.is_ok() {
490 self.is_forming_param = false;
491 } else {
492 self.args = initial_args
493 }
494 ret.map(|(arg, _)| arg)
495 }
496
497 pub fn next_as_with_form<P: Parameter>(
502 &mut self,
503 pa: &Pass,
504 ) -> Result<(P, Option<FormId>), Text> {
505 let initial_args = self.args.clone();
506 self.has_to_start_param = true;
507 let ret = P::new(pa, self);
508 if ret.is_ok() {
509 self.is_forming_param = false;
510 } else {
511 self.args = initial_args
512 }
513 ret
514 }
515
516 pub fn next_else<T: Into<Text>>(&mut self, to_text: T) -> Result<&'a str, Text> {
518 match self.args.next() {
519 Some((arg, _)) => Ok(arg),
520 None => Err(to_text.into()),
521 }
522 }
523
524 pub fn next_start(&mut self) -> Option<usize> {
530 self.args.peek().map(|(_, r)| r.start)
531 }
532
533 pub fn param_range(&self) -> Range<usize> {
539 self.param_range.clone()
540 }
541
542 pub(super) fn clone(&self) -> Self {
544 Self {
545 args: self.args.clone(),
546 param_range: self.param_range.clone(),
547 has_to_start_param: self.has_to_start_param,
548 is_forming_param: self.is_forming_param,
549 flags: self.flags.clone(),
550 }
551 }
552}
553
554#[derive(Clone)]
559pub struct Flags {
560 blob: String,
561 word: Vec<String>,
562}
563
564impl Flags {
565 pub fn blob(&self, blob: impl AsRef<str>) -> bool {
567 let mut all_chars = true;
568 for char in blob.as_ref().chars() {
569 all_chars &= self.blob.contains(char);
570 }
571 all_chars
572 }
573
574 pub fn word(&self, flag: impl AsRef<str>) -> bool {
576 self.word.iter().any(|w| w == flag.as_ref())
577 }
578
579 pub fn is_empty(&self) -> bool {
581 self.blob.is_empty() && self.word.is_empty()
582 }
583}
584
585pub fn get_args(command: &str) -> super::Args<'_> {
590 let mut blob = String::new();
591 let mut word = Vec::new();
592
593 let args = ArgsIter::new(command);
594 let mut args = args.peekable();
595 let mut byte = 0;
596
597 while let Some((arg, range)) = args.peek() {
598 if let Some(word_arg) = arg.strip_prefix("--") {
599 if !word_arg.is_empty() {
600 args.next();
601 if !word.iter().any(|w| w == word_arg) {
602 word.push(word_arg.to_string())
603 }
604 } else {
605 args.next();
606 break;
607 }
608 } else if let Some(blob_arg) = arg.strip_prefix('-') {
609 args.next();
610 for char in blob_arg.chars() {
611 if !blob.contains(char) {
612 blob.push(char)
613 }
614 }
615 } else {
616 byte = range.start;
617 break;
618 }
619 }
620
621 super::Args {
622 args,
623 param_range: byte..byte,
624 has_to_start_param: false,
625 is_forming_param: false,
626 flags: super::Flags { blob, word },
627 }
628}
629
630#[derive(Clone)]
635pub struct ArgsIter<'a> {
636 command: &'a str,
637 chars: std::str::CharIndices<'a>,
638 start: Option<usize>,
639 end: Option<usize>,
640 is_quoting: bool,
641 last_char: char,
642}
643
644impl<'a> ArgsIter<'a> {
645 pub fn new(command: &'a str) -> Self {
647 let mut args_iter = Self {
648 command,
649 chars: command.char_indices(),
650 start: None,
651 end: None,
652 is_quoting: false,
653 last_char: 'a',
655 };
656
657 args_iter.next();
658 args_iter
659 }
660}
661
662impl<'a> Iterator for ArgsIter<'a> {
663 type Item = (&'a str, Range<usize>);
664
665 fn next(&mut self) -> Option<Self::Item> {
666 while let Some((b, char)) = self.chars.next() {
667 let lc = self.last_char;
668 self.last_char = char;
669 if self.start.is_some() && char.is_whitespace() && !self.is_quoting {
670 self.end = Some(b);
671 break;
672 } else if char == '"' && lc != '\\' {
673 self.is_quoting = !self.is_quoting;
674 if !self.is_quoting {
675 self.end = Some(b + 1);
676 break;
677 } else {
678 self.start = Some(b);
679 }
680 } else if !char.is_whitespace() && self.start.is_none() {
681 self.start = Some(b);
682 }
683 }
684
685 let e = self.end.take().unwrap_or(self.command.len());
686 self.start.take().map(|s| (&self.command[s..e], s..e))
687 }
688}
689
690macro_rules! parse_impl {
691 ($t:ty) => {
692 impl Parameter for $t {
693 fn new(_: &Pass, args: &mut Args) -> Result<(Self, Option<FormId>), Text> {
694 let arg = args.next()?;
695 let arg = arg
696 .parse()
697 .map_err(|_| txt!("[a]{arg}[] couldn't be parsed as [a]{}[]", stringify!($t)));
698 arg.map(|arg| (arg, None))
699 }
700 }
701 };
702}
703
704parse_impl!(bool);
705parse_impl!(u8);
706parse_impl!(u16);
707parse_impl!(u32);
708parse_impl!(u64);
709parse_impl!(u128);
710parse_impl!(usize);
711parse_impl!(i8);
712parse_impl!(i16);
713parse_impl!(i32);
714parse_impl!(i64);
715parse_impl!(i128);
716parse_impl!(isize);
717parse_impl!(f32);
718parse_impl!(f64);
719parse_impl!(std::path::PathBuf);