1use rand::distr::Distribution;
2use rand::distr::weighted::WeightedIndex;
3use rand::seq::IndexedRandom;
4
5use super::error::RenderError;
6use super::state::{IterationValue, LoopMetadata, RenderState};
7
8pub trait TextGeneratorNode {
14 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError>;
16
17 fn generate_unique_text(
19 &self,
20 rule_name: &str,
21 _state: &mut RenderState,
22 ) -> Result<String, RenderError> {
23 Err(RenderError::UnsupportedUniqueChoice(rule_name.to_string()))
24 }
25}
26
27impl TextGeneratorNode for String {
33 fn generate_text(&self, _state: &mut RenderState) -> Result<String, RenderError> {
34 Ok(self.clone())
35 }
36}
37
38pub struct VariableNode {
45 name: String,
46}
47
48impl VariableNode {
49 pub fn new(name: String) -> Self {
51 VariableNode { name }
52 }
53}
54
55impl TextGeneratorNode for VariableNode {
56 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
57 state
58 .resolve_bound_text(&self.name)?
59 .ok_or_else(|| RenderError::UnknownRule(self.name.clone()))
60 }
61}
62
63pub struct RuleCallNode {
71 name: String,
72 unique: bool,
73}
74
75impl RuleCallNode {
76 pub fn new(name: String) -> Self {
78 RuleCallNode {
79 name,
80 unique: false,
81 }
82 }
83
84 pub fn new_unique(name: String) -> Self {
86 RuleCallNode { name, unique: true }
87 }
88}
89
90impl TextGeneratorNode for RuleCallNode {
91 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
92 if let Some(value) = state.resolve_bound_text(&self.name)? {
93 return Ok(value);
94 }
95
96 if let Some(value) = state
97 .ruleset
98 .render_context_default_with_state(&self.name, state)?
99 {
100 state.cache_context_default(&self.name, value.clone());
101 return Ok(value);
102 }
103
104 if self.unique {
105 return state
106 .ruleset
107 .render_unique_rule_with_state(&self.name, state);
108 }
109
110 state.ruleset.render_rule_with_state(&self.name, state)
111 }
112}
113
114pub enum BindMode {
117 IfMissing,
119 Overwrite,
121}
122
123pub struct BindNode {
131 name: String,
132 node: Box<dyn TextGeneratorNode>,
133 mode: BindMode,
134}
135
136impl BindNode {
137 pub fn new(name: String, node: Box<dyn TextGeneratorNode>, mode: BindMode) -> Self {
139 BindNode { name, node, mode }
140 }
141}
142
143impl TextGeneratorNode for BindNode {
144 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
145 if matches!(self.mode, BindMode::IfMissing) && state.contains_bound_value(&self.name)? {
146 return Ok(String::new());
147 }
148 if matches!(self.mode, BindMode::Overwrite) {
149 state.ensure_mutable_binding(&self.name)?;
150 }
151
152 let value = self.node.generate_text(state)?;
153 state.bind(&self.name, value, matches!(self.mode, BindMode::Overwrite))?;
154 Ok(String::new())
155 }
156}
157
158pub(crate) trait IterationSource {
159 fn elements<'a>(&self, state: &RenderState<'a>)
160 -> Result<Vec<IterationValue<'a>>, RenderError>;
161}
162
163pub(crate) struct ArrayIterationSource {
164 path: String,
165}
166
167impl ArrayIterationSource {
168 pub(crate) fn new(path: String) -> Self {
169 ArrayIterationSource { path }
170 }
171}
172
173impl IterationSource for ArrayIterationSource {
174 fn elements<'a>(
175 &self,
176 state: &RenderState<'a>,
177 ) -> Result<Vec<IterationValue<'a>>, RenderError> {
178 state.iterable_elements(&self.path)
179 }
180}
181
182pub(crate) struct ForEachNode {
183 variable_name: String,
184 source: Box<dyn IterationSource>,
185 body: Box<dyn TextGeneratorNode>,
186}
187
188impl ForEachNode {
189 pub(crate) fn new(
190 variable_name: String,
191 source: Box<dyn IterationSource>,
192 body: Box<dyn TextGeneratorNode>,
193 ) -> Self {
194 ForEachNode {
195 variable_name,
196 source,
197 body,
198 }
199 }
200}
201
202impl TextGeneratorNode for ForEachNode {
203 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
204 let elements = self.source.elements(state)?;
205 let length = elements.len();
206 let mut output = String::new();
207
208 for (index, element) in elements.into_iter().enumerate() {
209 state.push_iteration_scope(
210 &self.variable_name,
211 element,
212 LoopMetadata { index, length },
213 );
214 let rendered = self.body.generate_text(state);
215 state.pop_iteration_scope();
216 output.push_str(&rendered?);
217 }
218
219 Ok(output)
220 }
221}
222
223pub struct ProcessorPipelineNode {
225 node: Box<dyn TextGeneratorNode>,
226 processors: Vec<String>,
227}
228
229impl ProcessorPipelineNode {
230 pub fn new(node: Box<dyn TextGeneratorNode>, processors: Vec<String>) -> Self {
232 ProcessorPipelineNode { node, processors }
233 }
234}
235
236impl TextGeneratorNode for ProcessorPipelineNode {
237 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
238 let mut value = self.node.generate_text(state)?;
239 for processor_name in &self.processors {
240 value = state.ruleset.process(processor_name, &value)?;
241 }
242 Ok(value)
243 }
244}
245
246pub struct ChoiceNode {
252 nodes: Vec<Box<dyn TextGeneratorNode>>,
253}
254
255impl ChoiceNode {
256 pub fn new(nodes: Vec<Box<dyn TextGeneratorNode>>) -> Self {
258 ChoiceNode { nodes }
259 }
260}
261
262impl TextGeneratorNode for ChoiceNode {
263 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
264 let random_node = self
265 .nodes
266 .choose(&mut state.rng)
267 .ok_or(RenderError::EmptyChoice)?;
268 random_node.generate_text(state)
269 }
270
271 fn generate_unique_text(
272 &self,
273 rule_name: &str,
274 state: &mut RenderState,
275 ) -> Result<String, RenderError> {
276 if self.nodes.is_empty() {
277 return Err(RenderError::EmptyChoice);
278 }
279
280 let used_indices = state.used_unique_choice_indices(rule_name);
281 let unused_indices = (0..self.nodes.len())
282 .filter(|index| used_indices.is_none_or(|used| !used.contains(index)))
283 .collect::<Vec<_>>();
284 let selected_index = *unused_indices
285 .choose(&mut state.rng)
286 .ok_or_else(|| RenderError::ExhaustedUniqueChoice(rule_name.to_string()))?;
287 state.mark_unique_choice_index(rule_name, selected_index);
288 self.nodes[selected_index].generate_text(state)
289 }
290}
291
292pub struct WeightedChoiceNode {
298 nodes: Vec<Box<dyn TextGeneratorNode>>,
299 weights: Vec<f64>,
300 distribution: WeightedIndex<f64>,
301}
302
303impl WeightedChoiceNode {
304 pub fn new(entries: Vec<(Box<dyn TextGeneratorNode>, f64)>) -> Result<Self, RenderError> {
306 let (nodes, weights): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
307 let distribution = WeightedIndex::new(weights.clone())
308 .map_err(|error| RenderError::InvalidWeightedChoice(error.to_string()))?;
309 Ok(WeightedChoiceNode {
310 nodes,
311 weights,
312 distribution,
313 })
314 }
315}
316
317impl TextGeneratorNode for WeightedChoiceNode {
318 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
319 let index = self.distribution.sample(&mut state.rng);
320 self.nodes[index].generate_text(state)
321 }
322
323 fn generate_unique_text(
324 &self,
325 rule_name: &str,
326 state: &mut RenderState,
327 ) -> Result<String, RenderError> {
328 if self.nodes.is_empty() {
329 return Err(RenderError::EmptyChoice);
330 }
331
332 let used_indices = state.used_unique_choice_indices(rule_name);
333 let unused_entries = self
334 .weights
335 .iter()
336 .enumerate()
337 .filter(|(index, _)| used_indices.is_none_or(|used| !used.contains(index)))
338 .map(|(index, weight)| (index, *weight))
339 .collect::<Vec<_>>();
340 if unused_entries.is_empty() {
341 return Err(RenderError::ExhaustedUniqueChoice(rule_name.to_string()));
342 }
343
344 let remaining_weights = unused_entries
345 .iter()
346 .map(|(_, weight)| *weight)
347 .collect::<Vec<_>>();
348 let distribution = WeightedIndex::new(remaining_weights)
349 .map_err(|_| RenderError::ExhaustedUniqueChoice(rule_name.to_string()))?;
350 let selected_entry_index = distribution.sample(&mut state.rng);
351 let selected_node_index = unused_entries[selected_entry_index].0;
352 state.mark_unique_choice_index(rule_name, selected_node_index);
353 self.nodes[selected_node_index].generate_text(state)
354 }
355}
356
357pub struct VecNode {
364 nodes: Vec<Box<dyn TextGeneratorNode>>,
365}
366
367impl VecNode {
368 pub fn new(nodes: Vec<Box<dyn TextGeneratorNode>>) -> Self {
370 VecNode { nodes }
371 }
372}
373
374impl TextGeneratorNode for VecNode {
375 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
376 let mut output = String::new();
377
378 for node in &self.nodes {
379 output.push_str(&node.generate_text(state)?);
380 }
381
382 Ok(output)
383 }
384}
385
386pub struct UnsupportedValueNode {
392 value_type: String,
393}
394
395impl UnsupportedValueNode {
396 pub fn new(value_type: String) -> Self {
398 UnsupportedValueNode { value_type }
399 }
400}
401
402impl TextGeneratorNode for UnsupportedValueNode {
403 fn generate_text(&self, _state: &mut RenderState) -> Result<String, RenderError> {
404 Err(RenderError::UnsupportedValue(self.value_type.clone()))
405 }
406}