trident_syn/parser/
trident_flow_executor.rs1use syn::parse::Error as ParseError;
2use syn::parse::Result as ParseResult;
3use syn::spanned::Spanned;
4use syn::Attribute;
5use syn::ItemImpl;
6use syn::Meta;
7
8use crate::types::trident_flow_executor::FlowConstraints;
9use crate::types::trident_flow_executor::FlowMethod;
10use crate::types::trident_flow_executor::TridentFlowExecutorImpl;
11
12pub fn parse_trident_flow_executor(input: &ItemImpl) -> ParseResult<TridentFlowExecutorImpl> {
13 let type_name = if let syn::Type::Path(type_path) = &*input.self_ty {
15 let mut cleaned_path = type_path.clone();
16 if let Some(last_segment) = cleaned_path.path.segments.last_mut() {
18 last_segment.arguments = syn::PathArguments::None;
19 }
20 Box::new(syn::Type::Path(cleaned_path))
21 } else {
22 input.self_ty.clone()
23 };
24 let generics = input.generics.clone();
25
26 let mut init_method = None;
27 let mut end_method = None;
28 let mut flow_methods = Vec::new();
29
30 for item in &input.items {
32 if let syn::ImplItem::Fn(method) = item {
33 if method.attrs.iter().any(|attr| attr.path().is_ident("init")) {
35 if init_method.is_some() {
36 return Err(ParseError::new(
37 method.span(),
38 "Multiple #[init] methods found. Only one is allowed.",
39 ));
40 }
41 init_method = Some(method.sig.ident.clone());
42 continue;
43 }
44
45 if method.attrs.iter().any(|attr| attr.path().is_ident("end")) {
47 if end_method.is_some() {
48 return Err(ParseError::new(
49 method.span(),
50 "Multiple #[end] methods found. Only one is allowed.",
51 ));
52 }
53 end_method = Some(method.sig.ident.clone());
54 continue;
55 }
56
57 if let Some(flow_attr) = method
59 .attrs
60 .iter()
61 .find(|attr| attr.path().is_ident("flow"))
62 {
63 let constraints = parse_flow_constraints(flow_attr)?;
64 let flow_method = FlowMethod {
65 ident: method.sig.ident.clone(),
66 constraints,
67 };
68 flow_methods.push(flow_method);
69 }
70 }
71 }
72
73 let flows_with_weights: Vec<_> = flow_methods
75 .iter()
76 .filter(|f| f.constraints.weight.is_some())
77 .collect();
78 let flows_without_weights: Vec<_> = flow_methods
79 .iter()
80 .filter(|f| f.constraints.weight.is_none())
81 .collect();
82
83 if !flows_with_weights.is_empty() && !flows_without_weights.is_empty() {
84 return Err(ParseError::new(
85 proc_macro2::Span::call_site(),
86 format!("Weight consistency error: If any flow has a weight specified, all flows must have weights. Flows without weights: {}",
87 flows_without_weights.iter().map(|f| f.ident.to_string()).collect::<Vec<_>>().join(", "))
88 ));
89 }
90
91 if !flows_with_weights.is_empty() {
93 let total_weight: u32 = flows_with_weights
94 .iter()
95 .map(|f| f.constraints.weight.unwrap())
96 .sum();
97
98 if total_weight != 100 {
99 return Err(ParseError::new(
100 proc_macro2::Span::call_site(),
101 format!("Total weight must equal exactly 100: The sum of all flow weights is {} but must be exactly 100 to represent clear percentages.", total_weight)
102 ));
103 }
104 }
105
106 Ok(TridentFlowExecutorImpl {
107 type_name,
108 impl_block: input.items.clone(),
109 flow_methods,
110 init_method,
111 end_method,
112 generics,
113 })
114}
115
116fn parse_flow_constraints(attr: &Attribute) -> ParseResult<FlowConstraints> {
117 let mut constraints = FlowConstraints::default();
118
119 match &attr.meta {
121 Meta::Path(_) => {
122 Ok(constraints)
124 }
125 Meta::List(_) => {
126 attr.parse_nested_meta(|meta| {
128 if let Some(ident) = meta.path.get_ident() {
129 match ident.to_string().as_str() {
130 "ignore" => {
131 constraints.ignore = true;
132 Ok(())
133 }
134 "weight" => {
135 if meta.input.peek(syn::Token![=]) {
136 meta.input.parse::<syn::Token![=]>()?;
137 let weight_lit: syn::LitInt = meta.input.parse()?;
138 let weight_val = weight_lit.base10_parse::<u32>()?;
139
140 if weight_val > 100 {
141 return Err(meta.error("Weight must be between 0 and 100"));
142 }
143
144 constraints.weight = Some(weight_val);
145 }
146 Ok(())
147 }
148 _ => Err(meta.error("unsupported flow constraint")),
149 }
150 } else {
151 Err(meta.error("unsupported flow constraint"))
152 }
153 })?;
154 Ok(constraints)
155 }
156 _ => Err(ParseError::new_spanned(attr, "Invalid flow attribute")),
157 }
158}