use arora_types::call::Call;
use arora_types::value::{StructureField, Value};
use uuid::{uuid, Uuid};
use crate::graph::GraphDiff;
pub const PREFIX: &str = "arora/";
pub const TIME: &str = "arora/time";
pub const DT: &str = "arora/dt";
pub fn is_golden(key: &str) -> bool {
key.starts_with(PREFIX)
}
pub const BEHAVIOR_MODULE: Uuid = uuid!("61726f72-6100-0000-0000-000000000001");
pub const APPLY_DIFF: Uuid = uuid!("61726f72-6100-0000-0000-000000000002");
pub const APPLY_DIFF_ARG: Uuid = uuid!("61726f72-6100-0000-0000-000000000003");
pub fn encode_apply(diff: &GraphDiff) -> Call {
let json = serde_json::to_string(diff).expect("a GraphDiff serializes");
Call {
module_id: Some(BEHAVIOR_MODULE),
id: APPLY_DIFF,
args: vec![StructureField {
id: APPLY_DIFF_ARG,
value: Box::new(Value::String(json)),
}],
}
}
pub fn decode_apply(call: &Call) -> Result<GraphDiff, String> {
if call.id != APPLY_DIFF {
return Err(format!("not the behavior-edit function: {}", call.id));
}
let arg = call
.args
.iter()
.find(|arg| arg.id == APPLY_DIFF_ARG)
.ok_or("the edit call is missing its diff argument")?;
let Value::String(json) = arg.value.as_ref() else {
return Err("the diff argument must be a JSON string".to_string());
};
serde_json::from_str(json).map_err(|e| format!("malformed GraphDiff: {e}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn golden_keys_share_the_reserved_prefix() {
assert!(is_golden(TIME));
assert!(is_golden(DT));
assert!(TIME.starts_with(PREFIX));
assert!(DT.starts_with(PREFIX));
}
#[test]
fn ordinary_keys_are_not_golden() {
assert!(!is_golden("sensor/x"));
assert!(!is_golden("actuator/y"));
assert!(!is_golden("device/arora/time"));
}
#[test]
fn apply_call_round_trips_the_diff() {
let diff = GraphDiff {
remove_nodes: vec![Uuid::from_u128(7)],
set_root: Some(Uuid::from_u128(9)),
..Default::default()
};
let call = encode_apply(&diff);
assert_eq!(call.module_id, Some(BEHAVIOR_MODULE));
assert_eq!(call.id, APPLY_DIFF);
assert_eq!(decode_apply(&call).unwrap(), diff);
}
#[test]
fn decode_apply_rejects_malformed_calls() {
let mut call = encode_apply(&GraphDiff::default());
call.id = Uuid::from_u128(1);
assert!(decode_apply(&call).is_err());
let mut call = encode_apply(&GraphDiff::default());
call.args.clear();
assert!(decode_apply(&call).is_err());
let mut call = encode_apply(&GraphDiff::default());
call.args[0].value = Box::new(Value::Boolean(true));
assert!(decode_apply(&call).is_err());
let mut call = encode_apply(&GraphDiff::default());
call.args[0].value = Box::new(Value::String("not json".to_string()));
assert!(decode_apply(&call).is_err());
}
}