fcla/
range.rs

1use crate::internal::*;
2use std::ops;
3
4const START: Ident = Ident::Named("start");
5const END: Ident = Ident::Named("end");
6const BOUND: Ident = Ident::Named("bound");
7
8impl<T: FromArgs> FromArgs for ops::Range<T> {
9    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
10        Ok(args.parse(START)?..args.parse(END)?)
11    }
12}
13
14impl<T: FromArgs> FromArgs for ops::RangeFrom<T> {
15    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
16        Ok(args.parse(START)?..)
17    }
18}
19
20impl<T: FromArgs> FromArgs for ops::RangeTo<T> {
21    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
22        Ok(..args.parse(END)?)
23    }
24}
25
26impl FromArgs for ops::RangeFull {
27    fn from_args<S: Source + ?Sized>(_: &mut Args<S>) -> FromArgsResult<Self> {
28        Ok(..)
29    }
30}
31
32impl<T: FromArgs> FromArgs for ops::RangeInclusive<T> {
33    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
34        Ok(args.parse(START)?..=args.parse(END)?)
35    }
36}
37
38impl<T: FromArgs> FromArgs for ops::RangeToInclusive<T> {
39    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
40        Ok(..=args.parse(END)?)
41    }
42}
43
44impl<T: FromArgs> FromArgs for ops::Bound<T> {
45    /// - `.` &rarr; `Unbounded`
46    /// - `,` &rarr; `Excluded`
47    /// - `,=` &rarr; `Included`
48    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
49        args.branch([
50            (".", |_| Ok(Self::Unbounded)),
51            (",", |args| Ok(Self::Excluded(args.parse(BOUND)?))),
52            (",=", |args| Ok(Self::Included(args.parse(BOUND)?))),
53        ])
54    }
55}
56
57/// Dynamically bounded range.
58///
59/// This type uses a grammar similar to Rust's ranges with two key differences:
60/// 1. The range's variant is the first argument.
61/// 2. A bound's existence is communicated via fcla's [`Option`] grammar.
62///
63/// For example, the fcla equivalent of Rust's `0..10` would be `,, 0 10`.
64/// The two commas indicate that both bounds are present and the lack of a trailing `=` indicates that the end bound is exclusive.
65///
66/// The fcla equivalent of Rust's `..=10` would be `.,= 10`.
67/// The leading period indicates that the start bound is absent and the trailing `=` indicates that the end bound is inclusive.
68#[derive(Debug, Clone, PartialEq, Eq, Hash)]
69pub enum RangeDynamic<T> {
70    Range(ops::Range<T>),
71    RangeTo(ops::RangeTo<T>),
72    RangeFrom(ops::RangeFrom<T>),
73    RangeFull(ops::RangeFull),
74    RangeInclusive(ops::RangeInclusive<T>),
75    RangeToInclusive(ops::RangeToInclusive<T>),
76}
77
78impl<T: FromArgs> FromArgs for RangeDynamic<T> {
79    fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
80        args.branch([
81            (",,", |args| Ok(Self::Range(from_args(args)?))),
82            (",.", |args| Ok(Self::RangeFrom(from_args(args)?))),
83            (".,", |args| Ok(Self::RangeTo(from_args(args)?))),
84            ("..", |args| Ok(Self::RangeFull(from_args(args)?))),
85            (",,=", |args| Ok(Self::RangeInclusive(from_args(args)?))),
86            (".,=", |args| Ok(Self::RangeToInclusive(from_args(args)?))),
87        ])
88    }
89}
90
91impl<T> ops::RangeBounds<T> for RangeDynamic<T> {
92    fn start_bound(&self) -> ops::Bound<&T> {
93        match self {
94            Self::Range(range) => range.start_bound(),
95            Self::RangeTo(range) => range.start_bound(),
96            Self::RangeFrom(range) => range.start_bound(),
97            Self::RangeFull(range) => range.start_bound(),
98            Self::RangeInclusive(range) => range.start_bound(),
99            Self::RangeToInclusive(range) => range.start_bound(),
100        }
101    }
102
103    fn end_bound(&self) -> ops::Bound<&T> {
104        match self {
105            Self::Range(range) => range.end_bound(),
106            Self::RangeTo(range) => range.end_bound(),
107            Self::RangeFrom(range) => range.end_bound(),
108            Self::RangeFull(range) => range.end_bound(),
109            Self::RangeInclusive(range) => range.end_bound(),
110            Self::RangeToInclusive(range) => range.end_bound(),
111        }
112    }
113}