use ring::digest::{Context, SHA256};
use serde::Deserialize;
use std::collections::BTreeMap;
use crate::result::{diagnostic::Location, CompilerResult};
#[derive(Debug, PartialEq, Deserialize)]
pub struct DriversFile {
pub drivers: Drivers,
pub hash: Vec<u8>,
}
#[derive(Debug, PartialEq, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Drivers {
pub version: String,
pub sensor: Vec<VirtualSensorDriver>,
pub relay: Vec<VirtualRelayDriver>,
}
#[derive(Debug, PartialEq, Deserialize)]
pub struct VirtualSensorDriver {
pub namespace: String,
pub function: String,
pub fields: Option<Vec<BTreeMap<String, String>>>,
}
#[derive(Debug, PartialEq, Deserialize)]
pub struct VirtualRelayDriver {
pub namespace: String,
pub function: String,
pub fields: Option<Vec<BTreeMap<String, String>>>,
}
impl Drivers {
pub fn parse(file_id: usize, file_name: String, contents: &str) -> CompilerResult<DriversFile> {
let mut res = CompilerResult::new("Parsing a drivers file");
let taplo_parse = taplo::parser::parse(contents);
let taplo_dom = taplo_parse.clone().into_dom();
if !taplo_parse.errors.is_empty() {
for error in taplo_parse.errors {
let location = Location::from_raw(
file_id,
error.range.start().into(),
error.range.end().into(),
);
res.error((location, error.to_string()));
}
}
if !taplo_dom.errors().is_empty() {
for error in taplo_dom.errors() {
res.error(format!("{}\n\t{}", error.to_string(), file_name));
}
}
let drivers: Drivers = check!(res, toml::from_str(contents));
let mut context = Context::new(&SHA256);
context.update(contents.as_bytes());
let hash = context.finish().as_ref().to_vec();
let drivers_file = DriversFile {
drivers,
hash: hash,
};
res.with_value(drivers_file)
}
pub fn get_sensor_driver_index(&self, driver_name: &str) -> Option<u16> {
for (idx, sensor_driver) in self.sensor.iter().enumerate() {
let sensor_driver_name =
sensor_driver.namespace.clone() + "/" + sensor_driver.function.clone().as_str();
if driver_name.eq(sensor_driver_name.as_str()) {
return Some(idx as u16);
}
}
None
}
pub fn get_sensor_driver(&self, driver_name: &str) -> Option<&VirtualSensorDriver> {
let driver_idx = self.get_sensor_driver_index(driver_name);
if let Some(driver_idx) = driver_idx {
return Some(&self.sensor[driver_idx as usize]);
}
None
}
pub fn get_relay_driver_index(&self, driver_name: &str) -> Option<u16> {
for (idx, relay_driver) in self.relay.iter().enumerate() {
let relay_driver_name =
relay_driver.namespace.clone() + "/" + relay_driver.function.clone().as_str();
if driver_name.eq(relay_driver_name.as_str()) {
return Some(idx as u16);
}
}
None
}
pub fn get_relay_driver(&self, driver_name: &str) -> Option<&VirtualRelayDriver> {
let driver_idx = self.get_relay_driver_index(driver_name);
if let Some(driver_idx) = driver_idx {
return Some(&self.relay[driver_idx as usize]);
}
None
}
}
impl VirtualRelayDriver {
pub fn get_arg_index(&self, arg_name: &str) -> Option<u16> {
if let Some(args) = &self.fields {
for (idx, arg) in args.iter().enumerate() {
if arg.get("name") == Some(&arg_name.to_string()) {
return Some(idx as u16);
}
}
}
None
}
}
impl VirtualSensorDriver {
pub fn get_arg_index(&self, arg_name: &str) -> Option<u16> {
if let Some(args) = &self.fields {
for (idx, arg) in args.iter().enumerate() {
if arg.get("name") == Some(&arg_name.to_string()) {
return Some(idx as u16);
}
}
}
None
}
}
#[cfg(test)]
mod tests {
use codespan_reporting::files::SimpleFiles;
use super::*;
#[test]
fn toml_test() {
let input = r#"version = "0.1"
[[relay]]
namespace = "MissionControl"
function = "UDP_Command"
fields = [{ name = "id", type = "u16" }]
[[sensor]]
namespace = "MissionControl"
function = "UDP_Request"
fields = [{ name = "stand_id", type = "u16" }]
[[sensor]]
namespace = "TestStand"
function = "RedundantAggregate"
fields = [
{ name = "device_address_a", type = "u16" },
{ name = "device_address_b", type = "u16" },
{ name = "lower", type = "u16" },
{ name = "upper", type = "u16" },
]
"#;
let expected_hash = [
31, 189, 160, 233, 117, 157, 172, 154, 176, 22, 91, 206, 212, 23, 191, 133, 92, 170,
250, 217, 194, 189, 23, 33, 93, 196, 146, 95, 97, 5, 246, 194,
]
.to_vec();
let mut context = Context::new(&SHA256);
context.update(input.as_bytes());
let test_hash = context.finish().as_ref().to_vec();
assert_eq!(expected_hash, test_hash);
let mut sensor_id = BTreeMap::new();
sensor_id.insert("name".into(), "stand_id".into());
sensor_id.insert("type".into(), "u16".into());
let sensor_args_1: Vec<BTreeMap<String, String>> = vec![sensor_id];
let mut sensor_device_address_a = BTreeMap::new();
sensor_device_address_a.insert("name".into(), "device_address_a".into());
sensor_device_address_a.insert("type".into(), "u16".into());
let mut sensor_device_address_b = BTreeMap::new();
sensor_device_address_b.insert("name".into(), "device_address_b".into());
sensor_device_address_b.insert("type".into(), "u16".into());
let mut sensor_lower = BTreeMap::new();
sensor_lower.insert("name".into(), "lower".into());
sensor_lower.insert("type".into(), "u16".into());
let mut sensor_higher = BTreeMap::new();
sensor_higher.insert("name".into(), "upper".into());
sensor_higher.insert("type".into(), "u16".into());
let sensor_args_2: Vec<BTreeMap<String, String>> = vec![
sensor_device_address_a,
sensor_device_address_b,
sensor_lower,
sensor_higher,
];
let mut relay_id = BTreeMap::new();
relay_id.insert("name".into(), "id".into());
relay_id.insert("type".into(), "u16".into());
let relay_args_1: Vec<BTreeMap<String, String>> = vec![relay_id];
let sensor_drivers: Vec<VirtualSensorDriver> = vec![
VirtualSensorDriver {
namespace: "MissionControl".into(),
function: "UDP_Request".into(),
fields: Some(sensor_args_1),
},
VirtualSensorDriver {
namespace: "TestStand".into(),
function: "RedundantAggregate".into(),
fields: Some(sensor_args_2),
},
];
let relay_drivers: Vec<VirtualRelayDriver> = vec![VirtualRelayDriver {
namespace: "MissionControl".into(),
function: "UDP_Command".into(),
fields: Some(relay_args_1),
}];
let files = SimpleFiles::new();
Drivers::parse(0, "empty".to_string(), input)
.print(&files)
.expect("Failed to parse drivers file");
let actual: DriversFile = Drivers::parse(0, "empty".to_string(), input)
.to_option()
.unwrap();
assert_eq!(sensor_drivers, actual.drivers.sensor);
assert_eq!(relay_drivers, actual.drivers.relay);
assert_eq!(expected_hash, actual.hash);
}
}