use crate::ModuleConfiguration;
use facet::{Type, UserType, Variant};
use itertools::Itertools;
pub fn clean_string(raw: &[&str]) -> String {
raw.iter()
.map(|line| line.trim_start())
.map(unescape_backslash)
.join("\n")
}
fn unescape_backslash(val: &str) -> String {
let mut chars = val.chars().peekable();
let mut unescaped = String::new();
loop {
match chars.next() {
None => break,
Some(c) => {
let escaped_char = if c == '\\' {
if let Some(escaped_char) = chars.peek() {
let escaped_char = *escaped_char;
match escaped_char {
_ if escaped_char == '\\'
|| escaped_char == '\''
|| escaped_char == '`'
|| escaped_char == '$' =>
{
Some(escaped_char)
}
'n' => Some('\n'),
'r' => Some('\r'),
't' => Some('\t'),
_ => None,
}
} else {
None
}
} else {
None
};
if let Some(escaped_char) = escaped_char {
unescaped.push(escaped_char);
chars.next();
} else {
unescaped.push(c);
};
}
}
}
unescaped
}
pub struct ModuleInfo {
pub name: String,
pub doc: String,
pub configs: Vec<ModuleConfiguration>,
}
impl From<&Variant> for ModuleInfo {
fn from(module: &Variant) -> Self {
let module_name = module.name.to_lowercase();
let mut result = Self {
name: module_name,
doc: "".to_string(),
configs: Vec::new(),
};
let inner_field = module.data.fields.first().map(|m| {
let shape = m.shape();
if let Some(inner) = shape.inner {
inner()
} else {
shape
}
});
if let Some(inner_field) = inner_field
&& let Type::User(module_type) = inner_field.ty
&& let UserType::Struct(module_impl) = module_type
{
result.doc = clean_string(inner_field.doc);
result.configs = module_impl
.fields
.iter()
.map(|f| ModuleConfiguration {
name: f.name.to_lowercase(),
description: clean_string(f.doc),
})
.collect();
}
result
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_unescape_backslash() {
assert_eq!(unescape_backslash("ab\\$c"), "ab$c");
assert_eq!(unescape_backslash("ab\\\\cd\\\\"), "ab\\cd\\",);
assert_eq!(unescape_backslash("ab\\'cd\\te"), "ab'cd\te");
assert_eq!(unescape_backslash("a\\n"), "a\n");
}
}