1use std::collections::{BTreeMap, BTreeSet};
7use std::rc::Rc;
8
9use crate::ctx::{Ctx, DepTerminal};
10use crate::graph::{Graph, GraphNodeOpts};
11use crate::node::{Node, NodeOpts};
12use crate::operators::Operator;
13use crate::protocol::Message;
14
15pub struct Pipe<T> {
17 graph: Graph,
18 current: Node<T>,
19}
20
21pub fn pipe<T: 'static>(graph: &Graph, source: Node<T>) -> Pipe<T> {
23 Pipe {
24 graph: graph.clone(),
25 current: source,
26 }
27}
28
29impl<T: 'static> Pipe<T> {
30 pub fn through<U: 'static>(self, op: Operator<U>) -> Pipe<U> {
32 self.through_opts(op, GraphNodeOpts::default())
33 }
34
35 pub fn through_opts<U: 'static>(self, op: Operator<U>, opts: GraphNodeOpts) -> Pipe<U> {
37 let current = self.graph.init_node(op, vec![self.current.erased()], opts);
38 Pipe {
39 graph: self.graph,
40 current,
41 }
42 }
43
44 pub fn done(self) -> Node<T> {
46 self.current
47 }
48}
49
50#[derive(Clone, Debug, PartialEq, Eq)]
51pub struct StratifyRule<R> {
53 pub name: String,
55 pub rule: R,
57 pub meta: BTreeMap<String, String>,
59}
60
61impl<R> StratifyRule<R> {
62 pub fn new(name: impl Into<String>, rule: R) -> Self {
64 Self {
65 name: name.into(),
66 rule,
67 meta: BTreeMap::new(),
68 }
69 }
70
71 pub fn meta(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
73 self.meta.insert(key.into(), value.into());
74 self
75 }
76}
77
78#[derive(Clone, Debug)]
79pub struct StratifyOptions {
81 pub prefix: String,
83 pub rules: GraphNodeOpts,
85 pub branches: BTreeMap<String, GraphNodeOpts>,
87}
88
89impl Default for StratifyOptions {
90 fn default() -> Self {
91 Self {
92 prefix: "branch".to_owned(),
93 rules: GraphNodeOpts::default(),
94 branches: BTreeMap::new(),
95 }
96 }
97}
98
99#[derive(Clone)]
100pub struct Stratified<T, R> {
102 pub rules: Node<Vec<StratifyRule<R>>>,
104 pub branches: BTreeMap<String, Node<T>>,
106}
107
108pub fn stratify_branch<T, R, F>(
110 graph: &Graph,
111 source: &Node<T>,
112 rules: &Node<R>,
113 classifier: F,
114 opts: GraphNodeOpts,
115) -> Node<T>
116where
117 T: Clone + 'static,
118 R: Clone + 'static,
119 F: Fn(&R, &T) -> bool + 'static,
120{
121 let op = stratify_branch_operator(classifier, 0, 1);
122 graph.init_node(op, vec![source.erased(), rules.erased()], opts)
123}
124
125pub fn stratify<T, R, F>(
127 graph: &Graph,
128 source: &Node<T>,
129 rules: Vec<StratifyRule<R>>,
130 classifier: F,
131 opts: StratifyOptions,
132) -> Stratified<T, R>
133where
134 T: Clone + 'static,
135 R: Clone + 'static,
136 F: Fn(&R, &T) -> bool + 'static,
137{
138 let mut seen = BTreeSet::new();
139 for rule in &rules {
140 assert!(
141 seen.insert(rule.name.clone()),
142 "stratify: duplicate rule name '{}'",
143 rule.name
144 );
145 }
146
147 let mut rules_opts = opts.rules.clone();
148 if rules_opts.name.is_none() {
149 rules_opts.name = Some(format!("{}/rules", opts.prefix));
150 }
151 rules_opts
152 .meta
153 .entry("kind".to_owned())
154 .or_insert_with(|| "stratify_rules".to_owned());
155 let rules_node = graph.state_opts(rules.clone(), rules_opts);
156
157 let classifier = Rc::new(classifier);
158 let mut branches = BTreeMap::new();
159 for rule in rules {
160 let branch_name = rule.name.clone();
161 let classifier = classifier.clone();
162 let op = stratify_branch_operator(
163 move |all: &Vec<StratifyRule<R>>, value: &T| {
164 all.iter()
165 .find(|candidate| candidate.name == branch_name)
166 .map(|current| classifier(¤t.rule, value))
167 .unwrap_or(false)
168 },
169 0,
170 1,
171 );
172 let mut branch_opts = opts.branches.get(&rule.name).cloned().unwrap_or_default();
173 if branch_opts.name.is_none() {
174 branch_opts.name = Some(format!("{}/{}", opts.prefix, rule.name));
175 }
176 for (key, value) in rule.meta {
177 branch_opts.meta.entry(key).or_insert(value);
178 }
179 branch_opts
180 .meta
181 .entry("branch".to_owned())
182 .or_insert_with(|| rule.name.clone());
183 let branch = graph.init_node(op, vec![source.erased(), rules_node.erased()], branch_opts);
184 branches.insert(rule.name, branch);
185 }
186
187 Stratified {
188 rules: rules_node,
189 branches,
190 }
191}
192
193fn stratify_branch_operator<T, R, F>(
194 classifier: F,
195 source_index: usize,
196 rules_index: usize,
197) -> Operator<T>
198where
199 T: Clone + 'static,
200 R: Clone + 'static,
201 F: Fn(&R, &T) -> bool + 'static,
202{
203 Operator::with_opts(
204 "stratifyBranch",
205 NodeOpts {
206 partial: true,
207 complete_when_deps_complete: false,
208 error_when_deps_error: false,
209 terminal_as_real_input: true,
210 ..NodeOpts::default()
211 },
212 move |ctx: &Ctx| {
213 if let Some(rules) = ctx.data::<R>(rules_index) {
214 for value in ctx.batch::<T>(source_index) {
215 if classifier(rules.as_ref(), value.as_ref()) {
216 ctx.emit((*value).clone());
217 }
218 }
219 }
220
221 match ctx.terminal(source_index) {
222 Some(DepTerminal::Complete) => ctx.down(vec![Message::Complete]),
223 Some(DepTerminal::Error(error)) => {
224 ctx.down(vec![Message::Error(error.to_string().into())]);
225 }
226 None => {}
227 }
228 },
229 )
230}