summaryrefslogtreecommitdiff
path: root/src/componentparameter.rs
blob: a76f243e39ca6bd541395af16f6c528ba93fed91 (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

use std::collections::HashMap;
use rand::Rng;
use serde_json::Value;
use crate::parameter::{Parameter, ParameterType};

const MAX_NESTING: usize = 3;


#[derive(Debug, PartialEq, Clone)]
pub enum ComponentParameter {
	Constant(Parameter),
	Argument(String),
	Random(Vec<ComponentParameter>)
}

impl ComponentParameter {

	pub fn evaluate(&self, arguments: &HashMap<&str, Parameter>) -> Option<Parameter> {
		self.evaluate_(arguments, 0)
	}
	
	fn evaluate_(&self, arguments: &HashMap<&str, Parameter>, 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, nesting + 1)
			}
		}
	}
	
	pub fn from_json(value: &Value) -> Result<Self, &'static str> {
		let paramvalue = value.get(1).ok_or("index 0 not in component parameter")?;
		let typename = value.get(0).ok_or("index 0 not in component parameter")?.as_str().ok_or("compparam type not a string")?;
		if let Some(paramtype) = ParameterType::from_str(typename) {
			Ok(Self::Constant(Parameter::from_typed_json(paramtype, paramvalue).ok_or("failed to parse parameter constant")?))
		} else {
			match typename {
				"A" | "arg" => {
					let argname = paramvalue.as_str().ok_or("argument parameter not a string")?.to_string();
					Ok(Self::Argument(argname))
				},
				"random" => {
					let optionvalues = paramvalue.as_array().ok_or("random argument not a a string")?;
					let mut options = Vec::new();
					for option in optionvalues {
						options.push(Self::from_json(option)?)
					}
					Ok(Self::Random(options))
				},
				_ => Err("unknown compparam type")
			}
		}
	}
	
	pub fn get_type(&self, arguments: &[(String, ParameterType, Option<Parameter>)]) -> Result<ParameterType, &'static str>{
		Ok(match self {
			Self::Constant(param) => param.paramtype(),
			Self::Argument(argname) => arguments.iter().find(|(n, _t, _d)| n == argname).ok_or("unknown argument name")?.1,
			Self::Random(options) => {
				let typ: ParameterType = options.get(0).ok_or("random has no options")?.get_type(arguments)?;
				for param in options {
					if param.get_type(arguments)? != typ {
						return Err("inconsistent parameter types");
					}
				}
				typ
			}
		})
	}
}