diff --git a/Cargo.toml b/Cargo.toml index 1b8bc5798..1d190c787 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -155,7 +155,7 @@ dot_ix = "0.2.0" dyn-clone = "1.0.17" enser = "0.1.4" erased-serde = "0.4.3" -fn_graph = { version = "0.13.0", features = ["async", "graph_info", "interruptible", "resman"] } +fn_graph = { version = "0.13.2", features = ["async", "graph_info", "interruptible", "resman"] } futures = "0.3.30" heck = "0.4.1" indexmap = "2.2.5" diff --git a/crate/flow_model/src/flow_info.rs b/crate/flow_model/src/flow_info.rs index fee60679b..7ae0728cb 100644 --- a/crate/flow_model/src/flow_info.rs +++ b/crate/flow_model/src/flow_info.rs @@ -17,3 +17,13 @@ pub struct FlowInfo { /// Serialized representation of the flow graph. pub graph_info: GraphInfo, } + +impl FlowInfo { + /// Returns a new `FlowInfo`. + pub fn new(flow_id: FlowId, graph_info: GraphInfo) -> Self { + Self { + flow_id, + graph_info, + } + } +} diff --git a/crate/flow_model/src/flow_spec_info.rs b/crate/flow_model/src/flow_spec_info.rs index 448c9fa29..6303670ec 100644 --- a/crate/flow_model/src/flow_spec_info.rs +++ b/crate/flow_model/src/flow_spec_info.rs @@ -23,6 +23,14 @@ pub struct FlowSpecInfo { } impl FlowSpecInfo { + /// Returns a new `FlowSpecInfo`. + pub fn new(flow_id: FlowId, graph_info: GraphInfo) -> Self { + Self { + flow_id, + graph_info, + } + } + /// Returns an [`InfoGraph`] that represents the progress of the flow's /// execution. pub fn into_progress_info_graph(&self) -> InfoGraph { diff --git a/crate/flow_model/src/item_info.rs b/crate/flow_model/src/item_info.rs index 6b1656615..29882e5dc 100644 --- a/crate/flow_model/src/item_info.rs +++ b/crate/flow_model/src/item_info.rs @@ -9,3 +9,10 @@ pub struct ItemInfo { /// ID of the `Item`. pub item_id: ItemId, } + +impl ItemInfo { + /// Returns a new `ItemInfo`. + pub fn new(item_id: ItemId) -> Self { + Self { item_id } + } +} diff --git a/crate/flow_model/src/item_spec_info.rs b/crate/flow_model/src/item_spec_info.rs index a95f948f0..e5ff66e1a 100644 --- a/crate/flow_model/src/item_spec_info.rs +++ b/crate/flow_model/src/item_spec_info.rs @@ -9,3 +9,10 @@ pub struct ItemSpecInfo { /// ID of the `Item`. pub item_id: ItemId, } + +impl ItemSpecInfo { + /// Returns a new `ItemSpecInfo`. + pub fn new(item_id: ItemId) -> Self { + Self { item_id } + } +} diff --git a/crate/flow_model/src/lib.rs b/crate/flow_model/src/lib.rs index e8822b35f..f2d782848 100644 --- a/crate/flow_model/src/lib.rs +++ b/crate/flow_model/src/lib.rs @@ -3,6 +3,9 @@ //! This includes the serializable representation of a `Flow`. Since an actual //! `Flow` contains logic, it currently resides in `peace_rt_model`. +// Re-exports; +pub use fn_graph::GraphInfo; + pub use crate::{ flow_info::FlowInfo, flow_spec_info::FlowSpecInfo, item_info::ItemInfo, item_spec_info::ItemSpecInfo, diff --git a/crate/rt_model/src/flow.rs b/crate/rt_model/src/flow.rs index 2780f7258..9a804b64c 100644 --- a/crate/rt_model/src/flow.rs +++ b/crate/rt_model/src/flow.rs @@ -72,9 +72,6 @@ impl Flow { ItemSpecInfo { item_id } }); - FlowSpecInfo { - flow_id, - graph_info, - } + FlowSpecInfo::new(flow_id, graph_info) } } diff --git a/workspace_tests/src/flow_model.rs b/workspace_tests/src/flow_model.rs new file mode 100644 index 000000000..d6e8de6ba --- /dev/null +++ b/workspace_tests/src/flow_model.rs @@ -0,0 +1,4 @@ +mod flow_info; +mod flow_spec_info; +mod item_info; +mod item_spec_info; diff --git a/workspace_tests/src/flow_model/flow_info.rs b/workspace_tests/src/flow_model/flow_info.rs new file mode 100644 index 000000000..7520d5765 --- /dev/null +++ b/workspace_tests/src/flow_model/flow_info.rs @@ -0,0 +1,178 @@ +use peace::{ + cfg::{flow_id, item_id}, + data::fn_graph::{daggy::Dag, Edge, WouldCycle}, + flow_model::{FlowInfo, FlowSpecInfo, GraphInfo, ItemInfo, ItemSpecInfo}, + rt_model::{Flow, ItemGraph, ItemGraphBuilder}, +}; +use peace_items::blank::BlankItem; + +use crate::PeaceTestError; + +#[test] +fn clone() -> Result<(), Box> { + let flow_info = flow_info()?; + + assert_eq!(flow_info, Clone::clone(&flow_info)); + Ok(()) +} + +#[test] +fn debug() -> Result<(), Box> { + let flow_info = flow_info()?; + + assert_eq!( + "FlowInfo { \ + flow_id: FlowId(\"flow_id\"), \ + graph_info: GraphInfo { \ + graph: Dag { graph: Graph { Ty: \"Directed\", node_count: 6, edge_count: 9, edges: (0, 1), (0, 2), (1, 4), (2, 3), (3, 4), (5, 4), (1, 2), (5, 1), (0, 5), node weights: {0: ItemInfo { item_id: ItemId(\"a\") }, 1: ItemInfo { item_id: ItemId(\"b\") }, 2: ItemInfo { item_id: ItemId(\"c\") }, 3: ItemInfo { item_id: ItemId(\"d\") }, 4: ItemInfo { item_id: ItemId(\"e\") }, 5: ItemInfo { item_id: ItemId(\"f\") }}, edge weights: {0: Logic, 1: Logic, 2: Logic, 3: Logic, 4: Logic, 5: Logic, 6: Data, 7: Data, 8: Data} }, cycle_state: DfsSpace { dfs: Dfs { stack: [], discovered: FixedBitSet { data: [], length: 0 } } } } \ + } \ + }", + format!("{flow_info:?}") + ); + Ok(()) +} + +#[test] +fn serialize() -> Result<(), Box> { + let flow_info = flow_info()?; + + assert_eq!( + r#"flow_id: flow_id +graph_info: + graph: + nodes: + - item_id: a + - item_id: b + - item_id: c + - item_id: d + - item_id: e + - item_id: f + node_holes: [] + edge_property: directed + edges: + - - 0 + - 1 + - Logic + - - 0 + - 2 + - Logic + - - 1 + - 4 + - Logic + - - 2 + - 3 + - Logic + - - 3 + - 4 + - Logic + - - 5 + - 4 + - Logic + - - 1 + - 2 + - Data + - - 5 + - 1 + - Data + - - 0 + - 5 + - Data +"#, + serde_yaml::to_string(&flow_info)? + ); + Ok(()) +} + +#[test] +fn deserialize() -> Result<(), Box> { + let flow_info = flow_info()?; + + assert_eq!( + flow_info, + serde_yaml::from_str( + r#"flow_id: flow_id +graph_info: + graph: + nodes: + - item_id: a + - item_id: b + - item_id: c + - item_id: d + - item_id: e + - item_id: f + node_holes: [] + edge_property: directed + edges: + - [0, 1, Logic] + - [0, 2, Logic] + - [1, 4, Logic] + - [2, 3, Logic] + - [3, 4, Logic] + - [5, 4, Logic] + - [1, 2, Data] + - [5, 1, Data] + - [0, 5, Data] +"# + )? + ); + Ok(()) +} + +fn flow_info() -> Result> { + let flow = Flow::new(flow_id!("flow_id"), complex_graph()?); + let FlowSpecInfo { + flow_id, + graph_info, + } = flow.flow_spec_info(); + + let mut graph = graph_info.iter_insertion_with_indices().fold( + Dag::new(), + |mut graph, (_, item_spec_info)| { + let ItemSpecInfo { item_id } = item_spec_info; + let item_info = ItemInfo::new(item_id.clone()); + graph.add_node(item_info); + graph + }, + ); + + let edges = graph_info + .raw_edges() + .iter() + .map(|e| (e.source(), e.target(), e.weight)); + + graph.add_edges(edges).expect( + "Edges are all directed from the original graph, \ + so this cannot cause a cycle.", + ); + + let graph_info = GraphInfo::new(graph); + let flow_info = FlowInfo::new(flow_id, graph_info); + Ok(flow_info) +} + +fn complex_graph() -> Result, WouldCycle> { + // a - b --------- e + // \ / / + // '-- c - d / + // / + // f --------' + let mut item_graph_builder = ItemGraphBuilder::new(); + let [fn_id_a, fn_id_b, fn_id_c, fn_id_d, fn_id_e, fn_id_f] = item_graph_builder.add_fns([ + BlankItem::<()>::new(item_id!("a")).into(), + BlankItem::<()>::new(item_id!("b")).into(), + BlankItem::<()>::new(item_id!("c")).into(), + BlankItem::<()>::new(item_id!("d")).into(), + BlankItem::<()>::new(item_id!("e")).into(), + BlankItem::<()>::new(item_id!("f")).into(), + ]); + item_graph_builder.add_logic_edges([ + (fn_id_a, fn_id_b), + (fn_id_a, fn_id_c), + (fn_id_b, fn_id_e), + (fn_id_c, fn_id_d), + (fn_id_d, fn_id_e), + (fn_id_f, fn_id_e), + ])?; + let item_graph = item_graph_builder.build(); + Ok(item_graph) +} diff --git a/workspace_tests/src/flow_model/flow_spec_info.rs b/workspace_tests/src/flow_model/flow_spec_info.rs new file mode 100644 index 000000000..d0db0fbab --- /dev/null +++ b/workspace_tests/src/flow_model/flow_spec_info.rs @@ -0,0 +1,154 @@ +use peace::{ + cfg::{flow_id, item_id}, + data::fn_graph::{Edge, WouldCycle}, + flow_model::FlowSpecInfo, + rt_model::{Flow, ItemGraph, ItemGraphBuilder}, +}; +use peace_items::blank::BlankItem; + +use crate::PeaceTestError; + +#[test] +fn clone() -> Result<(), Box> { + let flow_spec_info = flow_spec_info()?; + + assert_eq!(flow_spec_info, Clone::clone(&flow_spec_info)); + Ok(()) +} + +#[test] +fn debug() -> Result<(), Box> { + let flow_spec_info = flow_spec_info()?; + + assert_eq!( + "FlowSpecInfo { \ + flow_id: FlowId(\"flow_id\"), \ + graph_info: GraphInfo { \ + graph: Dag { graph: Graph { Ty: \"Directed\", node_count: 6, edge_count: 9, edges: (0, 1), (0, 2), (1, 4), (2, 3), (3, 4), (5, 4), (1, 2), (5, 1), (0, 5), node weights: {0: ItemSpecInfo { item_id: ItemId(\"a\") }, 1: ItemSpecInfo { item_id: ItemId(\"b\") }, 2: ItemSpecInfo { item_id: ItemId(\"c\") }, 3: ItemSpecInfo { item_id: ItemId(\"d\") }, 4: ItemSpecInfo { item_id: ItemId(\"e\") }, 5: ItemSpecInfo { item_id: ItemId(\"f\") }}, edge weights: {0: Logic, 1: Logic, 2: Logic, 3: Logic, 4: Logic, 5: Logic, 6: Data, 7: Data, 8: Data} }, cycle_state: DfsSpace { dfs: Dfs { stack: [], discovered: FixedBitSet { data: [], length: 0 } } } } \ + } \ + }", + format!("{flow_spec_info:?}") + ); + Ok(()) +} + +#[test] +fn serialize() -> Result<(), Box> { + let flow_spec_info = flow_spec_info()?; + + assert_eq!( + r#"flow_id: flow_id +graph_info: + graph: + nodes: + - item_id: a + - item_id: b + - item_id: c + - item_id: d + - item_id: e + - item_id: f + node_holes: [] + edge_property: directed + edges: + - - 0 + - 1 + - Logic + - - 0 + - 2 + - Logic + - - 1 + - 4 + - Logic + - - 2 + - 3 + - Logic + - - 3 + - 4 + - Logic + - - 5 + - 4 + - Logic + - - 1 + - 2 + - Data + - - 5 + - 1 + - Data + - - 0 + - 5 + - Data +"#, + serde_yaml::to_string(&flow_spec_info)? + ); + Ok(()) +} + +#[test] +fn deserialize() -> Result<(), Box> { + let flow_spec_info = flow_spec_info()?; + + assert_eq!( + flow_spec_info, + serde_yaml::from_str( + r#"flow_id: flow_id +graph_info: + graph: + nodes: + - item_id: a + - item_id: b + - item_id: c + - item_id: d + - item_id: e + - item_id: f + node_holes: [] + edge_property: directed + edges: + - [0, 1, Logic] + - [0, 2, Logic] + - [1, 4, Logic] + - [2, 3, Logic] + - [3, 4, Logic] + - [5, 4, Logic] + - [1, 2, Data] + - [5, 1, Data] + - [0, 5, Data] +"# + )? + ); + Ok(()) +} + +fn flow_spec_info() -> Result> { + let flow_spec_info: FlowSpecInfo = { + let flow = Flow::new(flow_id!("flow_id"), complex_graph()?); + flow.flow_spec_info() + }; + Ok(flow_spec_info) +} + +fn complex_graph() -> Result, WouldCycle> { + // a - b --------- e + // \ / / + // '-- c - d / + // / + // f --------' + let mut item_graph_builder = ItemGraphBuilder::new(); + let [fn_id_a, fn_id_b, fn_id_c, fn_id_d, fn_id_e, fn_id_f] = item_graph_builder.add_fns([ + BlankItem::<()>::new(item_id!("a")).into(), + BlankItem::<()>::new(item_id!("b")).into(), + BlankItem::<()>::new(item_id!("c")).into(), + BlankItem::<()>::new(item_id!("d")).into(), + BlankItem::<()>::new(item_id!("e")).into(), + BlankItem::<()>::new(item_id!("f")).into(), + ]); + item_graph_builder.add_logic_edges([ + (fn_id_a, fn_id_b), + (fn_id_a, fn_id_c), + (fn_id_b, fn_id_e), + (fn_id_c, fn_id_d), + (fn_id_d, fn_id_e), + (fn_id_f, fn_id_e), + ])?; + let item_graph = item_graph_builder.build(); + Ok(item_graph) +} diff --git a/workspace_tests/src/flow_model/item_info.rs b/workspace_tests/src/flow_model/item_info.rs new file mode 100644 index 000000000..593e3cce8 --- /dev/null +++ b/workspace_tests/src/flow_model/item_info.rs @@ -0,0 +1,34 @@ +use peace::{cfg::item_id, flow_model::ItemInfo}; + +#[test] +fn clone() { + let item_info = ItemInfo::new(item_id!("item_id")); + + assert_eq!(item_info, Clone::clone(&item_info)); +} + +#[test] +fn debug() { + let item_info = ItemInfo::new(item_id!("item_id")); + + assert_eq!( + "ItemInfo { item_id: ItemId(\"item_id\") }", + format!("{item_info:?}") + ); +} + +#[test] +fn serialize() -> Result<(), serde_yaml::Error> { + let item_info = ItemInfo::new(item_id!("item_id")); + + assert_eq!("item_id: item_id\n", serde_yaml::to_string(&item_info)?); + Ok(()) +} + +#[test] +fn deserialize() -> Result<(), serde_yaml::Error> { + let item_info = ItemInfo::new(item_id!("item_id")); + + assert_eq!(item_info, serde_yaml::from_str("item_id: item_id\n")?); + Ok(()) +} diff --git a/workspace_tests/src/flow_model/item_spec_info.rs b/workspace_tests/src/flow_model/item_spec_info.rs new file mode 100644 index 000000000..b056c4c9c --- /dev/null +++ b/workspace_tests/src/flow_model/item_spec_info.rs @@ -0,0 +1,37 @@ +use peace::{cfg::item_id, flow_model::ItemSpecInfo}; + +#[test] +fn clone() { + let item_spec_info = ItemSpecInfo::new(item_id!("item_id")); + + assert_eq!(item_spec_info, Clone::clone(&item_spec_info)); +} + +#[test] +fn debug() { + let item_spec_info = ItemSpecInfo::new(item_id!("item_id")); + + assert_eq!( + "ItemSpecInfo { item_id: ItemId(\"item_id\") }", + format!("{item_spec_info:?}") + ); +} + +#[test] +fn serialize() -> Result<(), serde_yaml::Error> { + let item_spec_info = ItemSpecInfo::new(item_id!("item_id")); + + assert_eq!( + "item_id: item_id\n", + serde_yaml::to_string(&item_spec_info)? + ); + Ok(()) +} + +#[test] +fn deserialize() -> Result<(), serde_yaml::Error> { + let item_spec_info = ItemSpecInfo::new(item_id!("item_id")); + + assert_eq!(item_spec_info, serde_yaml::from_str("item_id: item_id\n")?); + Ok(()) +} diff --git a/workspace_tests/src/lib.rs b/workspace_tests/src/lib.rs index 5d961cda2..fd41ffbd7 100644 --- a/workspace_tests/src/lib.rs +++ b/workspace_tests/src/lib.rs @@ -23,6 +23,7 @@ mod cmd_model; mod cmd_rt; mod data; mod diff; +mod flow_model; mod fmt; mod params; mod resources;