use std::fmt::Display;
use proc_macro::{TokenStream, TokenTree};
use super::{
log::log_colored,
tag_registry::{PathExpr, TagRegistry},
util::parse_string,
};
pub(crate) fn run(input: TokenStream) -> TokenStream {
let spec = LoadConfigSpec::new(input);
log_colored("Specification", &format!("{}", spec));
TagRegistry::register(
&spec.tag,
PathExpr::new(spec.kconfig_file, spec.kconfig_path),
PathExpr::new(spec.dotconfig_file, spec.dotconfig_path),
);
TokenStream::new()
}
pub(crate) struct LoadConfigSpec {
tag: String,
kconfig_path: String,
kconfig_file: String,
dotconfig_path: String,
dotconfig_file: String,
}
impl LoadConfigSpec {
fn new(input: TokenStream) -> Self {
let mut spec = Self {
tag: "default".to_owned(),
kconfig_path: "./".to_owned(),
kconfig_file: "Kconfig".to_owned(),
dotconfig_path: "./".to_owned(),
dotconfig_file: ".config".to_owned(),
};
let mut done = DoneRegistry::default();
let mut state = ConfigSpecState::Start;
let mut needs_assign = false;
let mut needs_comma = false;
let iter = input.into_iter();
for tt in iter {
match tt {
TokenTree::Ident(ident) => match state {
ConfigSpecState::Start => {
needs_assign = true;
if ident.to_string() == "tag".to_owned() {
if done.tag {
panic!("Tag argument already configured");
}
done.tag = true;
state = ConfigSpecState::Tag
} else if ident.to_string() == "kconfig_path".to_owned() {
if done.kconfig_path {
panic!("kconfig_path argument already configured");
}
done.kconfig_path = true;
state =
ConfigSpecState::Lit(Lit::KConfigPath)
} else if ident.to_string() == "kconfig_file".to_owned() {
if done.kconfig_file {
panic!("kconfig_file argument already configured");
}
done.kconfig_file = true;
state =
ConfigSpecState::Lit(Lit::KConfigFile)
} else if ident.to_string() == "dotconfig_path".to_owned() {
if done.dotconfig_path {
panic!("dotconfig_path argument already configured");
}
done.dotconfig_path = true;
state =
ConfigSpecState::Lit(Lit::DotConfigPath)
} else if ident.to_string() == "dotconfig_file".to_owned() {
if done.dotconfig_file {
panic!("dotconfig_file argument already configured");
}
done.dotconfig_file = true;
state =
ConfigSpecState::Lit(Lit::DotConfigFile)
} else {
panic!("Expected tag, kconfig_path, kconfig_file, dotconfig_path or dotconfig_file, not {}", ident)
}
}
ConfigSpecState::Tag => {
spec.tag = ident.to_string();
needs_comma = true;
state = ConfigSpecState::Start;
}
_ => {
if needs_assign {
panic!("Expected assignment")
} else {
panic!("Expected string")
}
}
},
TokenTree::Punct(punct) => match (needs_assign, needs_comma) {
(true, false) => {
if punct.to_string() == "=" {
needs_assign = false;
} else {
panic!("Expected assignment")
}
}
(false, true) => {
if punct.to_string() == "," {
needs_comma = false;
} else {
panic!("Expected comma")
}
}
(_, _) => match state {
ConfigSpecState::Start | ConfigSpecState::Tag => {
panic!("Expected identifier")
}
ConfigSpecState::Lit(_) => panic!("Expected string"),
},
},
TokenTree::Literal(lit) => match (needs_assign, needs_comma) {
(false, false) => match &state {
ConfigSpecState::Lit(v) => {
let result = parse_string(&lit.to_string());
match v {
Lit::KConfigPath => spec.kconfig_path = result,
Lit::KConfigFile => spec.kconfig_file = result,
Lit::DotConfigPath => spec.dotconfig_path = result,
Lit::DotConfigFile => spec.dotconfig_file = result,
}
state = ConfigSpecState::Start;
needs_comma = true;
}
_ => panic!("Expected arguments of kconfig_path, kconfig_file, dotconfig_path or dotconfig_file"),
},
(true, false) => {
panic!("Expected assignment")
}
(false, true) => {
panic!("Expected comma")
}
_ => {
panic!("Unexpected literal")
}
},
TokenTree::Group(_) => panic!("Unexpected grouping"),
}
}
if needs_assign {
panic!("Unexpected end of macro content");
}
spec
}
}
impl Display for LoadConfigSpec {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"tag={} Kconfig (path={}, file={}) .config (path={}, file={})",
self.tag,
self.kconfig_path,
self.kconfig_file,
self.dotconfig_path,
self.dotconfig_file
)
}
}
#[derive(Clone, Debug)]
enum Lit {
KConfigPath,
KConfigFile,
DotConfigPath,
DotConfigFile,
}
#[derive(Default)]
struct DoneRegistry {
tag: bool,
kconfig_path: bool,
kconfig_file: bool,
dotconfig_path: bool,
dotconfig_file: bool,
}
#[derive(Clone, Debug)]
enum ConfigSpecState {
Start,
Tag,
Lit(Lit),
}