summaryrefslogtreecommitdiff
path: root/src/componentparameter.rs
blob: 47c9a26d8c8395da64737e9a144df01993224dda (plain)
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
		})
	}
}