1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use crate::Attribute;
use crate::{util::ws, KconfigInput};
use nom::sequence::pair;
use nom::{
    branch::alt,
    bytes::complete::tag,
    combinator::{map, opt},
    sequence::preceded,
    IResult,
};
#[cfg(feature = "deserialize")]
use serde::Deserialize;
#[cfg(feature = "serialize")]
use serde::Serialize;
#[cfg(feature = "display")]
use std::fmt::Display;

use super::{parse_expression, parse_if_attribute, parse_prompt_value, Expression};

pub fn parse_type(input: KconfigInput) -> IResult<KconfigInput, Attribute> {
    map(
        pair(
            ws(alt((
                map(
                    preceded(tag("boolean"), opt(parse_prompt_value)),
                    Type::Bool,
                ),
                map(preceded(tag("bool"), opt(parse_prompt_value)), Type::Bool),
                map(preceded(tag("hex"), opt(parse_prompt_value)), Type::Hex),
                map(preceded(tag("int"), opt(parse_prompt_value)), Type::Int),
                map(
                    preceded(tag("string"), opt(parse_prompt_value)),
                    Type::String,
                ),
                map(
                    preceded(tag("tristate"), opt(parse_prompt_value)),
                    Type::Tristate,
                ),
                map(preceded(tag("def_bool"), ws(parse_expression)), |e| {
                    Type::DefBool(e)
                }),
                map(preceded(tag("def_tristate"), ws(parse_expression)), |e| {
                    Type::DefTristate(e)
                }),
            ))),
            parse_if_attribute,
        ),
        |(t, i)| Attribute::Type(ConfigType { r#type: t, r#if: i }),
    )(input)
}

#[derive(Debug, PartialEq, Clone)]
#[cfg_attr(feature = "hash", derive(Hash))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
#[cfg_attr(
    any(feature = "serialize", feature = "deserialize"),
    serde(rename_all = "lowercase")
)]
pub enum Type {
    DefBool(Expression),
    DefTristate(Expression),
    Bool(Option<String>),
    Tristate(Option<String>),
    String(Option<String>),
    Hex(Option<String>),
    Int(Option<String>),
}

/// Every config option must have a type. There are only two basic types: tristate and string; the other types are based on these two. The type definition optionally accepts an input prompt.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "hash", derive(Hash))]
#[cfg_attr(feature = "serialize", derive(Serialize))]
#[cfg_attr(feature = "deserialize", derive(Deserialize))]
pub struct ConfigType {
    pub r#type: Type,
    #[cfg_attr(
        any(feature = "serialize", feature = "deserialize"),
        serde(skip_serializing_if = "Option::is_none")
    )]
    pub r#if: Option<Expression>,
}

#[cfg(feature = "display")]
impl Display for ConfigType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match &self.r#if {
            Some(i) => write!(f, "{} if {}", self.r#type, i),
            None => write!(f, "{}", self.r#type),
        }
    }
}

#[cfg(feature = "display")]
impl Display for Type {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            Type::Bool(prompt) => fmt_type(f, "bool", prompt),
            Type::Tristate(prompt) => fmt_type(f, "tristate", prompt),
            Type::String(prompt) => fmt_type(f, "string", prompt),
            Type::Hex(prompt) => fmt_type(f, "hex", prompt),
            Type::Int(prompt) => fmt_type(f, "int", prompt),
            Type::DefBool(v) => write!(f, "def_bool {}", v),
            Type::DefTristate(v) => write!(f, "def_tristate {}", v),
        }
    }
}

fn fmt_type(
    f: &mut std::fmt::Formatter,
    keyword: &str,
    prompt: &Option<String>,
) -> std::fmt::Result {
    match prompt {
        Some(p) => write!(f, "{} \"{}\"", keyword, p),
        None => write!(f, "{}", keyword),
    }
}