Skip to main content

nom_kconfig/attribute/
range.rs

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