cmdmat/
lib.rs

1//! A command matching engine
2//!
3//! This library matches a list of input parameters such as:
4//! `["example", "input", "123"]`
5//! to a handler that is able to handle these inputs.
6//!
7//! The handlers are registered using the `Spec` (specification) format:
8//! ```
9//! use cmdmat::{Decider, Decision, Spec, SVec};
10//!
11//! type Accept = i32;
12//! type Deny = String;
13//! type Context = i32;
14//!
15//! fn handler(_ctx: &mut Context, _args: &[Accept]) -> Result<String, String> {
16//!     Ok("".into())
17//! }
18//!
19//! fn accept_integer(input: &[&str], out: &mut SVec<Accept>) -> Decision<Deny> {
20//!     if input.len() != 1 {
21//!         return Decision::Deny("Require exactly 1 input".into());
22//!     }
23//!     if let Ok(num) = input[0].parse::<Accept>() {
24//!         out.push(num);
25//!         Decision::Accept(1)
26//!     } else {
27//!         Decision::Deny("Unable to get number".into())
28//!     }
29//! }
30//!
31//! const DEC: Decider<Accept, Deny> = Decider {
32//!     description: "<i32>",
33//!     decider: accept_integer,
34//! };
35//!
36//! const SPEC: Spec<Accept, Deny, Context> = (&[("example", None), ("input", Some(&DEC))], handler);
37//!
38//! ```
39//!
40//! In the above the `SPEC` variable defines a path to get to the `handler`, requiring first
41//! `"example"` with no validator `None`, then followed by `"input"` which takes a single
42//! integer.
43//!
44//! If the validator `accept_integer` fails, then the command lookup will also fail.
45//!
46//! The `Spec`s will be collected inside a `Mapping`, where lookup will happen among a tree of
47//! merged `Spec`s.
48//!
49//! The reason we have a separate literal string and validator is to make it easy and unambiguous
50//! to find the next node in the search tree. If we only used validators (which can be completely
51//! arbitrary), then we can not sort a tree to make searching `O(log n)`. These fixed literal
52//! search points also give us a good way to debug commands if they happen to not match anything.
53//!
54//! Here is an example with actual lookup where we call a handler:
55//! (Unfortunately, a bit of setup is required.)
56//!
57//! ```
58//! use cmdmat::{Decider, Decision, Mapping, Spec, SVec};
59//!
60//! // The accept type is the type enum containing accepted tokens, parsed into useful formats
61//! // the list of accepted input is at last passed to the finalizer
62//! #[derive(Debug)]
63//! enum Accept {
64//!     I32(i32),
65//! }
66//!
67//! // Deny is the type returned by a decider when it denies an input (the input is invalid)
68//! type Deny = String;
69//!
70//! // The context is the type on which "finalizers" (the actual command handler) will run
71//! type Context = i32;
72//!
73//! // This is a `spec` (command specification)
74//! const SPEC: Spec<Accept, Deny, Context> = (&[("my-command-name", Some(&DEC))], print_hello);
75//!
76//! fn print_hello(_ctx: &mut Context, args: &[Accept]) -> Result<String, String> {
77//!     println!("Hello world!");
78//!     assert_eq!(1, args.len());
79//!     println!("The args I got: {:?}", args);
80//!     Ok("".into())
81//! }
82//!
83//! // This decider accepts only a single number
84//! fn decider_function(input: &[&str], out: &mut SVec<Accept>) -> Decision<Deny> {
85//!     if input.is_empty() {
86//!         return Decision::Deny("No argument provided".into());
87//!     }
88//!     let num = input[0].parse::<i32>();
89//!     if let Ok(num) = num {
90//!         out.push(Accept::I32(num));
91//!         Decision::Accept(1)
92//!     } else {
93//!         Decision::Deny("Number is not a valid i32".into())
94//!     }
95//! }
96//!
97//! const DEC: Decider<Accept, Deny> = Decider {
98//!     description: "<i32>",
99//!     decider: decider_function,
100//! };
101//!
102//! let mut mapping = Mapping::default();
103//! mapping.register(SPEC).unwrap();
104//!
105//! let handler = mapping.lookup(&["my-command-name", "123"]);
106//!
107//! match handler {
108//!     Ok((func, buf)) => {
109//!         let mut ctx = 0i32;
110//!         func(&mut ctx, &buf); // prints hello world
111//!     }
112//!     Err(look_err) => {
113//!         println!("{:?}", look_err);
114//!         assert!(false);
115//!     }
116//! }
117//! ```
118//!
119//! This library also allows partial lookups and iterating over the direct descendants in order
120//! to make autocomplete easy to implement for terminal interfaces.
121//! ```
122//! use cmdmat::{Decider, Decision, Mapping, MappingEntry, Spec, SVec};
123//!
124//! #[derive(Debug)]
125//! enum Accept {
126//!     I32(i32),
127//! }
128//! type Deny = String;
129//! type Context = i32;
130//!
131//! const SPEC: Spec<Accept, Deny, Context> =
132//!     (&[("my-command-name", Some(&DEC)), ("something", None)], print_hello);
133//!
134//! fn print_hello(_ctx: &mut Context, args: &[Accept]) -> Result<String, String> {
135//!     Ok("".into())
136//! }
137//!
138//! fn decider_function(input: &[&str], out: &mut SVec<Accept>) -> Decision<Deny> {
139//!     if input.is_empty() {
140//!         return Decision::Deny("No argument provided".into());
141//!     }
142//!     let num = input[0].parse::<i32>();
143//!     if let Ok(num) = num {
144//!         out.push(Accept::I32(num));
145//!         Decision::Accept(1)
146//!     } else {
147//!         Decision::Deny("Number is not a valid i32".into())
148//!     }
149//! }
150//!
151//! const DEC: Decider<Accept, Deny> = Decider {
152//!     description: "<i32>",
153//!     decider: decider_function,
154//! };
155//!
156//! let mut mapping = Mapping::default();
157//! mapping.register(SPEC).unwrap();
158//!
159//! // When a decider is "next-up", we get its description
160//! // We can't know in advance what the decider will consume because it is arbitrary code,
161//! // so we will have to trust its description to be valuable.
162//! let decider_desc = mapping.partial_lookup(&["my-command-name"]).unwrap().right().unwrap();
163//! assert_eq!("<i32>", decider_desc);
164//!
165//! // In this case the decider succeeded during the partial lookup, so the next step in the
166//! // tree is the "something" node.
167//! let mapping = mapping.partial_lookup(&["my-command-name", "123"]).unwrap().left().unwrap();
168//! let MappingEntry { literal, decider, finalizer, submap } = mapping.get_direct_keys().next().unwrap();
169//! assert_eq!("something", literal);
170//! assert!(decider.is_none());
171//! assert!(finalizer.is_some());
172//! ```
173#![deny(
174    missing_docs,
175    trivial_casts,
176    trivial_numeric_casts,
177    unsafe_code,
178    unused_import_braces,
179    unused_qualifications
180)]
181#![feature(test, try_trait_v2)]
182extern crate test;
183
184use smallvec::SmallVec;
185use std::{
186    collections::HashMap,
187    convert::Infallible,
188    ops::{ControlFlow, FromResidual, Try},
189};
190
191// ---
192
193/// A specific-sized small vector
194pub type SVec<A> = SmallVec<[A; 8]>;
195
196/// The command specification format
197pub type Spec<'b, 'a, A, D, C> = (
198    &'b [(&'static str, Option<&'a Decider<A, D>>)],
199    Finalizer<A, C>,
200);
201
202/// A finalizer is the function that runs to handle the entirety of the command after it has been
203/// verified by the deciders.
204pub type Finalizer<A, C> = fn(&mut C, &[A]) -> Result<String, String>;
205
206/// Finalizer with associated vector of arguments
207pub type FinWithArgs<'o, A, C> = (Finalizer<A, C>, SVec<A>);
208
209/// Either a mapping or a descriptor of a mapping
210pub type MapOrDesc<'a, 'b, A, D, C> = Either<&'b Mapping<'a, A, D, C>, &'a str>;
211
212// ---
213
214/// Either sum type
215pub enum Either<L, R> {
216    /// Left variant
217    Left(L),
218    /// Right variant
219    Right(R),
220}
221
222impl<L, R> Either<L, R> {
223    /// Convert [Either] into [Option].
224    pub fn left(self) -> Option<L> {
225        match self {
226            Either::Left(left) => Some(left),
227            Either::Right(_) => None,
228        }
229    }
230    /// Convert [Either] into [Option].
231    pub fn right(self) -> Option<R> {
232        match self {
233            Either::Left(_) => None,
234            Either::Right(right) => Some(right),
235        }
236    }
237}
238
239// ---
240
241/// A decision contains information about token consumption by the decider
242///
243/// If the decider has accepted the tokens, it will return an `Accept(usize)`, if it failed to
244/// parse interpret the tokens, it will return a deny value.
245#[derive(Debug, PartialEq)]
246pub enum Decision<D> {
247    /// Accept any number of inputs
248    Accept(usize),
249    /// Deny the input
250    Deny(D),
251}
252
253impl<R> FromResidual<Result<Infallible, R>> for Decision<R> {
254    fn from_residual(residual: Result<Infallible, R>) -> Self {
255        Decision::Deny(residual.err().unwrap())
256    }
257}
258
259impl<D> FromResidual<D> for Decision<D> {
260    fn from_residual(residual: D) -> Self {
261        Decision::Deny(residual)
262    }
263}
264
265impl<D> Try for Decision<D> {
266    type Output = usize;
267    type Residual = D;
268    fn from_output(output: Self::Output) -> Self {
269        Decision::Accept(output)
270    }
271
272    fn branch(self) -> ControlFlow<Self::Residual, Self::Output> {
273        match self {
274            Decision::Accept(value) => ControlFlow::Continue(value),
275            Decision::Deny(value) => ControlFlow::Break(value),
276        }
277    }
278}
279
280/// A decider is a function taking in a sequence of tokens and an output array
281///
282/// It puts tokens into the output array according to interal logic and returns how many elements
283/// it has consumed. If it could not process the input tokens it will return a `Deny`, containing
284/// the reason for denying.
285pub struct Decider<A, D> {
286    /// The description of the decider. Used in help and error messages.
287    pub description: &'static str,
288    /// Decider function, takes a list of inputs and writes it to the list of outputs, returns a
289    /// decision that informs the machinery how to continue, by either telling it to consume N
290    /// items or to deny the input.
291    pub decider: fn(&[&str], &mut SVec<A>) -> Decision<D>,
292}
293
294/// Errors we can get by registering specs.
295#[derive(Debug, PartialEq)]
296pub enum RegError {
297    /// Decider for this node already exists
298    DeciderAlreadyExists,
299    /// Finalizer for this node already exists
300    FinalizerAlreadyExists,
301}
302
303/// Errors happening during lookup of a command.
304#[derive(Debug, PartialEq)]
305pub enum LookError<D> {
306    /// Decider consumed more than available input
307    DeciderAdvancedTooFar,
308    /// Decider denies the input
309    DeciderDenied(String, D),
310    /// A finalizer does not exist for this entry
311    FinalizerDoesNotExist,
312    /// The mapping element does not exist
313    UnknownMapping(String),
314}
315
316/// An entry in the mapping table
317pub struct MappingEntry<'a, A, D, C> {
318    /// The literal this mapping entry is matched against
319    pub literal: &'a str,
320    /// The decider function for miscellaneous arguments
321    pub decider: Option<&'a Decider<A, D>>,
322    /// The finalizer function for this mapping
323    pub finalizer: Option<Finalizer<A, C>>,
324    /// Submap containing all other literal-mapping pairs
325    pub submap: &'a HashMap<&'a str, Mapping<'a, A, D, C>>,
326}
327
328// ---
329
330/// Node in the matching tree
331///
332/// A `Mapping` is used to interface with `cmdmat`. Each node defines a point in a command tree,
333/// containing subcommands, deciders for argument parsing, and a finalizer if this mapping can be
334/// run.
335pub struct Mapping<'a, A, D, C> {
336    map: HashMap<&'a str, Mapping<'a, A, D, C>>,
337    decider: Option<&'a Decider<A, D>>,
338    finalizer: Option<Finalizer<A, C>>,
339}
340
341impl<'a, A, D, C> Default for Mapping<'a, A, D, C> {
342    fn default() -> Self {
343        Mapping {
344            map: HashMap::new(),
345            decider: None,
346            finalizer: None,
347        }
348    }
349}
350
351impl<'a, A, D, C> Mapping<'a, A, D, C> {
352    /// Register many command specs at once, see [Mapping::register] for more detail
353    pub fn register_many<'b>(&mut self, spec: &[Spec<'b, 'a, A, D, C>]) -> Result<(), RegError> {
354        for subspec in spec {
355            self.register(subspec.clone())?;
356        }
357        Ok(())
358    }
359
360    /// Register a single command specification into the tree
361    ///
362    /// The specification will be merged with existing command specifications, and may not
363    /// overwrite commands with new deciders or finalizers. The overriding decider must be `None`
364    /// to avoid an error.
365    pub fn register<'b>(&mut self, spec: Spec<'b, 'a, A, D, C>) -> Result<(), RegError> {
366        if spec.0.is_empty() {
367            if self.finalizer.is_some() {
368                return Err(RegError::FinalizerAlreadyExists);
369            }
370            self.finalizer = Some(spec.1);
371            return Ok(());
372        }
373        let key = spec.0[0].0;
374        let decider = spec.0[0].1;
375        if let Some(ref mut entry) = self.map.get_mut(key) {
376            if decider.is_some() {
377                return Err(RegError::DeciderAlreadyExists);
378            }
379            entry.register((&spec.0[1..], spec.1))?;
380        } else {
381            let mut mapping = Mapping::<A, D, C> {
382                map: HashMap::new(),
383                decider,
384                finalizer: None,
385            };
386            mapping.register((&spec.0[1..], spec.1))?;
387            self.map.insert(key, mapping);
388        }
389        Ok(())
390    }
391
392    /// Looks up a command and runs deciders to collect all arguments
393    pub fn lookup(&self, input: &[&str]) -> Result<FinWithArgs<A, C>, LookError<D>> {
394        let mut output = SVec::<A>::new();
395        match self.lookup_internal(input, &mut output) {
396            Ok((finalizer, _advance)) => Ok((finalizer, output)),
397            Err(err) => Err(err),
398        }
399    }
400
401    /// Looks up a command and runs deciders to collect all arguments
402    fn lookup_internal(
403        &self,
404        input: &[&str],
405        output: &mut SVec<A>,
406    ) -> Result<(Finalizer<A, C>, usize), LookError<D>> {
407        if input.is_empty() {
408            if let Some(finalizer) = self.finalizer {
409                return Ok((finalizer, 0));
410            } else {
411                return Err(LookError::FinalizerDoesNotExist);
412            }
413        }
414        if let Some(handler) = self.map.get(&input[0]) {
415            let mut advance_output = 0;
416            if let Some(ref decider) = handler.decider {
417                match (decider.decider)(&input[1..], output) {
418                    Decision::Accept(byte_count) => {
419                        advance_output = byte_count;
420                    }
421                    Decision::Deny(res) => {
422                        return Err(LookError::DeciderDenied(decider.description.into(), res));
423                    }
424                }
425            }
426            if input.len() > advance_output {
427                let res = handler.lookup_internal(&input[1 + advance_output..], output);
428                if let Ok(mut res) = res {
429                    res.1 += advance_output;
430                    return Ok(res);
431                } else {
432                    return res;
433                }
434            } else {
435                return Err(LookError::DeciderAdvancedTooFar);
436            }
437        }
438        Err(LookError::UnknownMapping(input[0].to_string()))
439    }
440
441    /// Iterator over the current `Mapping` keys: containing subcommands
442    pub fn get_direct_keys(&self) -> impl Iterator<Item = MappingEntry<'_, A, D, C>> {
443        self.map.iter().map(|(k, v)| MappingEntry {
444            literal: *k,
445            decider: v.decider,
446            finalizer: v.finalizer,
447            submap: &v.map,
448        })
449    }
450
451    /// Acquire any intermediate mapping, discards parsed inputs
452    pub fn partial_lookup<'b>(
453        &'b self,
454        input: &'b [&str],
455    ) -> Result<MapOrDesc<'a, 'b, A, D, C>, LookError<D>> {
456        let mut output = SVec::<A>::new();
457        self.partial_lookup_internal(input, &mut output)
458    }
459
460    /// Perform a partial lookup, useful for autocompletion
461    ///
462    /// Due to the node structure of `Mapping`, this function returns either a `Mapping` or a
463    /// `&str` describing the active decider.
464    fn partial_lookup_internal<'b>(
465        &'b self,
466        input: &'b [&str],
467        output: &mut SVec<A>,
468    ) -> Result<MapOrDesc<'a, 'b, A, D, C>, LookError<D>> {
469        if input.is_empty() {
470            return Ok(Either::Left(self));
471        }
472        if let Some(handler) = self.map.get(&input[0]) {
473            let mut advance_output = 0;
474            if let Some(ref decider) = handler.decider {
475                if input.len() == 1 {
476                    return Ok(Either::Right(decider.description));
477                }
478                match (decider.decider)(&input[1..], output) {
479                    Decision::Accept(res) => {
480                        advance_output = res;
481                    }
482                    Decision::Deny(res) => {
483                        return Err(LookError::DeciderDenied(decider.description.into(), res));
484                    }
485                }
486            }
487            if input.len() > advance_output {
488                return handler.partial_lookup_internal(&input[1 + advance_output..], output);
489            } else {
490                return Err(LookError::DeciderAdvancedTooFar);
491            }
492        }
493        Err(LookError::UnknownMapping(input[0].to_string()))
494    }
495
496    /// Get the decider associated with this node
497    pub fn decider(&self) -> &Option<&'a Decider<A, D>> {
498        &self.decider
499    }
500
501    /// Get the finalizer associated with this node
502    pub fn finalizer(&self) -> &Option<Finalizer<A, C>> {
503        &self.finalizer
504    }
505
506    /// Iterator looping over all submappings
507    pub fn iter(&self) -> impl Iterator<Item = (&&'a str, &Mapping<'a, A, D, C>)> {
508        self.map.iter()
509    }
510}
511
512// ---
513
514#[cfg(test)]
515mod tests {
516    use super::*;
517    use test::{black_box, Bencher};
518
519    // ---
520
521    type Accept = bool;
522    type Context = u32;
523
524    fn add_one(ctx: &mut Context, _: &[Accept]) -> Result<String, String> {
525        *ctx += 1;
526        Ok("".into())
527    }
528
529    // ---
530
531    #[test]
532    fn single_mapping() {
533        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
534        mapping.register((&[("add-one", None)], add_one)).unwrap();
535        let handler = mapping.lookup(&["add-one"]).unwrap();
536        assert_eq!(0, handler.1.len());
537        let mut ctx = 123;
538        handler.0(&mut ctx, &handler.1).unwrap();
539        assert_eq!(124, ctx);
540    }
541
542    #[test]
543    fn mapping_does_not_exist() {
544        let mapping: Mapping<Accept, (), Context> = Mapping::default();
545        if let Err(err) = mapping.lookup(&["add-one"]) {
546            assert_eq!(LookError::UnknownMapping("add-one".into()), err);
547        } else {
548            assert!(false);
549        }
550    }
551
552    #[test]
553    fn overlapping_decider_fails() {
554        fn decide(_: &[&str], _: &mut SVec<Accept>) -> Decision<()> {
555            Decision::Deny(())
556        }
557
558        const DECIDE: Decider<Accept, ()> = Decider {
559            description: "",
560            decider: decide,
561        };
562
563        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
564        mapping.register((&[("add-one", None)], add_one)).unwrap();
565        assert_eq!(
566            Err(RegError::DeciderAlreadyExists),
567            mapping.register((&[("add-one", Some(&DECIDE))], add_one))
568        );
569    }
570
571    #[test]
572    fn sequences_decider_fails() {
573        fn decide(_: &[&str], _: &mut SVec<Accept>) -> Decision<()> {
574            Decision::Deny(())
575        }
576
577        const DECIDE: Decider<Accept, ()> = Decider {
578            description: "",
579            decider: decide,
580        };
581
582        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
583        mapping
584            .register((&[("add-one", Some(&DECIDE))], add_one))
585            .unwrap();
586        if let Err(err) = mapping.register((&[("add-one", None)], add_one)) {
587            assert_eq!(RegError::FinalizerAlreadyExists, err);
588        } else {
589            assert!(false);
590        }
591    }
592
593    #[test]
594    fn decider_of_one() {
595        fn decide(_: &[&str], out: &mut SVec<Accept>) -> Decision<()> {
596            out.push(true);
597            Decision::Accept(1)
598        }
599
600        const DECIDE: Decider<Accept, ()> = Decider {
601            description: "",
602            decider: decide,
603        };
604
605        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
606        mapping
607            .register((&[("add-one", Some(&DECIDE))], add_one))
608            .unwrap();
609
610        let out = mapping.lookup(&["add-one", "123"]).unwrap();
611        assert_eq!(1, out.1.len());
612        assert_eq!(true, out.1[0]);
613    }
614
615    #[test]
616    fn decider_of_two_overrun() {
617        fn decide(_: &[&str], out: &mut SVec<Accept>) -> Decision<()> {
618            out.push(true);
619            out.push(true);
620            Decision::Accept(2)
621        }
622
623        const DECIDE: Decider<Accept, ()> = Decider {
624            description: "",
625            decider: decide,
626        };
627
628        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
629        mapping
630            .register((&[("add-one", Some(&DECIDE))], add_one))
631            .unwrap();
632
633        if let Err(err) = mapping.lookup(&["add-one", "123"]) {
634            assert_eq!(LookError::DeciderAdvancedTooFar, err);
635        } else {
636            assert!(false);
637        }
638    }
639
640    #[test]
641    fn decider_of_two() {
642        fn decide(_: &[&str], out: &mut SVec<Accept>) -> Decision<()> {
643            out.push(true);
644            out.push(false);
645            Decision::Accept(2)
646        }
647
648        const DECIDE: Decider<Accept, ()> = Decider {
649            description: "",
650            decider: decide,
651        };
652
653        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
654        mapping
655            .register((&[("add-one", Some(&DECIDE))], add_one))
656            .unwrap();
657
658        let output = mapping.lookup(&["add-one", "123", "456"]).unwrap().1;
659        assert_eq!(2, output.len());
660        assert_eq!(true, output[0]);
661        assert_eq!(false, output[1]);
662    }
663
664    #[test]
665    fn decider_of_two_short_output() {
666        fn decide(_: &[&str], _: &mut SVec<Accept>) -> Decision<()> {
667            Decision::Accept(2)
668        }
669
670        const DECIDE: Decider<Accept, ()> = Decider {
671            description: "",
672            decider: decide,
673        };
674
675        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
676        mapping
677            .register((&[("add-one", Some(&DECIDE))], add_one))
678            .unwrap();
679
680        let output = mapping.lookup(&["add-one", "123", "456"]).unwrap();
681        assert_eq!(0, output.1.len());
682    }
683
684    #[test]
685    fn decider_of_many() {
686        fn decide(input: &[&str], out: &mut SVec<i32>) -> Decision<()> {
687            for i in input.iter() {
688                let number = i.parse::<i32>();
689                if let Ok(number) = number {
690                    out.push(number);
691                } else {
692                    return Decision::Deny(());
693                }
694            }
695            Decision::Accept(input.len())
696        }
697
698        const DECIDE: Decider<i32, ()> = Decider {
699            description: "",
700            decider: decide,
701        };
702
703        fn do_sum(ctx: &mut u32, out: &[i32]) -> Result<String, String> {
704            for i in out {
705                *ctx += *i as u32;
706            }
707            Ok("".into())
708        }
709        let mut mapping: Mapping<i32, (), Context> = Mapping::default();
710        mapping
711            .register((&[("sum", Some(&DECIDE))], do_sum))
712            .unwrap();
713
714        let handler = mapping.lookup(&["sum", "123", "456", "789"]).unwrap();
715        assert_eq!(3, handler.1.len());
716
717        let mut ctx = 0;
718        handler.0(&mut ctx, &handler.1).unwrap();
719        assert_eq!(1368, ctx);
720    }
721
722    #[test]
723    fn nested() {
724        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
725        mapping
726            .register((
727                &[("lorem", None), ("ipsum", None), ("dolor", None)],
728                add_one,
729            ))
730            .unwrap();
731
732        mapping.lookup(&["lorem", "ipsum", "dolor"]).unwrap();
733        if let Err(err) = mapping.lookup(&["lorem", "ipsum", "dolor", "exceed"]) {
734            assert_eq!(LookError::UnknownMapping("exceed".into()), err);
735        } else {
736            assert!(false);
737        }
738        if let Err(err) = mapping.lookup(&["lorem", "ipsum"]) {
739            assert_eq!(LookError::FinalizerDoesNotExist, err);
740        } else {
741            assert!(false);
742        }
743    }
744
745    #[test]
746    fn finalizer_at_multiple_levels() {
747        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
748        mapping
749            .register((
750                &[("lorem", None), ("ipsum", None), ("dolor", None)],
751                add_one,
752            ))
753            .unwrap();
754        mapping
755            .register((&[("lorem", None), ("ipsum", None)], add_one))
756            .unwrap();
757
758        mapping.lookup(&["lorem", "ipsum", "dolor"]).unwrap();
759        if let Err(err) = mapping.lookup(&["lorem", "ipsum", "dolor", "exceed"]) {
760            assert_eq!(LookError::UnknownMapping("exceed".into()), err);
761        } else {
762            assert!(false);
763        }
764        mapping.lookup(&["lorem", "ipsum"]).unwrap();
765    }
766
767    #[test]
768    fn partial_lookup() {
769        fn decide(_: &[&str], _: &mut SVec<Accept>) -> Decision<()> {
770            Decision::Accept(0)
771        }
772
773        const DECIDE: Decider<Accept, ()> = Decider {
774            description: "Do nothing",
775            decider: decide,
776        };
777
778        fn consume_decide(input: &[&str], _: &mut SVec<Accept>) -> Decision<()> {
779            if input.is_empty() {
780                Decision::Deny(())
781            } else {
782                Decision::Accept(1)
783            }
784        }
785
786        const CONSUME_DECIDE: Decider<Accept, ()> = Decider {
787            description: "Consume a single element, regardless of what it is",
788            decider: consume_decide,
789        };
790
791        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
792        mapping
793            .register((
794                &[("lorem", None), ("ipsum", None), ("dolor", None)],
795                add_one,
796            ))
797            .unwrap();
798        mapping
799            .register((&[("lorem", None), ("ipsum", None)], add_one))
800            .unwrap();
801        mapping
802            .register((&[("mirana", None), ("ipsum", Some(&DECIDE))], add_one))
803            .unwrap();
804        mapping
805            .register((
806                &[("consume", Some(&CONSUME_DECIDE)), ("dummy", None)],
807                add_one,
808            ))
809            .unwrap();
810
811        let part = mapping
812            .partial_lookup(&["lorem", "ipsum"])
813            .unwrap()
814            .left()
815            .unwrap();
816        let key = part.get_direct_keys().next().unwrap();
817        assert_eq!("dolor", key.literal);
818        assert!(key.decider.is_none());
819        assert!(key.finalizer.is_some());
820
821        let part = mapping.partial_lookup(&["lorem"]).unwrap().left().unwrap();
822        let key = part.get_direct_keys().next().unwrap();
823        assert_eq!("ipsum", key.literal);
824        assert!(key.decider.is_none());
825        assert!(key.finalizer.is_some());
826
827        let part = mapping.partial_lookup(&["mirana"]).unwrap().left().unwrap();
828        let key = part.get_direct_keys().next().unwrap();
829        assert_eq!("ipsum", key.literal);
830        assert!(key.decider.is_some());
831        assert_eq!("Do nothing", key.decider.unwrap().description);
832        assert!(key.finalizer.is_some());
833
834        let part = mapping
835            .partial_lookup(&["consume", "123"])
836            .unwrap()
837            .left()
838            .unwrap();
839        let key = part.get_direct_keys().next().unwrap();
840        assert_eq!("dummy", key.literal);
841        assert!(key.decider.is_none());
842        assert!(key.finalizer.is_some());
843
844        let part = mapping
845            .partial_lookup(&["consume"])
846            .unwrap()
847            .right()
848            .unwrap();
849        assert_eq!("Consume a single element, regardless of what it is", part);
850    }
851
852    // ---
853
854    #[quickcheck_macros::quickcheck]
855    fn default_contains_no_elements(strings: Vec<String>) -> bool {
856        type Accept = ();
857        type Deny = ();
858        type Context = ();
859        let strings_ref = strings.iter().map(|s| &s[..]).collect::<Vec<_>>();
860        let mapping: Mapping<Accept, Deny, Context> = Mapping::default();
861        match mapping.lookup(&strings_ref[..]) {
862            Err(LookError::UnknownMapping(string)) => string == strings[0],
863            Err(LookError::FinalizerDoesNotExist) => strings.is_empty(),
864            _ => false,
865        }
866    }
867
868    // ---
869
870    #[bench]
871    fn lookup_speed(b: &mut Bencher) {
872        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
873        mapping
874            .register((
875                &[("lorem", None), ("ipsum", None), ("dolor", None)],
876                add_one,
877            ))
878            .unwrap();
879        b.iter(|| {
880            black_box(mapping.lookup(black_box(&["lorem", "ipsum", "dolor"]))).unwrap();
881        });
882    }
883
884    #[bench]
885    fn partial_lookup_speed(b: &mut Bencher) {
886        let mut mapping: Mapping<Accept, (), Context> = Mapping::default();
887        mapping
888            .register((
889                &[("lorem", None), ("ipsum", None), ("dolor", None)],
890                add_one,
891            ))
892            .unwrap();
893        b.iter(|| {
894            mapping.partial_lookup(black_box(&["lorem"])).unwrap();
895        });
896    }
897}