use anyhow::Result;
use std::fs;
use std::path::Path;
use syn::{Item, parse_file};
pub fn detect_appstate(root_mod_path: &Path) -> Result<Option<String>> {
if !root_mod_path.exists() {
return Ok(None);
}
let content = fs::read_to_string(root_mod_path)?;
let parsed = parse_file(&content)?;
for item in parsed.items {
if let Item::Struct(item_struct) = item {
let name = item_struct.ident.to_string();
if name.contains("State") {
return Ok(Some(name));
}
for attr in &item_struct.attrs {
if attr.path().is_ident("derive") {
if let Ok(meta) = attr.parse_args::<syn::Meta>() {
let meta_str = quote::quote!(#meta).to_string();
if meta_str.contains("Clone") && is_likely_state_name(&name) {
return Ok(Some(name));
}
}
}
}
}
else if let Item::Type(item_type) = item {
let name = item_type.ident.to_string();
if name.contains("State") {
return Ok(Some(name));
}
}
}
Ok(None)
}
fn is_likely_state_name(name: &str) -> bool {
let lowercase = name.to_lowercase();
lowercase.contains("context")|| lowercase.contains("app") || lowercase.contains("shared")
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::TempDir;
#[test]
fn test_detect_appstate() -> Result<()> {
let temp = TempDir::new()?;
let mod_path = temp.path().join("mod.rs");
let content = r#"
#[derive(Clone)]
pub struct AppState {
pub db: Database,
}
"#;
fs::write(&mod_path, content)?;
let result = detect_appstate(&mod_path)?;
assert_eq!(result, Some("AppState".to_string()));
Ok(())
}
#[test]
fn test_detect_no_state() -> Result<()> {
let temp = TempDir::new()?;
let mod_path = temp.path().join("mod.rs");
let content = r#"
pub struct User {
pub name: String,
}
"#;
fs::write(&mod_path, content)?;
let result = detect_appstate(&mod_path)?;
assert_eq!(result, None);
Ok(())
}
#[test]
fn test_detect_custom_state_name() -> Result<()> {
let temp = TempDir::new()?;
let mod_path = temp.path().join("mod.rs");
let content = r#"
#[derive(Clone)]
pub struct MyAppState {
pub config: Config,
}
"#;
fs::write(&mod_path, content)?;
let result = detect_appstate(&mod_path)?;
assert_eq!(result, Some("MyAppState".to_string()));
Ok(())
}
}