use std::rc::Rc;
use wolf_graph::prelude::*;
use anyhow::{anyhow, Result};
use wolf_derivation_graph::{DerivationGraph, OpInputs, Operation};
#[derive(Debug, Clone, PartialEq, Eq)]
enum Value {
Int(i32),
String(String),
}
type Graph = DerivationGraph<Value>;
type Inputs = OpInputs<Value>;
type Op = Operation<Value>;
fn input(inputs: &Inputs, key: &str) -> Result<Value> {
inputs.get(key).cloned().ok_or_else(|| anyhow!("key not found: '{key}'"))
}
fn input_int(inputs: &Inputs, key: &str) -> Result<i32> {
i32::try_from(input(inputs, key)?)
}
fn input_string(inputs: &Inputs, key: &str) -> Result<String> {
String::try_from(input(inputs, key)?)
}
fn output(graph: &Graph, node: &NodeID) -> Value {
graph.derived_node_value(node).unwrap()
}
fn output_int(graph: &Graph, node: &NodeID) -> i32 {
i32::try_from(output(graph, node)).unwrap()
}
fn output_string(graph: &Graph, node: &NodeID) -> String {
String::try_from(output(graph, node)).unwrap()
}
impl From<i32> for Value {
fn from(value: i32) -> Self {
Value::Int(value)
}
}
impl From<String> for Value {
fn from(value: String) -> Self {
Value::String(value)
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Value::String(value.to_string())
}
}
impl TryFrom<Value> for i32 {
type Error = anyhow::Error;
fn try_from(value: Value) -> Result<Self> {
match value {
Value::Int(i) => Ok(i),
_ => Err(anyhow!("not an integer")),
}
}
}
impl TryFrom<Value> for String {
type Error = anyhow::Error;
fn try_from(value: Value) -> Result<Self> {
match value {
Value::String(s) => Ok(s),
_ => Err(anyhow!("not a string")),
}
}
}
fn add_op() -> Op {
Rc::new(|inputs| {
let lhs = input_int(&inputs, "lhs")?;
let rhs = input_int(&inputs, "rhs")?;
Ok((lhs + rhs).into())
})
}
fn sub_op() -> Op {
Rc::new(|inputs| {
let lhs = input_int(&inputs, "lhs")?;
let rhs = input_int(&inputs, "rhs")?;
Ok((lhs - rhs).into())
})
}
fn mul_op() -> Op {
Rc::new(|inputs| {
let lhs = input_int(&inputs, "lhs")?;
let rhs = input_int(&inputs, "rhs")?;
Ok((lhs * rhs).into())
})
}
fn replicate_op() -> Op {
Rc::new(|inputs| {
let count = input_int(&inputs, "count")?;
let value = input_string(&inputs, "value")?;
if count < 0 {
return Err(anyhow!("count must be non-negative"));
}
Ok(value.repeat(count as usize).into())
})
}
#[test]
fn test() {
let mut graph = DerivationGraph::<Value>::new();
let int_1 = NodeID::new();
graph.add_node_with_value(&int_1, "Int 1", 2).unwrap();
assert_eq!(graph.node_label(&int_1).unwrap(), "Int 1");
let int_2 = NodeID::new();
graph.add_node_with_value(&int_2, "Int 2", 3).unwrap();
let arith_op = NodeID::new();
graph.add_node_with_operation(&arith_op, "ArithOp", add_op()).unwrap();
let eid1 = EdgeID::new();
graph.add_edge(&eid1, &int_1, &arith_op, "lhs").unwrap();
graph.add_edge(EdgeID::new(), &int_2, &arith_op, "rhs").unwrap();
assert_eq!(graph.edge_label(&eid1).unwrap(), "lhs");
let animal = NodeID::new();
graph.add_node_with_value(&animal, "Animal", "Cat").unwrap();
let string_op = NodeID::new();
graph.add_node_with_operation(&string_op, "StringOp", replicate_op()).unwrap();
graph.add_edge(EdgeID::new(), &arith_op, &string_op, "count").unwrap();
graph.add_edge(EdgeID::new(), &animal, &string_op, "value").unwrap();
assert_eq!(output_string(&graph, &string_op), "CatCatCatCatCat");
graph.set_node_value(&animal, "Dog").unwrap();
assert_eq!(output_string(&graph, &string_op), "DogDogDogDogDog");
graph.set_operation(&arith_op, mul_op()).unwrap();
assert_eq!(output_string(&graph, &string_op), "DogDogDogDogDogDog");
assert_eq!(output_int(&graph, &arith_op), 6);
graph.clear_all_node_values();
graph.set_node_value(&int_1, 12).unwrap();
graph.set_node_value(&int_2, 7).unwrap();
graph.set_operation(&arith_op, sub_op()).unwrap();
graph.set_node_value(&animal, "🐺").unwrap();
assert_eq!(output_string(&graph, &string_op), "🐺🐺🐺🐺🐺");
}