1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
use std::collections::HashMap;
use rand::Rng;
use serde_json::Value;
use crate::{
parameter::{Parameter, ParameterType},
Template,
Result,
aerr,
PResult,
perr
};
const MAX_NESTING: usize = 5;
#[derive(Debug, PartialEq, Clone)]
pub enum ComponentParameter {
Constant(Parameter),
Argument(String),
Random(Vec<ComponentParameter>),
Concat(Vec<ComponentParameter>),
TemplateSelf,
TemplateName
}
impl ComponentParameter {
pub fn evaluate(&self, arguments: &HashMap<&str, Parameter>, template: &Template) -> Option<Parameter> {
self.evaluate_(arguments, template, 0)
}
fn evaluate_(&self, arguments: &HashMap<&str, Parameter>, template: &Template, nesting: usize) -> Option<Parameter> {
if nesting > MAX_NESTING {
return None;
}
match self {
Self::Constant(val) => {
Some(val.clone())
}
Self::Argument(argname) => {
Some(arguments.get(argname.as_str())?.clone())
}
Self::Random(options) => {
let r = rand::thread_rng().gen_range(0, options.len());
options[r].evaluate_(arguments, template, nesting + 1)
}
Self::Concat(options) => {
let mut string = String::new();
for option in options {
if let Parameter::String(s) = option.evaluate_(arguments, template, nesting+1)? {
string.push_str(&s);
} else {
return None;
}
}
Some(Parameter::String(string))
}
Self::TemplateSelf => Some(Parameter::Template(template.clone())),
Self::TemplateName => Some(Parameter::String(template.name.0.clone())),
}
}
pub fn from_json(value: &Value) -> PResult<Self> {
if !value.is_array() {
return Ok(Self::Constant(Parameter::guess_from_json(value).ok_or(perr!("invalid component parameter {:?}", value))?));
}
let paramvalue = value.get(1).ok_or(perr!("index 1 not in component parameter"))?;
let typename = value.get(0).ok_or(perr!("index 0 not in component parameter"))?.as_str().ok_or(perr!("compparam type not a string"))?;
if let Some(paramtype) = ParameterType::from_str(typename) {
Ok(Self::Constant(Parameter::from_typed_json(paramtype, paramvalue).ok_or_else(||
perr!("failed to parse parameter constant: {:?} {:?}", paramtype, paramvalue)
)?))
} else {
match typename {
"A" | "arg" => {
let argname = paramvalue.as_str().ok_or(perr!("argument parameter not a string"))?.to_string();
Ok(Self::Argument(argname))
},
"random" => {
let optionvalues = paramvalue.as_array().ok_or(perr!("random argument not an array"))?;
let mut options = Vec::new();
for option in optionvalues {
options.push(Self::from_json(option)?)
}
Ok(Self::Random(options))
},
"concat" => {
let values = paramvalue.as_array().ok_or(perr!("concat argument not an array"))?;
let mut options = Vec::new();
for option in values {
options.push(Self::from_json(option)?)
}
Ok(Self::Concat(options))
},
"self" => Ok(Self::TemplateSelf),
"name" => Ok(Self::TemplateName),
_ => Err(perr!("unknown compparam type '{}'", typename))
}
}
}
pub fn get_type(&self, arguments: &[(String, ParameterType, Option<Parameter>)]) -> Result<ParameterType>{
Ok(match self {
Self::Constant(param) => param.paramtype(),
Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or(aerr!("unknown argument name {} in {:?}", argname, arguments))?.1,
Self::Random(options) => {
let typ: ParameterType = options.get(0).ok_or(aerr!("random has no options"))?.get_type(arguments)?;
for param in options {
if param.get_type(arguments)? != typ {
return Err(aerr!("inconsistent parameter types"));
}
}
typ
},
Self::Concat(_s) => ParameterType::String,
Self::TemplateSelf => ParameterType::Template,
Self::TemplateName => ParameterType::String
})
}
}
|