copperlace/render/
nodes.rs1use rand::distr::Distribution;
2use rand::distr::weighted::WeightedIndex;
3use rand::seq::IndexedRandom;
4
5use super::error::RenderError;
6use super::state::RenderState;
7
8pub trait TextGeneratorNode {
14 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError>;
16}
17
18impl TextGeneratorNode for String {
24 fn generate_text(&self, _state: &mut RenderState) -> Result<String, RenderError> {
25 Ok(self.clone())
26 }
27}
28
29pub struct VariableNode {
36 name: String,
37}
38
39impl VariableNode {
40 pub fn new(name: String) -> Self {
42 VariableNode { name }
43 }
44}
45
46impl TextGeneratorNode for VariableNode {
47 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
48 state
49 .context
50 .get(&self.name)
51 .cloned()
52 .ok_or_else(|| RenderError::UnknownRule(self.name.clone()))
53 }
54}
55
56pub struct RuleCallNode {
64 name: String,
65}
66
67impl RuleCallNode {
68 pub fn new(name: String) -> Self {
70 RuleCallNode { name }
71 }
72}
73
74impl TextGeneratorNode for RuleCallNode {
75 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
76 if let Some(value) = state.context.get(&self.name) {
77 return Ok(value.clone());
78 }
79
80 if let Some(value) = state
81 .ruleset
82 .render_context_default_with_state(&self.name, state)?
83 {
84 state.context.insert(self.name.clone(), value.clone());
85 return Ok(value);
86 }
87
88 state.ruleset.render_rule_with_state(&self.name, state)
89 }
90}
91
92pub enum BindMode {
95 IfMissing,
97 Overwrite,
99}
100
101pub struct BindNode {
109 name: String,
110 node: Box<dyn TextGeneratorNode>,
111 mode: BindMode,
112}
113
114impl BindNode {
115 pub fn new(name: String, node: Box<dyn TextGeneratorNode>, mode: BindMode) -> Self {
117 BindNode { name, node, mode }
118 }
119}
120
121impl TextGeneratorNode for BindNode {
122 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
123 if matches!(self.mode, BindMode::IfMissing) && state.context.contains_key(&self.name) {
124 return Ok(String::new());
125 }
126
127 let value = self.node.generate_text(state)?;
128 state.context.insert(self.name.clone(), value);
129 Ok(String::new())
130 }
131}
132
133pub struct ProcessorPipelineNode {
135 node: Box<dyn TextGeneratorNode>,
136 processors: Vec<String>,
137}
138
139impl ProcessorPipelineNode {
140 pub fn new(node: Box<dyn TextGeneratorNode>, processors: Vec<String>) -> Self {
142 ProcessorPipelineNode { node, processors }
143 }
144}
145
146impl TextGeneratorNode for ProcessorPipelineNode {
147 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
148 let mut value = self.node.generate_text(state)?;
149 for processor_name in &self.processors {
150 value = state.ruleset.process(processor_name, &value)?;
151 }
152 Ok(value)
153 }
154}
155
156pub struct ChoiceNode {
162 nodes: Vec<Box<dyn TextGeneratorNode>>,
163}
164
165impl ChoiceNode {
166 pub fn new(nodes: Vec<Box<dyn TextGeneratorNode>>) -> Self {
168 ChoiceNode { nodes }
169 }
170}
171
172impl TextGeneratorNode for ChoiceNode {
173 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
174 let random_node = self
175 .nodes
176 .choose(&mut state.rng)
177 .ok_or(RenderError::EmptyChoice)?;
178 random_node.generate_text(state)
179 }
180}
181
182pub struct WeightedChoiceNode {
188 nodes: Vec<Box<dyn TextGeneratorNode>>,
189 distribution: WeightedIndex<f64>,
190}
191
192impl WeightedChoiceNode {
193 pub fn new(entries: Vec<(Box<dyn TextGeneratorNode>, f64)>) -> Result<Self, RenderError> {
195 let (nodes, weights): (Vec<_>, Vec<_>) = entries.into_iter().unzip();
196 let distribution = WeightedIndex::new(weights)
197 .map_err(|error| RenderError::InvalidWeightedChoice(error.to_string()))?;
198 Ok(WeightedChoiceNode {
199 nodes,
200 distribution,
201 })
202 }
203}
204
205impl TextGeneratorNode for WeightedChoiceNode {
206 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
207 let index = self.distribution.sample(&mut state.rng);
208 self.nodes[index].generate_text(state)
209 }
210}
211
212pub struct VecNode {
219 nodes: Vec<Box<dyn TextGeneratorNode>>,
220}
221
222impl VecNode {
223 pub fn new(nodes: Vec<Box<dyn TextGeneratorNode>>) -> Self {
225 VecNode { nodes }
226 }
227}
228
229impl TextGeneratorNode for VecNode {
230 fn generate_text(&self, state: &mut RenderState) -> Result<String, RenderError> {
231 let mut output = String::new();
232
233 for node in &self.nodes {
234 output.push_str(&node.generate_text(state)?);
235 }
236
237 Ok(output)
238 }
239}
240
241pub struct UnsupportedValueNode {
247 value_type: String,
248}
249
250impl UnsupportedValueNode {
251 pub fn new(value_type: String) -> Self {
253 UnsupportedValueNode { value_type }
254 }
255}
256
257impl TextGeneratorNode for UnsupportedValueNode {
258 fn generate_text(&self, _state: &mut RenderState) -> Result<String, RenderError> {
259 Err(RenderError::UnsupportedValue(self.value_type.clone()))
260 }
261}