nom_kconfig/entry/
source.rs

1use std::path::PathBuf;
2
3use nom::{
4    branch::alt,
5    bytes::complete::tag,
6    character::complete::{alphanumeric1, one_of},
7    combinator::{cut, map, recognize},
8    error::{Error, ErrorKind, ParseError},
9    multi::many1,
10    sequence::delimited,
11    IResult, Parser,
12};
13use regex::Regex;
14
15use crate::{
16    kconfig::{parse_kconfig, Kconfig},
17    util::{ws, wsi},
18    KconfigFile, KconfigInput,
19};
20
21pub fn parse_filepath(input: KconfigInput<'_>) -> IResult<KconfigInput<'_>, &str> {
22    map(
23        recognize(ws(many1(alt((
24            alphanumeric1::<KconfigInput, _>,
25            recognize(one_of(".$()-_$/")),
26        ))))),
27        |d| d.fragment().to_owned(),
28    )
29    .parse(input)
30}
31
32pub fn parse_source(input: KconfigInput) -> IResult<KconfigInput, Source> {
33    let (input, _) = ws(tag("source")).parse(input)?;
34    let (input, file) = wsi(alt((
35        delimited(tag("\""), parse_filepath, tag("\"")),
36        parse_filepath,
37    )))
38    .parse(input)?;
39    if let Some(file) = apply_vars(file, &input.extra.vars) {
40        let source_kconfig_file = KconfigFile::new_with_vars(
41            input.clone().extra.root_dir,
42            PathBuf::from(file),
43            &input.extra.vars,
44        );
45        let source_content = source_kconfig_file
46            .read_to_string()
47            .map_err(|_| nom::Err::Error(Error::from_error_kind(input.clone(), ErrorKind::Fail)))?;
48
49        let binding = source_content.clone();
50        #[allow(clippy::let_and_return)]
51        let x = match cut(parse_kconfig).parse(KconfigInput::new_extra(
52            &binding,
53            source_kconfig_file.clone(),
54        )) {
55            Ok((_, kconfig)) => Ok((input, kconfig)),
56            Err(_e) => Err(nom::Err::Error(nom::error::Error::new(
57                KconfigInput::new_extra("", source_kconfig_file),
58                ErrorKind::Fail,
59            ))),
60        };
61        x
62    } else {
63        Ok((
64            input,
65            Source {
66                file: file.to_string(),
67                ..Default::default()
68            },
69        ))
70    }
71}
72
73pub fn apply_vars(
74    file: &str,
75    extra_vars: &std::collections::HashMap<String, String>,
76) -> Option<String> {
77    let re = Regex::new(r"\$\((\S+)\)").unwrap();
78    let mut file_copy = String::from(file);
79    for (var_name, var_value) in re.captures_iter(file).map(|cap| {
80        let ex: (&str, [&str; 1]) = cap.extract();
81        let var = ex.1[0];
82        (var, extra_vars.get(var))
83    }) {
84        if let Some(var_value) = var_value {
85            file_copy = file_copy.replace(&format!("$({var_name})"), var_value);
86        } else {
87            return None;
88        }
89    }
90    Some(file_copy)
91}
92
93/// Entry that reads the specified configuration file. This file is always parsed.
94pub type Source = Kconfig;