use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RuntimeValue {
Null,
Boolean(bool),
String(String),
Integer(i64),
Decimal(f64),
Binary(String),
List(Vec<RuntimeValue>),
Map(BTreeMap<String, RuntimeValue>),
}
impl RuntimeValue {
#[must_use]
pub fn is_null(&self) -> bool {
matches!(self, Self::Null)
}
#[must_use]
pub fn as_str(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s.as_str()),
_ => None,
}
}
#[must_use]
pub fn as_integer(&self) -> Option<i64> {
match self {
Self::Integer(v) => Some(*v),
Self::Decimal(v) => Some(*v as i64),
Self::String(s) => s.parse().ok(),
_ => None,
}
}
#[must_use]
pub fn as_decimal(&self) -> Option<f64> {
match self {
Self::Decimal(v) => Some(*v),
Self::Integer(v) => Some(*v as f64),
Self::String(s) => s.parse().ok(),
_ => None,
}
}
#[must_use]
pub fn as_bool(&self) -> Option<bool> {
match self {
Self::Boolean(v) => Some(*v),
_ => None,
}
}
}
pub type Row = BTreeMap<String, RuntimeValue>;
pub type Dataset = Vec<Row>;
pub type RuntimeInputs = BTreeMap<String, Dataset>;
pub type RuntimeOutputs = BTreeMap<String, Dataset>;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct QualifiedField {
pub interface_id: String,
pub field_name: String,
}
#[must_use]
pub fn parse_qualified_field(target: &str) -> Option<QualifiedField> {
parse_qualified_field_with_interfaces(target, &[])
}
#[must_use]
pub fn parse_qualified_field_with_interfaces(
target: &str,
interface_ids: &[String],
) -> Option<QualifiedField> {
let target = target.trim();
if target.is_empty() || !target.contains('.') {
return None;
}
let mut best: Option<(&str, usize)> = None;
for id in interface_ids {
let prefix = format!("{id}.");
if target.starts_with(&prefix) {
let len = id.len();
if best.map_or(true, |(_, bl)| len > bl) {
best = Some((id.as_str(), len));
}
}
}
if let Some((id, len)) = best {
let field_name = &target[len + 1..];
if field_name.is_empty() {
return None;
}
return Some(QualifiedField {
interface_id: id.to_string(),
field_name: field_name.to_string(),
});
}
let (interface_id, field_name) = target.split_once('.')?;
if interface_id.is_empty() || field_name.is_empty() {
return None;
}
Some(QualifiedField {
interface_id: interface_id.into(),
field_name: field_name.into(),
})
}