Skip to main content

nom_kconfig/attribute/
range.rs

1use nom::{
2    branch::alt,
3    bytes::complete::tag,
4    character::{complete::alphanumeric1, one_of},
5    combinator::{map, recognize},
6    multi::many1,
7    sequence::{delimited, pair, preceded},
8    IResult, Parser,
9};
10#[cfg(feature = "deserialize")]
11use serde::Deserialize;
12#[cfg(feature = "serialize")]
13use serde::Serialize;
14#[cfg(feature = "display")]
15use std::fmt::Display;
16
17use super::expression::{parse_if_attribute, Expression};
18use crate::{
19    number::parse_number,
20    symbol::{parse_constant_hex_as_string, parse_non_constant_symbol},
21};
22use crate::{util::ws, KconfigInput};
23
24/// This attribute allows to limit the range of possible input values for int and hex symbols. The user can only input a value which is larger than or equal to the first symbol and smaller than or equal to the second symbol.
25#[derive(Debug, Clone, PartialEq)]
26#[cfg_attr(feature = "hash", derive(Hash))]
27#[cfg_attr(feature = "serialize", derive(Serialize))]
28#[cfg_attr(feature = "deserialize", derive(Deserialize))]
29pub struct Range {
30    pub lower_bound: RangeBound,
31    pub upper_bound: RangeBound,
32    #[cfg_attr(
33        any(feature = "serialize", feature = "deserialize"),
34        serde(skip_serializing_if = "Option::is_none")
35    )]
36    pub r#if: Option<Expression>,
37}
38
39#[cfg(feature = "display")]
40impl Display for Range {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match &self.r#if {
43            Some(i) => write!(f, "{} {} if {}", self.lower_bound, self.upper_bound, i),
44            None => write!(f, "{} {}", self.lower_bound, self.upper_bound),
45        }
46    }
47}
48
49// TODO bounds can be numbers or hex, here we only accept numbers...
50fn parse_bounds(input: KconfigInput) -> IResult<KconfigInput, (RangeBound, RangeBound)> {
51    (ws(parse_range_bound), ws(parse_range_bound)).parse(input)
52}
53
54/// Parses the `range` attribute.
55/// # Example
56/// ```
57/// use nom_kconfig::{
58///     assert_parsing_eq,
59///     attribute::{parse_range, Range, range::RangeBound}
60/// };
61///
62/// assert_parsing_eq!(
63///     parse_range,
64///     "range 1 5",
65///     Ok((
66///         "",
67///         Range {
68///             lower_bound: RangeBound::Number(1),
69///             upper_bound: RangeBound::Number(5),
70///             r#if: None
71///         }
72///     ))
73/// )
74/// ```
75pub fn parse_range(input: KconfigInput) -> IResult<KconfigInput, Range> {
76    map(
77        preceded(ws(tag("range")), pair(ws(parse_bounds), parse_if_attribute)),
78        |((l, r), i)| Range {
79            lower_bound: l,
80            upper_bound: r,
81            r#if: i,
82        },
83    )
84    .parse(input)
85}
86
87#[derive(Debug, Clone, PartialEq)]
88#[cfg_attr(feature = "hash", derive(Hash))]
89#[cfg_attr(feature = "serialize", derive(Serialize))]
90#[cfg_attr(feature = "deserialize", derive(Deserialize))]
91pub enum RangeBound {
92    Hex(String),
93    Number(i64),
94    Symbol(String),
95    Variable(String),
96}
97
98fn parse_range_bound(input: KconfigInput) -> IResult<KconfigInput, RangeBound> {
99    alt((
100        map(parse_constant_hex_as_string, RangeBound::Hex),
101        map(parse_number, RangeBound::Number),
102        map(parse_non_constant_symbol, |s| {
103            RangeBound::Symbol(s.to_string())
104        }),
105        map(parse_variable, |v| RangeBound::Variable(v.to_string())),
106    ))
107    .parse(input)
108}
109
110#[cfg(feature = "display")]
111impl Display for RangeBound {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        match self {
114            RangeBound::Hex(h) => write!(f, "{}", h),
115            RangeBound::Number(n) => write!(f, "{}", n),
116            RangeBound::Symbol(s) => write!(f, "{}", s),
117            RangeBound::Variable(v) => write!(f, "$({})", v),
118        }
119    }
120}
121
122fn parse_variable(input: KconfigInput) -> IResult<KconfigInput, String> {
123    map(
124        delimited(
125            tag("$("),
126            recognize(ws(many1(alt((alphanumeric1, recognize(one_of("_"))))))),
127            tag(")"),
128        ),
129        |d: KconfigInput| d.fragment().to_string(),
130    )
131    .parse(input)
132}