1use alloc::format;
47use alloc::string::String;
48use alloc::vec::Vec;
49
50pub type NodeId = u32;
52pub type ModeId = u16;
54
55pub const F_NO: u8 = 1;
57pub const F_HIDDEN: u8 = 2;
59
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
62#[non_exhaustive]
63pub enum Param {
64 Num(i64, i64),
65 Ipv4,
66 Mask,
67 Prefix,
68 Mac,
69 Word,
70 Line,
71 Name(u8),
72 Ipv6,
73 Prefix6,
74}
75
76impl Param {
77 pub fn label(&self) -> String {
79 match self {
80 Param::Num(a, b) => format!("<{}-{}>", a, b),
81 Param::Ipv4 | Param::Mask => String::from("A.B.C.D"),
82 Param::Prefix => String::from("A.B.C.D/nn"),
83 Param::Mac => String::from("H.H.H"),
84 Param::Word => String::from("WORD"),
85 Param::Line => String::from("LINE"),
86 Param::Name(_) => String::from("NAME"),
87 Param::Ipv6 => String::from("X:X:X:X::X"),
88 Param::Prefix6 => String::from("X:X:X:X::X/<0-128>"),
89 }
90 }
91 fn of(s: &str) -> Option<Param> {
92 Some(match s {
93 "A.B.C.D" => Param::Ipv4,
94 "MASK" => Param::Mask,
95 "A.B.C.D/nn" => Param::Prefix,
96 "H.H.H" => Param::Mac,
97 "WORD" => Param::Word,
98 "LINE" => Param::Line,
99 "NAME" => Param::Name(0),
100 "X:X:X:X::X" => Param::Ipv6,
101 "X:X:X:X::X/<0-128>" => Param::Prefix6,
102 _ => {
103 if let Some(d) = s.strip_prefix("NAME")
104 && d.len() == 1
105 && let Some(n) = d.chars().next().and_then(|c| c.to_digit(10))
106 {
107 return Some(Param::Name(n as u8));
108 }
109 let r = s.strip_prefix('<')?.strip_suffix('>')?;
110 let cut = r.char_indices().skip(1).find(|&(_, c)| c == '-').map(|(i, _)| i)?;
112 Param::Num(r[..cut].parse().ok()?, r[cut + 1..].parse().ok()?)
113 }
114 })
115 }
116}
117
118#[derive(Clone, Debug, PartialEq, Eq)]
120#[non_exhaustive]
121pub enum Val {
122 Key(String),
124 Num(i64),
125 Ipv4([u8; 4]),
126 Mask(u8),
127 Prefix([u8; 4], u8),
128 Mac([u8; 6]),
129 Word(String),
130 Line(String),
131 Name(String),
132 Ipv6([u8; 16]),
133 Prefix6([u8; 16], u8),
134}
135
136pub trait Names {
139 fn names(&self, list: u8) -> Vec<String>;
140 fn type_help(&self, _list: u8, ty: &str) -> String {
142 format!("{} interface", ty)
143 }
144}
145
146pub struct NoNames;
148
149impl Names for NoNames {
150 fn names(&self, _list: u8) -> Vec<String> {
151 Vec::new()
152 }
153}
154
155#[derive(Clone, Debug)]
156struct Node<C> {
157 word: String,
159 param: Option<Param>,
160 key: String,
161 help: String,
162 next: Vec<NodeId>,
163 also: Option<NodeId>,
165 cmd: Option<C>,
166 no_cmd: Option<C>,
168 flags: u8,
169}
170
171#[derive(Clone, Debug)]
173pub struct Mode {
174 pub prompt: String,
176 pub title: String,
178 pub parent: Option<ModeId>,
181 root: NodeId,
182 no: Option<NodeId>,
184}
185
186#[derive(Clone, Debug, PartialEq, Eq)]
188pub struct Parsed<C> {
189 pub cmd: C,
190 pub no: bool,
192 pub vals: Vec<(String, Val)>,
194}
195
196impl<C> Parsed<C> {
197 pub fn get(&self, key: &str) -> Option<&Val> {
200 self.vals.iter().rev().find(|(k, _)| k == key).map(|(_, v)| v)
201 }
202 pub fn has(&self, key: &str) -> bool {
204 self.get(key).is_some()
205 }
206 pub fn num(&self, key: &str) -> Option<i64> {
207 match self.get(key)? {
208 Val::Num(n) => Some(*n),
209 _ => None,
210 }
211 }
212 pub fn ip(&self, key: &str) -> Option<[u8; 4]> {
213 match self.get(key)? {
214 Val::Ipv4(a) => Some(*a),
215 _ => None,
216 }
217 }
218 pub fn mask(&self, key: &str) -> Option<u8> {
220 match self.get(key)? {
221 Val::Mask(m) => Some(*m),
222 _ => None,
223 }
224 }
225 pub fn prefix(&self, key: &str) -> Option<([u8; 4], u8)> {
226 match self.get(key)? {
227 Val::Prefix(a, l) => Some((*a, *l)),
228 _ => None,
229 }
230 }
231 pub fn mac(&self, key: &str) -> Option<[u8; 6]> {
232 match self.get(key)? {
233 Val::Mac(m) => Some(*m),
234 _ => None,
235 }
236 }
237 pub fn ip6(&self, key: &str) -> Option<[u8; 16]> {
238 match self.get(key)? {
239 Val::Ipv6(a) => Some(*a),
240 _ => None,
241 }
242 }
243 pub fn prefix6(&self, key: &str) -> Option<([u8; 16], u8)> {
245 match self.get(key)? {
246 Val::Prefix6(a, l) => Some((*a, *l)),
247 _ => None,
248 }
249 }
250 pub fn text(&self, key: &str) -> Option<&str> {
252 match self.get(key)? {
253 Val::Key(s) | Val::Word(s) | Val::Line(s) | Val::Name(s) => Some(s),
254 _ => None,
255 }
256 }
257}
258
259#[derive(Clone, Copy, Debug, PartialEq, Eq)]
261#[non_exhaustive]
262pub enum Error {
263 Empty,
265 Invalid(usize),
267 Incomplete,
269 Ambiguous(usize),
271}
272
273impl Error {
274 pub fn render(&self, col: usize, line: &str) -> String {
277 match self {
278 Error::Empty => String::new(),
279 Error::Invalid(at) => {
280 let chars = line.char_indices().take_while(|&(i, _)| i < *at).count();
281 let mut s: String = core::iter::repeat_n(' ', col + chars).collect();
282 s.push_str("^\n% Invalid input detected at '^' marker.\n\n");
283 s
284 }
285 Error::Incomplete => String::from("% Incomplete command.\n\n"),
286 Error::Ambiguous(_) => format!("% Ambiguous command: \"{}\"\n", line.trim()),
287 }
288 }
289}
290
291#[derive(Clone, Debug, PartialEq, Eq)]
293#[non_exhaustive]
294pub enum Help {
295 List { title: Option<String>, items: Vec<(String, String)>, cr: bool },
298 Words(Vec<String>),
300 Unrecognized,
302 Error(Error),
303}
304
305impl Help {
306 pub fn render(&self, col: usize, line: &str) -> String {
308 match self {
309 Help::List { title, items, cr } => {
310 let mut s = String::new();
311 if let Some(t) = title {
312 s.push_str(t);
313 s.push('\n');
314 }
315 let w = items.iter().map(|(n, _)| n.chars().count()).max().unwrap_or(0) + 2;
316 for (n, d) in items {
317 if d.is_empty() {
318 s.push_str(&format!(" {}\n", n));
319 } else {
320 s.push_str(&format!(" {:<w$}{}\n", n, d, w = w));
321 }
322 }
323 if *cr {
324 s.push_str(" <cr>\n");
325 }
326 s.push('\n');
327 s
328 }
329 Help::Words(ws) => {
330 let mut s = String::new();
331 let mut col = 0;
332 for w in ws {
333 if col > 0 && col + w.len() + 2 > 79 {
334 s.push('\n');
335 col = 0;
336 }
337 s.push_str(w);
338 s.push_str(" ");
339 col += w.len() + 2;
340 }
341 s.push_str("\n\n");
342 s
343 }
344 Help::Unrecognized => String::from("% Unrecognized command\n"),
345 Help::Error(e) => e.render(col, line),
346 }
347 }
348}
349
350#[derive(Clone, Debug, Default, PartialEq, Eq)]
352pub struct Completion {
353 pub line: Option<String>,
355 pub choices: Vec<String>,
357}
358
359#[derive(Clone, Copy, Debug)]
361struct Tok<'a> {
362 s: &'a str,
363 at: usize,
364}
365
366fn tokens(line: &str) -> Vec<Tok<'_>> {
367 let mut v = Vec::new();
368 let b = line.as_bytes();
369 let mut i = 0;
370 while i < b.len() {
371 while i < b.len() && b[i].is_ascii_whitespace() {
372 i += 1;
373 }
374 let start = i;
375 while i < b.len() && !b[i].is_ascii_whitespace() {
376 i += 1;
377 }
378 if i > start {
379 v.push(Tok { s: &line[start..i], at: start });
380 }
381 }
382 v
383}
384
385fn common(a: &str, b: &str) -> usize {
387 a.bytes().zip(b.bytes()).take_while(|(x, y)| x.eq_ignore_ascii_case(y)).count()
388}
389
390fn starts_ci(word: &str, part: &str) -> bool {
391 word.len() >= part.len() && word.as_bytes()[..part.len()].eq_ignore_ascii_case(part.as_bytes())
392}
393
394pub fn parse_ipv4(s: &str) -> Option<[u8; 4]> {
396 let mut out = [0u8; 4];
397 let mut n = 0;
398 for part in s.split('.') {
399 if n == 4 || part.is_empty() || part.len() > 3 || !part.bytes().all(|c| c.is_ascii_digit()) {
400 return None;
401 }
402 let v: u32 = part.parse().ok()?;
403 if v > 255 {
404 return None;
405 }
406 out[n] = v as u8;
407 n += 1;
408 }
409 if n == 4 { Some(out) } else { None }
410}
411
412pub fn parse_mask(s: &str) -> Option<u8> {
414 let a = parse_ipv4(s)?;
415 let v = u32::from_be_bytes(a);
416 let len = v.leading_ones();
417 if v.checked_shl(len).unwrap_or(0) == 0 { Some(len as u8) } else { None }
418}
419
420pub fn parse_mac(s: &str) -> Option<[u8; 6]> {
422 let hex: String = match s.len() {
423 14 if s.as_bytes()[4] == b'.' && s.as_bytes()[9] == b'.' => s.split('.').collect(),
424 17 => {
425 let sep = s.as_bytes()[2];
426 if (sep != b':' && sep != b'-') || s.bytes().enumerate().any(|(i, c)| (i % 3 == 2) != (c == sep)) {
427 return None;
428 }
429 s.split(sep as char).collect()
430 }
431 _ => return None,
432 };
433 if hex.len() != 12 || !hex.bytes().all(|c| c.is_ascii_hexdigit()) {
434 return None;
435 }
436 let mut m = [0u8; 6];
437 for (i, o) in m.iter_mut().enumerate() {
438 *o = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).ok()?;
439 }
440 Some(m)
441}
442
443pub fn parse_ipv6(s: &str) -> Option<[u8; 16]> {
447 if s.is_empty() || s.len() > 45 {
448 return None;
449 }
450 let (head, tail, gap) = match s.find("::") {
452 Some(p) if s[p + 2..].contains("::") => return None,
453 Some(p) => (&s[..p], &s[p + 2..], true),
454 None => (s, "", false),
455 };
456 let groups = |part: &str, last: bool| -> Option<Vec<u16>> {
457 let mut v: Vec<u16> = Vec::new();
458 if part.is_empty() {
459 return Some(v);
460 }
461 let pieces: Vec<&str> = part.split(':').collect();
462 for (i, p) in pieces.iter().enumerate() {
463 if last && i + 1 == pieces.len() && p.contains('.') {
464 let a = parse_ipv4(p)?;
465 v.push(u16::from_be_bytes([a[0], a[1]]));
466 v.push(u16::from_be_bytes([a[2], a[3]]));
467 } else if p.is_empty() || p.len() > 4 || !p.bytes().all(|b| b.is_ascii_hexdigit()) {
468 return None;
469 } else {
470 v.push(u16::from_str_radix(p, 16).ok()?);
471 }
472 }
473 Some(v)
474 };
475 let front = groups(head, !gap)?;
476 let back = if gap { groups(tail, true)? } else { Vec::new() };
477 let n = front.len() + back.len();
478 if (gap && n > 7) || (!gap && n != 8) {
479 return None;
480 }
481 let mut seg = [0u16; 8];
482 seg[..front.len()].copy_from_slice(&front);
483 seg[8 - back.len()..].copy_from_slice(&back);
484 let mut out = [0u8; 16];
485 for (i, g) in seg.iter().enumerate() {
486 out[2 * i..2 * i + 2].copy_from_slice(&g.to_be_bytes());
487 }
488 Some(out)
489}
490
491fn typed_value(p: Param, s: &str) -> Option<Val> {
493 match p {
494 Param::Num(lo, hi) => {
495 if s.is_empty() || !s.bytes().all(|b| b.is_ascii_digit() || b == b'-') {
496 return None;
497 }
498 let v: i64 = s.parse().ok()?;
499 if (lo..=hi).contains(&v) { Some(Val::Num(v)) } else { None }
500 }
501 Param::Ipv4 => parse_ipv4(s).map(Val::Ipv4),
502 Param::Mask => parse_mask(s).map(Val::Mask),
503 Param::Prefix => {
504 let (a, l) = s.split_once('/')?;
505 let a = parse_ipv4(a)?;
506 if l.is_empty() || l.len() > 2 || !l.bytes().all(|b| b.is_ascii_digit()) {
507 return None;
508 }
509 let l: u8 = l.parse().ok()?;
510 if l <= 32 { Some(Val::Prefix(a, l)) } else { None }
511 }
512 Param::Mac => parse_mac(s).map(Val::Mac),
513 Param::Ipv6 => parse_ipv6(s).map(Val::Ipv6),
514 Param::Prefix6 => {
515 let (a, l) = s.split_once('/')?;
516 let a = parse_ipv6(a)?;
517 if l.is_empty() || l.len() > 3 || !l.bytes().all(|b| b.is_ascii_digit()) {
518 return None;
519 }
520 let l: u8 = l.parse().ok()?;
521 if l <= 128 { Some(Val::Prefix6(a, l)) } else { None }
522 }
523 Param::Word | Param::Line | Param::Name(_) => None,
524 }
525}
526
527fn split_name(s: &str) -> (&str, &str) {
532 let b = s.as_bytes();
533 let mut at = b.len();
534 while at > 0 && (b[at - 1].is_ascii_digit() || b"/.:*".contains(&b[at - 1])) {
535 at -= 1;
536 }
537 while at < b.len() && !(b[at].is_ascii_digit() || b[at] == b'*') {
538 at += 1;
539 }
540 (&s[..at], &s[at..])
541}
542
543fn name_fits(pattern: &str, name: &str) -> bool {
545 let (p, n) = (pattern.as_bytes(), name.as_bytes());
546 let (mut i, mut j) = (0, 0);
547 while i < p.len() {
548 if p[i] == b'*' {
549 let start = j;
550 while j < n.len() && n[j].is_ascii_digit() {
551 j += 1;
552 }
553 if j == start {
554 return false;
555 }
556 i += 1;
557 } else {
558 if j >= n.len() || !p[i].eq_ignore_ascii_case(&n[j]) {
559 return false;
560 }
561 i += 1;
562 j += 1;
563 }
564 }
565 j == n.len()
566}
567
568enum NameFit {
570 Name(String, usize),
572 NeedNumber,
574 NoType,
576 AmbiguousType,
578 BadNumber(usize),
580}
581
582fn name_types(names: &[String]) -> Vec<String> {
584 let mut v: Vec<String> = Vec::new();
585 for n in names {
586 let t = split_name(n).0;
587 if !t.is_empty() && !v.iter().any(|x| x.eq_ignore_ascii_case(t)) {
588 v.push(String::from(t));
589 }
590 }
591 v
592}
593
594fn pick_type<'a>(types: &'a [String], t: &str) -> Result<&'a str, bool> {
596 if let Some(x) = types.iter().find(|x| x.eq_ignore_ascii_case(t)) {
597 return Ok(x);
598 }
599 let m: Vec<&String> = types.iter().filter(|x| starts_ci(x, t)).collect();
600 match m.len() {
601 1 => Ok(m[0]),
602 0 => Err(false),
603 _ => Err(true),
604 }
605}
606
607fn fit_name(names: &[String], toks: &[Tok], i: usize) -> NameFit {
608 let t = toks[i];
609 let (ty, num) = split_name(t.s);
610 if ty.is_empty() {
611 return NameFit::NoType;
612 }
613 let types = name_types(names);
614 let full_ty = match pick_type(&types, ty) {
615 Ok(x) => x,
616 Err(true) => return NameFit::AmbiguousType,
617 Err(false) => return NameFit::NoType,
618 };
619 let (num, used, num_at) = if num.is_empty() {
620 match toks.get(i + 1) {
621 Some(n) if n.s.starts_with(|c: char| c.is_ascii_digit()) => (n.s, 2, n.at),
622 Some(n) => return NameFit::BadNumber(n.at),
623 None => return NameFit::NeedNumber,
624 }
625 } else {
626 (num, 1, t.at + ty.len())
627 };
628 let want = format!("{}{}", full_ty, num);
629 if let Some(n) = names.iter().find(|n| !n.contains('*') && n.eq_ignore_ascii_case(&want)) {
630 return NameFit::Name(n.clone(), used);
631 }
632 if names.iter().any(|n| n.contains('*') && name_fits(n, &want)) {
633 return NameFit::Name(want, used);
634 }
635 NameFit::BadNumber(num_at)
636}
637
638enum Pick {
640 One { node: NodeId, val: Val, used: usize },
641 None(usize),
642 Ambiguous,
643 Incomplete,
644}
645
646#[derive(Clone, Debug)]
648pub struct Tree<C> {
649 nodes: Vec<Node<C>>,
650 modes: Vec<Mode>,
651}
652
653impl<C: Copy> Default for Tree<C> {
654 fn default() -> Tree<C> {
655 Tree::new()
656 }
657}
658
659impl<C: Copy> Tree<C> {
660 pub fn new() -> Tree<C> {
661 Tree { nodes: Vec::new(), modes: Vec::new() }
662 }
663
664 fn new_node(&mut self, word: &str, param: Option<Param>, key: &str) -> NodeId {
665 self.nodes.push(Node { word: String::from(word), param, key: String::from(key), help: String::new(), next: Vec::new(), also: None, cmd: None, no_cmd: None, flags: 0 });
666 (self.nodes.len() - 1) as NodeId
667 }
668
669 pub fn mode(&mut self, prompt: &str, title: &str, parent: Option<ModeId>, no_help: Option<&str>) -> ModeId {
671 let root = self.new_node("", None, "");
672 let no = no_help.map(|h| {
673 let n = self.new_node("no", None, "no");
674 self.nodes[n as usize].help = String::from(h);
675 n
676 });
677 self.modes.push(Mode { prompt: String::from(prompt), title: String::from(title), parent, root, no });
678 (self.modes.len() - 1) as ModeId
679 }
680 pub fn mode_info(&self, m: ModeId) -> &Mode {
681 &self.modes[m as usize]
682 }
683 pub fn find_mode(&self, prompt: &str) -> Option<ModeId> {
685 self.modes.iter().position(|m| m.prompt == prompt).map(|i| i as ModeId)
686 }
687 pub fn prompt(&self, m: ModeId, host: &str) -> String {
689 format!("{}{}", host, self.modes[m as usize].prompt)
690 }
691
692 fn step(&mut self, at: NodeId, w: &str) -> NodeId {
694 let (body, key) = match w.split_once('=') {
695 Some((b, k)) => (b, k),
696 None => (w, ""),
697 };
698 let (word, param) = match Param::of(body) {
699 Some(p) => ("", Some(p)),
700 None => (body, None),
701 };
702 let key = if key.is_empty() { body.to_ascii_lowercase() } else { String::from(key) };
703 let found = self.nodes[at as usize].next.iter().copied().find(|&n| {
704 let x = &self.nodes[n as usize];
705 x.param == param && x.word.eq_ignore_ascii_case(word) && x.key == key
706 });
707 match found {
708 Some(n) => n,
709 None => {
710 let n = self.new_node(word, param, &key);
711 self.nodes[at as usize].next.push(n);
712 n
713 }
714 }
715 }
716
717 fn path(&mut self, mode: ModeId, path: &str) -> NodeId {
719 let mut at = self.modes[mode as usize].root;
720 for w in path.split_whitespace() {
721 at = self.step(at, w);
722 }
723 at
724 }
725
726 pub fn add(&mut self, mode: ModeId, path: &str, help: &str, cmd: Option<C>, flags: u8) -> NodeId {
729 let n = self.path(mode, path);
730 let node = &mut self.nodes[n as usize];
731 if !help.is_empty() {
732 node.help = String::from(help);
733 }
734 if cmd.is_some() {
735 node.cmd = cmd;
736 }
737 node.flags |= flags;
738 n
739 }
740 pub fn add_no(&mut self, mode: ModeId, path: &str, cmd: C) {
742 let n = self.path(mode, path);
743 self.nodes[n as usize].no_cmd = Some(cmd);
744 }
745 pub fn also(&mut self, mode: ModeId, path: &str, target: &str) {
748 let t = self.path(mode, target);
749 let n = self.path(mode, path);
750 if n != t {
751 self.nodes[n as usize].also = Some(t);
752 }
753 }
754
755 fn children(&self, mode: ModeId, at: NodeId) -> Vec<NodeId> {
757 let m = &self.modes[mode as usize];
758 if Some(at) == m.no {
759 return self.nodes[m.root as usize].next.iter().copied().filter(|&n| self.nodes[n as usize].flags & F_NO != 0).collect();
760 }
761 let node = &self.nodes[at as usize];
762 let mut v = node.next.clone();
763 if at == m.root
764 && let Some(no) = m.no
765 {
766 v.push(no);
767 }
768 if let Some(a) = node.also {
769 for &n in &self.nodes[a as usize].next {
770 if !v.contains(&n) {
771 v.push(n);
772 }
773 }
774 }
775 v
776 }
777
778 fn pick(&self, cands: &[NodeId], toks: &[Tok], i: usize, line: &str, names: &dyn Names) -> Pick {
780 let t = toks[i];
781 let mut exact = None;
782 let mut fits: Vec<(NodeId, Val, usize)> = Vec::new();
783 let mut loose: Vec<(NodeId, Val)> = Vec::new();
784 let mut far = t.at;
785 let mut need_number = false;
786 let mut ambiguous_type = false;
787 for &c in cands {
788 let n = &self.nodes[c as usize];
789 match n.param {
790 None => {
791 if n.word.eq_ignore_ascii_case(t.s) {
792 exact = Some(c);
793 } else if starts_ci(&n.word, t.s) {
794 fits.push((c, Val::Key(n.word.clone()), 1));
795 } else {
796 far = far.max(t.at + common(&n.word, t.s));
797 }
798 }
799 Some(p) => {
800 let v = match p {
801 Param::Num(..) | Param::Ipv4 | Param::Mask | Param::Prefix | Param::Mac | Param::Ipv6 | Param::Prefix6 => typed_value(p, t.s),
802 Param::Word => {
803 loose.push((c, Val::Word(String::from(t.s))));
804 None
805 }
806 Param::Line => {
807 loose.push((c, Val::Line(String::from(line[t.at..].trim_end()))));
808 None
809 }
810 Param::Name(list) => match fit_name(&names.names(list), toks, i) {
811 NameFit::Name(s, used) => {
812 fits.push((c, Val::Name(s), used));
813 None
814 }
815 NameFit::NeedNumber => {
816 need_number = true;
817 None
818 }
819 NameFit::NoType => None,
820 NameFit::AmbiguousType => {
821 ambiguous_type = true;
822 None
823 }
824 NameFit::BadNumber(at) => {
825 far = far.max(at);
826 None
827 }
828 },
829 };
830 if let Some(v) = v {
831 fits.push((c, v, 1));
832 }
833 }
834 }
835 }
836 if let Some(e) = exact {
837 return Pick::One { node: e, val: Val::Key(self.nodes[e as usize].word.clone()), used: 1 };
838 }
839 if fits.len() == 1 && !ambiguous_type {
840 let (node, val, used) = fits.remove(0);
841 return Pick::One { node, val, used };
842 }
843 if fits.len() > 1 || ambiguous_type {
844 return Pick::Ambiguous;
845 }
846 if need_number {
847 return Pick::Incomplete;
848 }
849 if let Some((node, val)) = loose.into_iter().next() {
850 return Pick::One { node, val, used: 1 };
851 }
852 Pick::None(far)
853 }
854
855 fn walk(&self, mode: ModeId, line: &str, toks: &[Tok], names: &dyn Names) -> Result<(NodeId, bool, Vec<(String, Val)>), Error> {
857 let m = &self.modes[mode as usize];
858 let mut at = m.root;
859 let mut no = false;
860 let mut vals = Vec::new();
861 let mut i = 0;
862 while i < toks.len() {
863 let cands = self.children(mode, at);
864 match self.pick(&cands, toks, i, line, names) {
865 Pick::One { node, val, used } => {
866 if Some(node) == m.no {
867 no = true;
868 } else {
869 vals.push((self.nodes[node as usize].key.clone(), val));
870 }
871 at = node;
872 if self.nodes[node as usize].param == Some(Param::Line) {
873 return Ok((at, no, vals));
874 }
875 i += used;
876 }
877 Pick::None(far) => return Err(Error::Invalid(far)),
878 Pick::Ambiguous => return Err(Error::Ambiguous(toks[i].at)),
879 Pick::Incomplete => return Err(Error::Incomplete),
880 }
881 }
882 Ok((at, no, vals))
883 }
884
885 pub fn parse(&self, mode: ModeId, line: &str, names: &dyn Names) -> Result<Parsed<C>, Error> {
887 let toks = tokens(line);
888 if toks.is_empty() {
889 return Err(Error::Empty);
890 }
891 let (at, no, vals) = self.walk(mode, line, &toks, names)?;
892 let n = &self.nodes[at as usize];
893 let cmd = if no { n.no_cmd.or(n.cmd) } else { n.cmd };
894 match cmd {
895 Some(cmd) => Ok(Parsed { cmd, no, vals }),
896 None => Err(Error::Incomplete),
897 }
898 }
899
900 fn listing(&self, mode: ModeId, at: NodeId, names: &dyn Names) -> Vec<(String, String)> {
902 let mut items: Vec<(String, String)> = Vec::new();
903 for c in self.children(mode, at) {
904 let n = &self.nodes[c as usize];
905 if n.flags & F_HIDDEN != 0 {
906 continue;
907 }
908 match n.param {
909 None => items.push((n.word.clone(), n.help.clone())),
910 Some(Param::Name(list)) => {
911 for ty in name_types(&names.names(list)) {
912 let h = names.type_help(list, &ty);
913 if !items.iter().any(|(l, _)| *l == ty) {
914 items.push((ty, h));
915 }
916 }
917 }
918 Some(p) => items.push((p.label(), n.help.clone())),
919 }
920 }
921 items.sort_by(|a, b| a.0.cmp(&b.0));
922 items.dedup_by(|a, b| a.0 == b.0);
923 items
924 }
925
926 fn complete_here(&self, at: NodeId, no: bool) -> bool {
927 let n = &self.nodes[at as usize];
928 n.cmd.is_some() || (no && n.no_cmd.is_some())
929 }
930
931 pub fn help(&self, mode: ModeId, line: &str, names: &dyn Names) -> Help {
934 let toks = tokens(line);
935 let partial = !line.is_empty() && !line.ends_with(|c: char| c.is_ascii_whitespace());
936 let done = if partial { &toks[..toks.len() - 1] } else { &toks[..] };
937 let (at, no, _) = match self.walk(mode, line, done, names) {
938 Ok(x) => x,
939 Err(e) => return Help::Error(e),
940 };
941 if self.nodes[at as usize].param == Some(Param::Line) && !done.is_empty() {
942 return Help::List { title: None, items: Vec::new(), cr: true };
943 }
944 if partial {
945 let part = toks[toks.len() - 1].s;
946 let mut ws: Vec<String> = Vec::new();
947 for c in self.children(mode, at) {
948 let n = &self.nodes[c as usize];
949 if n.flags & F_HIDDEN != 0 {
950 continue;
951 }
952 match n.param {
953 None if starts_ci(&n.word, part) => ws.push(n.word.clone()),
954 None => {}
955 Some(Param::Name(list)) => {
956 let (ty, num) = split_name(part);
957 if num.is_empty() {
958 for t in name_types(&names.names(list)) {
959 if starts_ci(&t, ty) {
960 ws.push(t);
961 }
962 }
963 }
964 }
965 Some(p) => {
966 if typed_value(p, part).is_some() {
967 ws.push(p.label());
968 }
969 }
970 }
971 }
972 ws.sort();
973 ws.dedup();
974 return if ws.is_empty() { Help::Unrecognized } else { Help::Words(ws) };
975 }
976 let title = if toks.is_empty() { Some(self.modes[mode as usize].title.clone()) } else { None };
977 Help::List { title, items: self.listing(mode, at, names), cr: self.complete_here(at, no) }
978 }
979
980 pub fn complete(&self, mode: ModeId, line: &str, names: &dyn Names) -> Completion {
982 let toks = tokens(line);
983 if line.is_empty() || line.ends_with(|c: char| c.is_ascii_whitespace()) || toks.is_empty() {
984 return Completion::default();
985 }
986 let last = toks[toks.len() - 1];
987 let Ok((at, _, _)) = self.walk(mode, line, &toks[..toks.len() - 1], names) else {
988 return Completion::default();
989 };
990 let mut words: Vec<String> = Vec::new();
991 let mut types: Vec<String> = Vec::new();
992 for c in self.children(mode, at) {
993 let n = &self.nodes[c as usize];
994 if n.flags & F_HIDDEN != 0 && !n.word.eq_ignore_ascii_case(last.s) {
995 continue;
996 }
997 match n.param {
998 None if starts_ci(&n.word, last.s) => words.push(n.word.clone()),
999 Some(Param::Name(list)) => {
1000 let (ty, num) = split_name(last.s);
1001 if num.is_empty() && !ty.is_empty() {
1002 for t in name_types(&names.names(list)) {
1003 if starts_ci(&t, ty) && !types.contains(&t) {
1004 types.push(t);
1005 }
1006 }
1007 }
1008 }
1009 _ => {}
1010 }
1011 }
1012 let exact = words.iter().find(|w| w.eq_ignore_ascii_case(last.s)).cloned();
1013 let mut choices: Vec<String> = words.iter().chain(types.iter()).cloned().collect();
1014 choices.sort();
1015 let head = &line[..last.at];
1016 let out = match (exact, words.len() + types.len()) {
1017 (Some(w), _) => Some(format!("{}{} ", head, w)),
1018 (None, 1) if words.len() == 1 => Some(format!("{}{} ", head, words[0])),
1019 (None, 1) => Some(format!("{}{}", head, types[0])),
1020 _ => None,
1021 };
1022 Completion { line: out, choices }
1023 }
1024
1025 pub fn knows_first(&self, mode: ModeId, line: &str) -> bool {
1029 let Some(t) = tokens(line).first().copied() else {
1030 return false;
1031 };
1032 let m = &self.modes[mode as usize];
1033 self.children(mode, m.root).iter().any(|&c| {
1034 let n = &self.nodes[c as usize];
1035 n.param.is_some() || starts_ci(&n.word, t.s)
1036 })
1037 }
1038}