use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfigActionDef {
#[serde(rename = "action", default)]
pub action: String,
#[serde(rename = "label", default)]
pub label: String,
#[serde(rename = "description", default)]
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind", content = "binding")]
pub enum FieldAction {
#[serde(rename = "data")]
Data(DataActionBinding),
#[serde(rename = "effect")]
Effect(EffectActionBinding),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DataActionBinding {
#[serde(rename = "action", default)]
pub action: String,
#[serde(rename = "component", default)]
pub component: Option<String>,
#[serde(rename = "labelField", default)]
pub label_field: String,
#[serde(rename = "labelFieldLabel", default)]
pub label_field_label: String,
#[serde(rename = "valueField", default)]
pub value_field: String,
#[serde(rename = "mergeKey", default)]
pub merge_key: Option<String>,
#[serde(rename = "fieldMapping", default)]
pub field_mapping: Vec<(String, String)>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EffectActionBinding {
#[serde(rename = "action", default)]
pub action: String,
#[serde(rename = "component", default)]
pub component: Option<String>,
#[serde(rename = "fieldMapping", default)]
pub field_mapping: Vec<(String, String)>,
#[serde(rename = "transient", default)]
pub transient: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailActionDef {
#[serde(rename = "action", default)]
pub action: String,
#[serde(rename = "paramField", default)]
pub param_field: String,
#[serde(rename = "paramKey", default)]
pub param_key: String,
#[serde(rename = "previewItemKey", default)]
pub preview_item_key: String,
#[serde(rename = "previewItemLabel", default)]
pub preview_item_label: String,
#[serde(rename = "targetField", default)]
pub target_field: String,
#[serde(rename = "targetMatchKey", default)]
pub target_match_key: String,
}
#[cfg(test)]
mod tests {
use super::{DataActionBinding, EffectActionBinding, FieldAction};
#[test]
fn field_action_uses_discriminated_shape() {
let data = serde_json::to_value(FieldAction::Data(DataActionBinding {
action: "search_candidates".into(),
component: Some("candidate-registry".into()),
label_field: "name".into(),
label_field_label: "名称".into(),
value_field: "target".into(),
merge_key: None,
field_mapping: vec![("iconRequestJson".into(), "icon_request_json".into())],
}))
.expect("Data action must serialize");
assert_eq!(data["kind"], "data");
assert_eq!(data["binding"]["labelField"], "name");
assert_eq!(data["binding"]["labelFieldLabel"], "名称");
assert_eq!(data["binding"]["valueField"], "target");
assert!(data["binding"]["mergeKey"].is_null());
assert_eq!(data["binding"]["fieldMapping"][0][0], "iconRequestJson");
let effect = serde_json::to_value(FieldAction::Effect(EffectActionBinding {
action: "apply_override".into(),
component: None,
field_mapping: vec![("custom_icon_path".into(), "custom_icon_path".into())],
transient: true,
}))
.expect("Effect action must serialize");
assert_eq!(effect["kind"], "effect");
assert_eq!(effect["binding"]["transient"], true);
assert_eq!(effect["binding"]["fieldMapping"][0][1], "custom_icon_path");
}
}