use std::ops::RangeInclusive; use serde::{Deserialize, Serialize}; pub mod id; pub mod instruction; pub mod semi_human; pub type Map = ahash::AHashMap; pub type Set = ahash::AHashSet; /// Gives you a super well typed graph IR for a given human-readable repr. /// /// Look at [`semi_human::GraphIr`] and the test files in the repo at `testfiles/` /// to see what the RON should look like. /// No, we don't want you to write out [`GraphIr`] in full by hand. /// That's something for the machines to do. /// /// # Errors /// /// Returns an error if the parsed source is not a valid human-readable graph IR. pub fn from_ron(source: &str) -> ron::error::SpannedResult { let human_repr: semi_human::GraphIr = ron::from_str(source)?; Ok(human_repr.into()) } /// The toplevel representation of a whole pipeline. /// /// Pipelines may not be fully linear. They may branch out and recombine later on. /// As such, the representation for them which is currently used is a /// [**D**irected **A**cyclic **G**raph](https://en.wikipedia.org/wiki/Directed_acyclic_graph). /// /// For those who are already familiar with graphs, a DAG is one, except that: /// /// - It is **directed**: Edges have a direction they point to. /// In this case, edges point from the outputs of streamers to inputs of consumers. /// - It is **acyclic**: Those directed edges may not form loops. /// In other words, if one follows edges only in their direction, it must be impossible /// to come back to an already visited node. /// /// Here, if an edge points from _A_ to _B_ (`A --> B`), /// then _A_ is called a **dependency** of _B_, /// and _B_ is called a **dependent** of _A_. /// /// The DAG also enables another neat operation: /// [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting). /// This allows to put the entire graph into a linear list, /// where it's guaranteed that once a vertex is visited, /// all dependencies of it will have been visited already as well. /// /// The representation used here in specific is a bit more complicated, /// since **instructions** directly aren't just connected to one another, /// but their **sockets** are instead. /// /// So the vertices of the DAG are the **sockets** /// (which are either [`id::Input`] or [`id::Output`] depending on the direction), /// and each **socket** in turn belongs to an **instruction**. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub struct GraphIr { /// "Backbone" storage of all **instruction** IDs to /// what **kind of instruction** they are. instructions: Map, /// How the data flows forward. **Dependencies** map to **dependents** here. edges: Map>, /// How the data flows backward. **Dependents** map to **dependencies** here. rev_edges: Map>, } #[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] pub struct Span { range: RangeInclusive, }