1use std::collections::HashMap;
2
3use serde::Deserialize;
4
5use crate::{
6 op_acc_add, op_acc_mul, op_add_prefix, op_add_suffix, op_nop, op_octal_add,
7 op_octal_multiply, op_octet_add, op_octet_encode, DispatchError, DispatchResult, OpArg,
8 OperationTable,
9};
10
11#[derive(Debug, Deserialize)]
13pub struct OperationConfig {
14 pub operations: HashMap<String, OperationDefinition>,
16}
17
18#[derive(Debug, Deserialize)]
20pub struct OperationDefinition {
21 #[serde(rename = "type")]
23 pub op_type: String,
24 pub prefix: Option<String>,
26 pub suffix: Option<String>,
28 pub multiplier: Option<i64>,
30 pub value: Option<i64>,
32}
33
34pub fn parse_selector(text: &str) -> DispatchResult<u8> {
36 let trimmed = text.trim();
37 let parsed = if let Some(hex) = trimmed.strip_prefix("0x").or_else(|| trimmed.strip_prefix("0X")) {
38 u8::from_str_radix(hex, 16)
39 } else {
40 trimmed.parse::<u8>()
41 };
42 parsed.map_err(|_| DispatchError::InvalidInput(format!("bad selector `{text}`")))
43}
44
45pub fn table_from_toml(text: &str) -> DispatchResult<OperationTable> {
47 let config: OperationConfig = toml::from_str(text).map_err(|e| DispatchError::InvalidInput(e.to_string()))?;
48 table_from_config(config)
49}
50
51pub fn table_from_config(config: OperationConfig) -> DispatchResult<OperationTable> {
53 let mut table = OperationTable::new();
54 for (selector_text, op) in config.operations {
55 let selector = parse_selector(&selector_text)?;
56 let name = op.op_type.clone();
57 match op.op_type.as_str() {
58 "nop" => table.register(selector, name, op_nop, OpArg::default()),
59 "add_prefix" => {
60 let prefix = op.prefix.ok_or_else(|| DispatchError::MissingParam { op: name.clone(), param: "prefix" })?;
61 table.register(selector, name, op_add_prefix, OpArg::text(prefix));
62 }
63 "add_suffix" => {
64 let suffix = op.suffix.ok_or_else(|| DispatchError::MissingParam { op: name.clone(), param: "suffix" })?;
65 table.register(selector, name, op_add_suffix, OpArg::text(suffix));
66 }
67 "octal_multiply" => {
68 let n = op.multiplier.ok_or_else(|| DispatchError::MissingParam { op: name.clone(), param: "multiplier" })?;
69 table.register(selector, name, op_octal_multiply, OpArg::n(n));
70 }
71 "octal_add" => {
72 let n = op.value.ok_or_else(|| DispatchError::MissingParam { op: name.clone(), param: "value" })?;
73 table.register(selector, name, op_octal_add, OpArg::n(n));
74 }
75 "octet_encode" => table.register(selector, name, op_octet_encode, OpArg::default()),
76 "octet_add" => {
77 let n = op.value.ok_or_else(|| DispatchError::MissingParam { op: name.clone(), param: "value" })?;
78 table.register(selector, name, op_octet_add, OpArg::n(n));
79 }
80 "acc_add" => table.register(selector, name, op_acc_add, OpArg::n(op.value.unwrap_or(1))),
81 "acc_mul" => table.register(selector, name, op_acc_mul, OpArg::n(op.multiplier.unwrap_or(2))),
82 other => return Err(DispatchError::UnknownOperationType(other.to_string())),
83 }
84 }
85 Ok(table)
86}