Initialize project scaffold and domain model
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MappingCondition {
|
||||
pub source: String,
|
||||
pub equals: Value,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum TransformKind {
|
||||
Identity,
|
||||
ToString,
|
||||
ToNumber,
|
||||
ToBoolean,
|
||||
Join,
|
||||
Split,
|
||||
WrapArray,
|
||||
UnwrapSingleton,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Transform {
|
||||
pub kind: TransformKind,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub struct MappingRule {
|
||||
pub source: String,
|
||||
pub target: String,
|
||||
#[serde(default)]
|
||||
pub required: bool,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default_value: Option<Value>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub transform: Option<Transform>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub condition: Option<MappingCondition>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub notes: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub struct MappingSet {
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub rules: Vec<MappingRule>,
|
||||
}
|
||||
|
||||
impl MappingSet {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.rules.is_empty()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.rules.len()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::{MappingRule, MappingSet, Transform, TransformKind};
|
||||
|
||||
#[test]
|
||||
fn mapping_set_reports_non_empty_rules() {
|
||||
let mapping = MappingSet {
|
||||
rules: vec![MappingRule {
|
||||
source: "$.mcp.email".to_owned(),
|
||||
target: "$.request.body.contact.email".to_owned(),
|
||||
required: true,
|
||||
default_value: None,
|
||||
transform: Some(Transform {
|
||||
kind: TransformKind::Identity,
|
||||
}),
|
||||
condition: None,
|
||||
notes: None,
|
||||
}],
|
||||
};
|
||||
|
||||
assert!(!mapping.is_empty());
|
||||
assert_eq!(mapping.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_rule_serializes_jsonpath_fields() {
|
||||
let mapping = MappingSet {
|
||||
rules: vec![MappingRule {
|
||||
source: "$.response.body.id".to_owned(),
|
||||
target: "$.output.id".to_owned(),
|
||||
required: false,
|
||||
default_value: Some(json!("lead_123")),
|
||||
transform: None,
|
||||
condition: None,
|
||||
notes: Some("map identifier".to_owned()),
|
||||
}],
|
||||
};
|
||||
|
||||
let value = serde_json::to_value(mapping).unwrap();
|
||||
|
||||
assert_eq!(value["rules"][0]["source"], "$.response.body.id");
|
||||
assert_eq!(value["rules"][0]["target"], "$.output.id");
|
||||
assert_eq!(value["rules"][0]["default_value"], "lead_123");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn mapping_set_roundtrips_through_yaml() {
|
||||
let mapping = MappingSet {
|
||||
rules: vec![MappingRule {
|
||||
source: "$.mcp.tags".to_owned(),
|
||||
target: "$.request.body.tags".to_owned(),
|
||||
required: false,
|
||||
default_value: None,
|
||||
transform: Some(Transform {
|
||||
kind: TransformKind::WrapArray,
|
||||
}),
|
||||
condition: None,
|
||||
notes: Some("normalize tags".to_owned()),
|
||||
}],
|
||||
};
|
||||
|
||||
let yaml = serde_yaml::to_string(&mapping).unwrap();
|
||||
let restored: MappingSet = serde_yaml::from_str(&yaml).unwrap();
|
||||
|
||||
assert!(yaml.contains("source: $.mcp.tags"));
|
||||
assert_eq!(restored, mapping);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user