use crate::ts_syn::abi::DecoratorIR;
#[derive(Default, Clone)]
pub struct CompareFieldOptions {
pub skip: bool,
}
impl CompareFieldOptions {
pub fn from_decorators(decorators: &[DecoratorIR], attr_name: &str) -> Self {
let mut opts = Self::default();
for decorator in decorators {
if !decorator.name.eq_ignore_ascii_case(attr_name) {
continue;
}
let args = decorator.args_src.trim();
if has_flag(args, "skip") {
opts.skip = true;
}
}
opts
}
}
#[derive(Default, Clone)]
pub struct DefaultFieldOptions {
pub value: Option<String>,
pub has_default: bool,
}
impl DefaultFieldOptions {
pub fn from_decorators(decorators: &[DecoratorIR]) -> Self {
let mut opts = Self::default();
for decorator in decorators {
if !decorator.name.eq_ignore_ascii_case("default") {
continue;
}
opts.has_default = true;
let args = decorator.args_src.trim();
if let Some(value) = extract_default_value(args) {
opts.value = Some(value);
} else if !args.is_empty() {
opts.value = Some(args.to_string());
}
}
opts
}
}
fn extract_default_value(args: &str) -> Option<String> {
if let Some(value) = extract_named_string(args, "value") {
return Some(value);
}
if let Some(value) = parse_string_literal(args) {
return Some(format!("\"{}\"", value));
}
None
}
pub fn has_flag(args: &str, flag: &str) -> bool {
if flag_explicit_false(args, flag) {
return false;
}
args.split(|c: char| !c.is_alphanumeric() && c != '_')
.any(|token| token.eq_ignore_ascii_case(flag))
}
fn flag_explicit_false(args: &str, flag: &str) -> bool {
let lower = args.to_ascii_lowercase();
let condensed: String = lower.chars().filter(|c| !c.is_whitespace()).collect();
condensed.contains(&format!("{flag}:false")) || condensed.contains(&format!("{flag}=false"))
}
pub fn extract_named_string(args: &str, name: &str) -> Option<String> {
let lower = args.to_ascii_lowercase();
let idx = lower.find(name)?;
let remainder = &args[idx + name.len()..];
let remainder = remainder.trim_start();
if remainder.starts_with(':') || remainder.starts_with('=') {
let value = remainder[1..].trim_start();
return parse_string_literal(value);
}
if remainder.starts_with('(')
&& let Some(close) = remainder.rfind(')')
{
let inner = remainder[1..close].trim();
return parse_string_literal(inner);
}
None
}
pub(crate) fn parse_string_literal(input: &str) -> Option<String> {
let trimmed = input.trim();
let mut chars = trimmed.chars();
let quote = chars.next()?;
if quote != '"' && quote != '\'' {
return None;
}
let mut escaped = false;
let mut buf = String::new();
for c in chars {
if escaped {
buf.push(c);
escaped = false;
continue;
}
if c == '\\' {
escaped = true;
continue;
}
if c == quote {
return Some(buf);
}
buf.push(c);
}
None
}