Skip to content

Instantly share code, notes, and snippets.

@tobz
Created April 14, 2023 18:53
Show Gist options
  • Save tobz/d6385fdbf667d4e653c717012470e7ee to your computer and use it in GitHub Desktop.
Save tobz/d6385fdbf667d4e653c717012470e7ee to your computer and use it in GitHub Desktop.
with defaults: Some(Array [String("*")])
with multiple_outputs=true: Some(Array [String("logs"), String("metrics"), String("traces")])
with multiple_outputs=true, disable_traces=true: Some(Array [String("logs"), String("metrics")])
use serde_json::Value;
pub enum Computed {
Fixed(Value),
Optional { condition: Condition, value: Box<Computed> },
Array(Vec<Computed>),
IfElse { if_condition: Condition, if_value: Box<Computed>, else_value: Box<Computed> },
}
impl Computed {
pub fn if_else<IV, EV>(if_condition: Condition, if_value: IV, else_value: EV) -> Self
where
IV: Into<Computed>,
EV: Into<Computed>,
{
Self::IfElse { if_condition, if_value: Box::new(if_value.into()), else_value: Box::new(else_value.into()) }
}
pub fn array<I, T>(items: I) -> Self
where
I: IntoIterator<Item = T>,
T: Into<Computed>,
{
Self::Array(items.into_iter().map(Into::into).collect())
}
pub fn optional<V>(condition: Condition, value: V) -> Self
where
V: Into<Computed>,
{
Self::Optional { condition, value: Box::new(value.into()) }
}
pub fn compute(&self, input: &Value) -> Option<Value> {
match self {
Computed::Fixed(v) => Some(v.clone()).and_then(nonnull_or_none),
Computed::Optional { condition, value } => condition.check(input).then_some(value.compute(input)).flatten(),
Computed::Array(items) => Some(items.into_iter().filter_map(|c| c.compute(input)).collect()),
Computed::IfElse { if_condition, if_value, else_value } => {
if_condition.check(input)
.then(|| if_value.compute(input))
.flatten()
.or_else(|| else_value.compute(input))
}
}
}
}
impl<T> From<T> for Computed
where
T: Into<Value>,
{
fn from(value: T) -> Self {
Computed::Fixed(value.into())
}
}
pub struct Condition {
target: &'static str,
operator: Operator,
operand: Value,
}
impl Condition {
pub fn eq<O>(target: &'static str, operand: O) -> Self
where
O: Into<Value>,
{
Self {
target,
operator: Operator::Eq,
operand: operand.into(),
}
}
pub fn ne<O>(target: &'static str, operand: O) -> Self
where
O: Into<Value>,
{
Self {
target,
operator: Operator::Ne,
operand: operand.into(),
}
}
pub fn check(&self, input: &Value) -> bool {
let target_value = input.pointer(self.target).unwrap_or(&Value::Null);
match self.operator {
Operator::Eq => target_value == &self.operand,
Operator::Ne => target_value != &self.operand,
}
}
}
pub enum Operator {
Eq,
Ne,
}
fn nonnull_or_none(value: Value) -> Option<Value> {
match value {
Value::Null => None,
v => Some(v),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn connectability_datadog_agent_outputs() {
let constraint = Computed::if_else(
Condition::eq("/multiple_outputs", true),
Computed::array([
Computed::optional(Condition::ne("/disable_logs", true), "logs"),
Computed::optional(Condition::ne("/disable_metrics", true), "metrics"),
Computed::optional(Condition::ne("/disable_traces", true), "traces"),
]),
Computed::array(["*"]),
);
let defaults = json!({});
println!("with defaults: {:?}", constraint.compute(&defaults));
let multiple_outputs = json!({
"multiple_outputs": true
});
println!("with multiple_outputs=true: {:?}", constraint.compute(&multiple_outputs));
let multiple_outputs_no_traces = json!({
"multiple_outputs": true,
"disable_traces": true
});
println!("with multiple_outputs=true, disable_traces=true: {:?}", constraint.compute(&multiple_outputs_no_traces));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment