dfir_lang/graph/ops/
difference_multiset.rs

1use quote::{quote_spanned, ToTokens};
2use syn::parse_quote;
3
4use super::{
5    DelayType, OperatorCategory, OperatorConstraints, OperatorInstance,
6    OperatorWriteOutput, PortIndexValue, WriteContextArgs, RANGE_0, RANGE_1,
7};
8
9/// > 2 input streams of the same type T, 1 output stream of type T
10///
11/// Forms the set difference of the items in the input
12/// streams, returning items in the `pos` input that are not found in the
13/// `neg` input.
14///
15/// `difference` can be provided with one or two generic lifetime persistence arguments
16/// in the same way as [`join`](#join), see [`join`'s documentation](#join) for more info.
17///
18/// Note multiset semantics here: each (possibly duplicated) item in the `pos` input
19/// that has no match in `neg` is sent to the output.
20///
21/// ```dfir
22/// source_iter(vec!["cat", "cat", "elephant", "elephant"]) -> [pos]diff;
23/// source_iter(vec!["cat", "gorilla"]) -> [neg]diff;
24/// diff = difference_multiset() -> assert_eq(["elephant", "elephant"]);
25/// ```
26pub const DIFFERENCE_MULTISET: OperatorConstraints = OperatorConstraints {
27    name: "difference_multiset",
28    categories: &[OperatorCategory::MultiIn],
29    hard_range_inn: &(2..=2),
30    soft_range_inn: &(2..=2),
31    hard_range_out: RANGE_1,
32    soft_range_out: RANGE_1,
33    num_args: 0,
34    persistence_args: &(0..=2),
35    type_args: RANGE_0,
36    is_external_input: false,
37    has_singleton_output: false,
38    flo_type: None,
39    ports_inn: Some(|| super::PortListSpec::Fixed(parse_quote! { pos, neg })),
40    ports_out: None,
41    input_delaytype_fn: |idx| match idx {
42        PortIndexValue::Path(path) if "neg" == path.to_token_stream().to_string() => {
43            Some(DelayType::Stratum)
44        }
45        _else => None,
46    },
47    write_fn: |wc @ &WriteContextArgs {
48                   op_span,
49                   ident,
50                   inputs,
51                   op_inst: OperatorInstance { .. },
52                   ..
53               },
54               diagnostics| {
55        let OperatorWriteOutput {
56            write_prologue,
57            write_iterator,
58            write_iterator_after,
59        } = (super::anti_join_multiset::ANTI_JOIN_MULTISET.write_fn)(wc, diagnostics)?;
60
61        let pos = &inputs[1];
62        let write_iterator = quote_spanned! {op_span=>
63            let #pos = #pos.map(|k| (k, ()));
64            #write_iterator
65            let #ident = #ident.map(|(k, ())| k);
66        };
67
68        Ok(OperatorWriteOutput {
69            write_prologue,
70            write_iterator,
71            write_iterator_after,
72        })
73    },
74};