pub trait ConfigField:
Sized
+ Send
+ Sync
+ 'static {
const NULL_IS_VALUE: bool = false;
const IS_OUTPUT: bool = false;
// Required methods
fn compile(raw: &Value, cx: &mut CompileCx<'_>) -> Option<Self>;
fn resolve(&self, r: &mut dyn Resolver) -> Result<Option<OwnedDataValue>>;
fn kind() -> FieldKind;
// Provided methods
fn absent() -> Option<Self> { ... }
fn visit(&self, _v: &mut Visit<'_>) { ... }
}Expand description
A type a config is made of.
An Option<T> field is optional: an absent key is None, and so is an
explicit null, unless T takes null as a value
(NULL_IS_VALUE), as an Expr, an
Interpolated document and any JSON value do. There the null is
compiled as T: map’s "logic": null is a constant null expression,
not a missing logic.
A host writes its own field type by implementing it: here a connector’s
name, a newtype over an Expr<String>, compiled, walked and resolved as
the expression is, so a derived config holding one checks its secrets
and template keys, and resolves it, as it would the expression.
use dataflow_rs::datavalue::OwnedDataValue;
use dataflow_rs::expr::{CompileCx, ConfigField, FieldKind, Resolver, ValueKind, Visit};
use dataflow_rs::function::{FunctionConfig, testing};
use dataflow_rs::{Ctx, Expr, Function, Message, Outcome, Result};
use serde_json::{Value, json};
/// The connector a call reaches, by name.
struct Connector(Expr<String>);
impl ConfigField for Connector {
fn compile(raw: &Value, cx: &mut CompileCx<'_>) -> Option<Self> {
Expr::<String>::compile(raw, cx).map(Self)
}
fn visit(&self, v: &mut Visit<'_>) {
v.expr(self.0.raw());
}
fn resolve(&self, r: &mut dyn Resolver) -> Result<Option<OwnedDataValue>> {
r.eval(&self.0).map(Some)
}
fn kind() -> FieldKind {
FieldKind::Expr(ValueKind::String)
}
}
#[derive(FunctionConfig)]
struct Query {
connector: Connector,
limit: Option<Expr<u64>>,
}
/// Writes its input, as a layer or a dry run reads it, at `data.input`.
struct Echo;
impl Function for Echo {
type Config = Query;
async fn call(&self, ctx: &mut Ctx<'_>, _cfg: &Query) -> Result<Outcome> {
let input = ctx.input()?;
ctx.set(&"data.input".parse()?, input)?;
Ok(Outcome::Ok)
}
}
let input = json!({"connector": {"cat": ["db-", {"var": "data.region"}]}});
let data = OwnedDataValue::from(&json!({"region": "eu"}));
let out = testing::call(Echo, input)
.message(Message::from_parts(OwnedDataValue::Null, data, OwnedDataValue::Null))
.await;
assert_eq!(out.outcome.unwrap(), Outcome::Ok);
// The field resolved at its key; the `Option` left unset is absent.
let at = "data.input".parse().unwrap();
let written = out.message.context().get(&at).unwrap();
assert_eq!(serde_json::to_string(written).unwrap(), r#"{"connector":"db-eu"}"#);Provided Associated Constants§
Sourceconst NULL_IS_VALUE: bool = false
const NULL_IS_VALUE: bool = false
Whether null is one of the type’s values. When it is not, an explicit
null in an Option<Self> field means None.
Sourceconst IS_OUTPUT: bool = false
const IS_OUTPUT: bool = false
Whether the field is an output: a destination its call writes to.
false unless the type is Output or an
Option of one. The derive reads a field typed Output or marked
#[output] as an output and asserts that this is true, so a type
that merely shares the name is refused at compile time.
Required Methods§
Sourcefn compile(raw: &Value, cx: &mut CompileCx<'_>) -> Option<Self>
fn compile(raw: &Value, cx: &mut CompileCx<'_>) -> Option<Self>
Compiles raw; None after recording at least one issue.
Sourcefn resolve(&self, r: &mut dyn Resolver) -> Result<Option<OwnedDataValue>>
fn resolve(&self, r: &mut dyn Resolver) -> Result<Option<OwnedDataValue>>
The field’s value for this call, resolved through r, or None when
it has none (an Option the input left unset, with no default). The
value sits where Self::visit reports the field’s expressions: a
list’s elements at their indices, a map’s values at their keys, and
an element left unset is null, keeping its place.
§Errors
The resolver’s, for the first expression or document that fails.
Provided Methods§
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".
Implementations on Foreign Types§
Source§impl ConfigField for OwnedDataValue
impl ConfigField for OwnedDataValue
Source§impl ConfigField for String
impl ConfigField for String
Source§impl ConfigField for Value
impl ConfigField for Value
Source§impl ConfigField for bool
impl ConfigField for bool
Source§impl ConfigField for f64
A float, written as one: 2.0 stays 2.0, as the JSON bridge keeps a
float literal, where OwnedDataValue’s From<f64> would make it the
integer 2.
impl ConfigField for f64
A float, written as one: 2.0 stays 2.0, as the JSON bridge keeps a
float literal, where OwnedDataValue’s From<f64> would make it the
integer 2.
Source§impl ConfigField for i32
impl ConfigField for i32
Source§impl ConfigField for i64
impl ConfigField for i64
Source§impl ConfigField for u8
impl ConfigField for u8
Source§impl ConfigField for u16
impl ConfigField for u16
Source§impl ConfigField for u32
impl ConfigField for u32
Source§impl ConfigField for u64
impl ConfigField for u64
Source§impl ConfigField for usize
impl ConfigField for usize
Source§impl<T: ConfigField> ConfigField for BTreeMap<String, T>
impl<T: ConfigField> ConfigField for BTreeMap<String, T>
Source§impl<T: ConfigField> ConfigField for Option<T>
impl<T: ConfigField> ConfigField for Option<T>
Source§const NULL_IS_VALUE: bool = true
const NULL_IS_VALUE: bool = true
null is None, or T’s own null.