use crate::parser::{Decorator, FunctionDecl, ImplBlock, Item, StructDecl, Type};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone)]
pub struct ComponentInfo {
pub name: String,
pub state_fields: Vec<StateField>,
pub methods: Vec<ComponentMethod>,
pub render_method: Option<String>, }
#[derive(Debug, Clone)]
pub struct StateField {
pub name: String,
pub field_type: Type,
pub is_reactive: bool, }
#[derive(Debug, Clone)]
pub struct ComponentMethod {
pub name: String,
pub mutates_state: bool,
pub is_event_handler: bool,
}
pub struct ComponentAnalyzer {
components: HashMap<String, ComponentInfo>,
}
impl Default for ComponentAnalyzer {
fn default() -> Self {
Self::new()
}
}
impl ComponentAnalyzer {
pub fn new() -> Self {
ComponentAnalyzer {
components: HashMap::new(),
}
}
pub fn analyze(&mut self, items: &[Item]) -> Result<(), String> {
for item in items {
if let Item::Struct {
decl: struct_decl, ..
} = item
{
if self.has_component_decorator(&struct_decl.decorators) {
self.analyze_component_struct(struct_decl)?;
}
}
}
for item in items {
if let Item::Impl {
block: impl_block, ..
} = item
{
let type_name = &impl_block.type_name;
if self.components.contains_key(type_name) {
self.analyze_component_impl(type_name, impl_block)?;
}
}
}
Ok(())
}
fn has_component_decorator(&self, decorators: &[Decorator]) -> bool {
decorators.iter().any(|d| d.name == "component")
}
fn analyze_component_struct(&mut self, struct_decl: &StructDecl) -> Result<(), String> {
let mut state_fields = Vec::new();
for field in &struct_decl.fields {
state_fields.push(StateField {
name: field.name.clone(),
field_type: field.field_type.clone(),
is_reactive: true, });
}
let component_info = ComponentInfo {
name: struct_decl.name.clone(),
state_fields,
methods: Vec::new(),
render_method: None,
};
self.components
.insert(struct_decl.name.clone(), component_info);
Ok(())
}
fn analyze_component_impl(
&mut self,
type_name: &str,
impl_block: &ImplBlock,
) -> Result<(), String> {
let state_fields = self
.components
.get(type_name)
.ok_or_else(|| format!("Component {} not found", type_name))?
.state_fields
.clone();
let component = self
.components
.get_mut(type_name)
.ok_or_else(|| format!("Component {} not found", type_name))?;
for method in &impl_block.functions {
let mutates_state = Self::method_mutates_state(method, &state_fields);
if method.name == "render" {
component.render_method = Some(method.name.clone());
}
component.methods.push(ComponentMethod {
name: method.name.clone(),
mutates_state,
is_event_handler: mutates_state && method.name != "render",
});
}
Ok(())
}
fn method_mutates_state(method: &FunctionDecl, state_fields: &[StateField]) -> bool {
let state_field_names: HashSet<_> = state_fields.iter().map(|f| f.name.as_str()).collect();
for param in &method.parameters {
if param.name == "self"
&& matches!(param.ownership, crate::parser::OwnershipHint::Owned)
{
return true;
}
}
for stmt in &method.body {
if Self::statement_mutates_fields(stmt, &state_field_names) {
return true;
}
}
false
}
fn statement_mutates_fields(
stmt: &crate::parser::Statement,
field_names: &HashSet<&str>,
) -> bool {
use crate::parser::Statement;
match stmt {
Statement::Assignment { target, .. } => {
if let crate::parser::Expression::Identifier { name, .. } = target {
return field_names.contains(name.as_str());
}
false
}
_ => false,
}
}
pub fn get_component(&self, name: &str) -> Option<&ComponentInfo> {
self.components.get(name)
}
pub fn is_component(&self, name: &str) -> bool {
self.components.contains_key(name)
}
pub fn all_components(&self) -> impl Iterator<Item = (&String, &ComponentInfo)> {
self.components.iter()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_component_analyzer_creation() {
let analyzer = ComponentAnalyzer::new();
assert_eq!(analyzer.components.len(), 0);
}
}