use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum ValueFormat {
#[default]
Raw,
#[serde(alias = "fmt")]
Pretty,
#[serde(alias = "full")]
Both,
}
impl std::str::FromStr for ValueFormat {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"raw" => Ok(ValueFormat::Raw),
"pretty" | "fmt" => Ok(ValueFormat::Pretty),
"both" | "full" => Ok(ValueFormat::Both),
_ => Err(()),
}
}
}
impl ValueFormat {
pub fn parse(s: &str) -> Option<Self> {
s.parse().ok()
}
pub fn as_str(&self) -> &'static str {
match self {
ValueFormat::Raw => "raw",
ValueFormat::Pretty => "pretty",
ValueFormat::Both => "both",
}
}
pub fn transform(&self, value: serde_json::Value) -> serde_json::Value {
match self {
ValueFormat::Both => value, _ => self.transform_recursive(value),
}
}
fn transform_recursive(&self, value: serde_json::Value) -> serde_json::Value {
use serde_json::Value;
match value {
Value::Object(map) => {
if self.is_formatted_value(&map) {
return self.extract_value(&map);
}
let transformed: serde_json::Map<String, Value> = map
.into_iter()
.map(|(k, v)| (k, self.transform_recursive(v)))
.collect();
Value::Object(transformed)
}
Value::Array(arr) => Value::Array(
arr.into_iter()
.map(|v| self.transform_recursive(v))
.collect(),
),
other => other,
}
}
fn is_formatted_value(&self, map: &serde_json::Map<String, serde_json::Value>) -> bool {
if !map.contains_key("raw") {
return false;
}
let known_keys = ["raw", "fmt", "longFmt"];
let unknown_keys = map
.keys()
.filter(|k| !known_keys.contains(&k.as_str()))
.count();
unknown_keys == 0
}
fn extract_value(&self, map: &serde_json::Map<String, serde_json::Value>) -> serde_json::Value {
match self {
ValueFormat::Raw => {
map.get("raw").cloned().unwrap_or(serde_json::Value::Null)
}
ValueFormat::Pretty => {
map.get("fmt")
.or_else(|| map.get("longFmt"))
.cloned()
.unwrap_or(serde_json::Value::Null)
}
ValueFormat::Both => {
serde_json::Value::Object(map.clone())
}
}
}
}