use serde::{Deserialize, Serialize};
use serde_json::Value;
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct PatchOperation {
#[serde(skip_serializing_if = "Option::is_none")]
pub op: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<Value>,
}
impl PatchOperation {
#[must_use]
pub fn add(path: impl Into<String>, value: impl Into<Value>) -> Self {
Self {
op: Some("add".to_owned()),
path: Some(path.into()),
value: Some(value.into()),
}
}
#[must_use]
pub fn remove(path: impl Into<String>) -> Self {
Self {
op: Some("remove".to_owned()),
path: Some(path.into()),
value: None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_operation_serde_roundtrip() {
let op = PatchOperation::add("/connectionPermissions/1", "READ");
let json = serde_json::to_string(&op).unwrap();
let deserialized: PatchOperation = serde_json::from_str(&json).unwrap();
assert_eq!(op, deserialized);
}
#[test]
fn remove_operation_serde_roundtrip() {
let op = PatchOperation::remove("/connectionPermissions/1");
let json = serde_json::to_string(&op).unwrap();
let deserialized: PatchOperation = serde_json::from_str(&json).unwrap();
assert_eq!(op, deserialized);
}
#[test]
fn remove_operation_omits_value() {
let op = PatchOperation::remove("/path");
let json = serde_json::to_value(&op).unwrap();
assert!(json.get("value").is_none());
}
#[test]
fn default_operation_serializes_empty() {
let op = PatchOperation::default();
let json = serde_json::to_value(&op).unwrap();
let obj = json.as_object().unwrap();
assert!(obj.is_empty());
}
#[test]
fn add_operation_field_values() {
let op = PatchOperation::add("/connectionPermissions/1", "READ");
assert_eq!(op.op.as_deref(), Some("add"));
assert_eq!(op.path.as_deref(), Some("/connectionPermissions/1"));
assert_eq!(op.value, Some(Value::String("READ".to_string())));
}
#[test]
fn add_operation_non_string_values() {
let bool_op = PatchOperation::add("/path", true);
assert_eq!(bool_op.value, Some(Value::Bool(true)));
let num_op = PatchOperation::add("/path", 42);
assert_eq!(num_op.value, Some(serde_json::json!(42)));
let obj_op = PatchOperation::add("/path", serde_json::json!({"key": "val"}));
assert!(obj_op.value.as_ref().unwrap().is_object());
}
#[test]
fn add_operation_array_and_null_values() {
let arr_op = PatchOperation::add("/path", serde_json::json!(["a", "b"]));
assert_eq!(arr_op.value, Some(serde_json::json!(["a", "b"])));
let null_op = PatchOperation::add("/path", serde_json::Value::Null);
assert_eq!(null_op.value, Some(serde_json::Value::Null));
}
#[test]
fn remove_operation_field_values() {
let op = PatchOperation::remove("/connectionPermissions/1");
assert_eq!(op.op.as_deref(), Some("remove"));
assert_eq!(op.path.as_deref(), Some("/connectionPermissions/1"));
assert!(op.value.is_none());
}
}