use anyhow::{Context, Result};
use serde_json::Value;
pub(super) fn request_key(id: &Value) -> String {
match id {
Value::String(value) => value.clone(),
_ => id.to_string(),
}
}
pub(super) fn optional_string(value: &Value, key: &str) -> Option<String> {
value
.get(key)
.and_then(Value::as_str)
.map(ToOwned::to_owned)
}
pub(super) fn normalize_optional_text(value: Option<String>) -> Option<String> {
value.and_then(|value| {
let trimmed = value.trim();
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
})
}
pub(super) fn normalize_name(name: Option<String>) -> Option<String> {
normalize_optional_text(name)
}
pub(super) fn normalize_note(note: Option<String>) -> Option<String> {
normalize_optional_text(note)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) enum OptionalTextUpdate {
Unchanged,
Clear,
Set(String),
}
pub(super) fn resolve_optional_text_update(
current: Option<&str>,
requested: Option<&str>,
) -> OptionalTextUpdate {
let current = normalize_optional_text(current.map(str::to_string));
match normalize_optional_text(requested.map(str::to_string)) {
Some(next) => {
if current.as_deref() == Some(next.as_str()) {
OptionalTextUpdate::Unchanged
} else {
OptionalTextUpdate::Set(next)
}
}
None if requested.is_none() => OptionalTextUpdate::Unchanged,
None if current.is_some() => OptionalTextUpdate::Clear,
None => OptionalTextUpdate::Unchanged,
}
}
pub(super) fn required_string<'a>(value: &'a Value, key: &str) -> Result<&'a str> {
value
.get(key)
.and_then(Value::as_str)
.with_context(|| format!("缺少字段 {key}"))
}