Skip to main content

dataflow_rs/function/
config.rs

1//! `FunctionConfig`: a function's input, compiled once at build.
2
3use crate::base::{IssueCode, Result};
4use crate::expr::{CompileCx, ExprVisitor, FieldSpec, Output, Resolver};
5use datavalue::OwnedDataValue;
6use serde_json::Value;
7
8/// A function's `input`, compiled at build so a malformed one fails before any
9/// message runs. `#[derive(FunctionConfig)]` writes the implementation.
10///
11/// ```
12/// use dataflow_rs::Expr;
13/// use dataflow_rs::expr::{CompileCx, ExprEngine, Output};
14/// use dataflow_rs::function::FunctionConfig;
15/// use dataflow_rs::program::IssueCode;
16/// use serde_json::json;
17///
18/// #[derive(FunctionConfig)]
19/// struct Fetch {
20///     /// The URL to fetch.
21///     #[config(records)]
22///     url: Expr<String>,
23///     #[config(default = 5000)]
24///     timeout_ms: Expr<u64>,
25///     #[output(default = "data.response")]
26///     output: Output,
27/// }
28///
29/// let engine = ExprEngine::default();
30/// let mut issues = Vec::new();
31/// let mut cx = CompileCx::new(&engine, &mut issues);
32/// let fetch = Fetch::compile(&json!({"url": {"var": "data.url"}}), &mut cx).unwrap();
33/// assert_eq!(fetch.timeout_ms.constant(), Some(&5000));
34/// assert_eq!(fetch.output.constant().unwrap().to_string(), "data.response");
35/// assert!(fetch.url.constant().is_none());
36///
37/// assert!(Fetch::compile(&json!({"uri": "x"}), &mut cx).is_none());
38/// drop(cx);
39/// let codes: Vec<IssueCode> = issues.iter().map(|i| i.code).collect();
40/// assert_eq!(codes, [IssueCode::LikelyTypo, IssueCode::InputParse]);
41/// assert_eq!(issues[1].path.to_string(), "url");
42/// ```
43///
44/// Two outputs need one of them marked `#[output(primary)]`:
45///
46/// ```compile_fail
47/// use dataflow_rs::expr::Output;
48/// use dataflow_rs::function::FunctionConfig;
49///
50/// #[derive(FunctionConfig)]
51/// struct Split {
52///     ok: Output,
53///     failed: Output,
54/// }
55/// ```
56///
57/// The primary output, marked or the only one, is an `Output` or an
58/// `Option<Output>`, since `primary_output` lends it:
59///
60/// ```compile_fail
61/// use dataflow_rs::expr::Output;
62/// use dataflow_rs::function::FunctionConfig;
63///
64/// #[derive(FunctionConfig)]
65/// struct Fan {
66///     #[output(primary)]
67///     many: Vec<Output>,
68///     failed: Output,
69/// }
70/// ```
71///
72/// A field merely *named* `Output` is not one: the derive asserts the type's
73/// `ConfigField::IS_OUTPUT`, so an implementor that leaves it `false` is
74/// refused instead of silently compiling as a destination:
75///
76/// ```compile_fail
77/// use dataflow_rs::expr::{CompileCx, ConfigField, FieldKind, Resolver};
78/// use dataflow_rs::function::FunctionConfig;
79///
80/// struct Output;
81///
82/// impl ConfigField for Output {
83///     fn compile(_raw: &serde_json::Value, _cx: &mut CompileCx<'_>) -> Option<Self> {
84///         Some(Self)
85///     }
86///     fn resolve(
87///         &self,
88///         _r: &mut dyn Resolver,
89///     ) -> dataflow_rs::Result<Option<dataflow_rs::datavalue::OwnedDataValue>> {
90///         Ok(None)
91///     }
92///     fn kind() -> FieldKind {
93///         FieldKind::Output
94///     }
95/// }
96///
97/// #[derive(FunctionConfig)]
98/// struct Writes {
99///     out: Output,
100/// }
101/// ```
102///
103/// The derive refuses what it cannot honour. An unknown `config` key:
104///
105/// ```compile_fail
106/// # use dataflow_rs::function::FunctionConfig;
107/// #[derive(FunctionConfig)]
108/// struct Unknown {
109///     #[config(renamed = "k")]
110///     a: bool,
111/// }
112/// ```
113///
114/// A generic struct, even one whose parameter is a config field:
115///
116/// ```compile_fail
117/// # use dataflow_rs::expr::ConfigField;
118/// # use dataflow_rs::function::FunctionConfig;
119/// #[derive(FunctionConfig)]
120/// struct Generic<T: ConfigField> {
121///     a: T,
122/// }
123/// ```
124///
125/// A tuple struct:
126///
127/// ```compile_fail
128/// # use dataflow_rs::function::FunctionConfig;
129/// #[derive(FunctionConfig)]
130/// struct Tuple(bool);
131/// ```
132///
133/// `records` on a `serde` field, which holds no expression to record:
134///
135/// ```compile_fail
136/// # use dataflow_rs::function::FunctionConfig;
137/// #[derive(FunctionConfig)]
138/// struct Recorded {
139///     #[config(serde, records)]
140///     a: Option<bool>,
141/// }
142/// ```
143///
144/// A `serde` field whose type is not `Serialize`, which
145/// [`resolve`](Self::resolve) reads it through:
146///
147/// ```compile_fail
148/// # use dataflow_rs::function::FunctionConfig;
149/// #[derive(serde::Deserialize)]
150/// enum Mode {
151///     Fast,
152/// }
153///
154/// #[derive(FunctionConfig)]
155/// struct Unserialized {
156///     #[config(serde)]
157///     mode: Mode,
158/// }
159/// ```
160///
161/// A key given twice on one field:
162///
163/// ```compile_fail
164/// # use dataflow_rs::function::FunctionConfig;
165/// #[derive(FunctionConfig)]
166/// struct Renamed {
167///     #[config(rename = "a", rename = "b")]
168///     x: bool,
169/// }
170/// ```
171///
172/// A `default_json` that is not JSON, refused where it is written rather than
173/// when a definition is first compiled:
174///
175/// ```compile_fail
176/// # use dataflow_rs::function::FunctionConfig;
177/// #[derive(FunctionConfig)]
178/// struct BadDefault {
179///     #[config(default_json = "{not json")]
180///     extra: serde_json::Value,
181/// }
182/// ```
183///
184/// A hand-written config that does not say how it resolves:
185///
186/// ```compile_fail
187/// use dataflow_rs::expr::{CompileCx, ExprVisitor, FieldSpec};
188/// use dataflow_rs::function::FunctionConfig;
189///
190/// struct Bare;
191///
192/// impl FunctionConfig for Bare {
193///     fn compile(_input: &serde_json::Value, _cx: &mut CompileCx<'_>) -> Option<Self> {
194///         Some(Self)
195///     }
196///     fn visit(&self, _v: &mut dyn ExprVisitor) {}
197///     fn fields() -> &'static [FieldSpec] {
198///         &[]
199///     }
200/// }
201/// ```
202pub trait FunctionConfig: Sized + Send + Sync + 'static {
203    /// Whether this is [`Templated`](super::Templated), whose fields are
204    /// declared by the call's input rather than by a struct: the registry
205    /// checks its spec against that, and the erasure walks its primary
206    /// output by its own rule.
207    const TEMPLATED: bool = false;
208
209    /// Every issue in the config, each at its exact path below the
210    /// [`CompileCx`]'s root. `None` when any is Rejected.
211    fn compile(input: &Value, cx: &mut CompileCx<'_>) -> Option<Self>;
212
213    /// Walks the config's expressions; paths are relative to the config's root.
214    fn visit(&self, v: &mut dyn ExprVisitor);
215
216    /// The config's fields, for tooling.
217    fn fields() -> &'static [FieldSpec];
218
219    /// The output [`Ctx::emit_default`](crate::Ctx::emit_default) writes
220    /// through, and an answering layer answers with: the derive returns the
221    /// field marked `#[output(primary)]`, or the only output when there is
222    /// one (`None` for an `Option<Output>` the input did not set). A
223    /// hand-written config has none unless it says so.
224    fn primary_output(&self) -> Option<&Output> {
225        None
226    }
227
228    /// The config resolved for one call, through `r`: every field with a
229    /// value, set by the input or by its default, keyed by its authored key,
230    /// in declaration order; an `Option` left unset is absent. An expression
231    /// is evaluated, a document rendered, an output's path given as its
232    /// dotted text, a nested config resolved as an object, and a
233    /// `#[config(serde)]` field written as it serializes (unset when that is
234    /// `null`, as an `Option` left `None` is). The derive generates it; a
235    /// hand-written config writes its own, as it writes
236    /// [`compile`](Self::compile) and [`visit`](Self::visit), so one that
237    /// forgets it fails to compile rather than resolving to nothing.
238    ///
239    /// [`Ctx::input`](crate::Ctx::input) is this over the call's context,
240    /// with every field that reads a secret `null`.
241    ///
242    /// # Errors
243    ///
244    /// The first field's that fails: an evaluation's, or a serde field that
245    /// does not serialize.
246    fn resolve(&self, r: &mut dyn Resolver) -> Result<OwnedDataValue>;
247}
248
249/// The config of a function that takes no input: `{}` or `null`.
250impl FunctionConfig for () {
251    fn compile(input: &Value, cx: &mut CompileCx<'_>) -> Option<Self> {
252        let before = cx.rejected();
253        match input {
254            Value::Null => {}
255            Value::Object(map) => super::__private::unknown_keys(map, &[], cx),
256            _ => cx.issue(
257                IssueCode::InputParse,
258                "expected an object or null, this function takes no input",
259            ),
260        }
261        (cx.rejected() == before).then_some(())
262    }
263
264    fn visit(&self, _v: &mut dyn ExprVisitor) {}
265
266    fn fields() -> &'static [FieldSpec] {
267        &[]
268    }
269
270    /// No input, so nothing to resolve: `null`.
271    fn resolve(&self, _r: &mut dyn Resolver) -> Result<OwnedDataValue> {
272        Ok(OwnedDataValue::Null)
273    }
274}