use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use superposition_types::{
database::models::cac::{DependencyGraph, DimensionType},
Cac, Condition, DefaultConfigsWithSchema, DetailedConfig, DimensionInfo, ExtendedMap,
Overrides,
};
use crate::format::{ConfigFormat, FormatError, MarkupFormat};
fn dim_type_default() -> String {
DimensionType::default().to_string()
}
#[derive(Serialize, Deserialize)]
struct DimensionInfoJson {
position: i32,
schema: Value,
#[serde(rename = "type", default = "dim_type_default")]
dimension_type: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
description: Option<String>,
}
impl TryFrom<DimensionInfo> for DimensionInfoJson {
type Error = FormatError;
fn try_from(d: DimensionInfo) -> Result<Self, Self::Error> {
Ok(Self {
position: d.position,
schema: Value::from(&d.schema),
dimension_type: d.dimension_type.to_string(),
description: Some(d.description),
})
}
}
impl TryFrom<DimensionInfoJson> for DimensionInfo {
type Error = FormatError;
fn try_from(d: DimensionInfoJson) -> Result<Self, Self::Error> {
let schema_map = match d.schema {
Value::Object(map) => map,
_ => {
return Err(JsonFormat::conversion_error("Schema must be an object"));
}
};
Ok(Self {
position: d.position,
schema: ExtendedMap::from(schema_map),
dimension_type: DimensionType::from_str(&d.dimension_type)
.map_err(JsonFormat::conversion_error)?,
dependency_graph: DependencyGraph(HashMap::new()),
value_compute_function_name: None,
description: d.description.unwrap_or_default(),
})
}
}
use std::str::FromStr;
#[derive(Serialize, Deserialize)]
struct JsonConfig {
#[serde(rename = "default-configs")]
default_configs: DefaultConfigsWithSchema,
dimensions: HashMap<String, DimensionInfoJson>,
#[serde(default)]
overrides: Vec<JsonContext>,
}
#[derive(Serialize, Deserialize)]
struct JsonContext {
#[serde(rename = "_context_")]
context: Map<String, Value>,
#[serde(flatten)]
overrides: Map<String, Value>,
}
pub struct JsonFormat;
impl TryFrom<JsonConfig> for DetailedConfig {
type Error = FormatError;
fn try_from(json_config: JsonConfig) -> Result<Self, Self::Error> {
let dimensions: HashMap<String, DimensionInfo> = json_config
.dimensions
.into_iter()
.map(|(k, v)| {
let mut dim_info = DimensionInfo::try_from(v)?;
if dim_info.description.trim().is_empty() {
dim_info.description = k.clone();
}
Ok((k, dim_info))
})
.collect::<Result<_, FormatError>>()?;
let mut default_configs = json_config.default_configs;
for (k, info) in default_configs.iter_mut() {
if info.description.trim().is_empty() {
info.description = k.clone();
}
}
JsonFormat::try_into_detailed(
default_configs,
dimensions,
json_config.overrides,
|ctx| {
let condition = Cac::<Condition>::try_from(ctx.context)
.map(|cac| cac.into_inner())
.map_err(|e| {
JsonFormat::conversion_error(format!("Invalid condition: {}", e))
})?;
let override_vals = Cac::<Overrides>::try_from(ctx.overrides)
.map(|cac| cac.into_inner())
.map_err(|e| {
JsonFormat::conversion_error(format!("Invalid overrides: {}", e))
})?;
Ok((condition, override_vals))
},
)
}
}
impl TryFrom<DetailedConfig> for JsonConfig {
type Error = FormatError;
fn try_from(detailed_config: DetailedConfig) -> Result<Self, Self::Error> {
let dimensions: HashMap<String, DimensionInfoJson> = detailed_config
.dimensions
.iter()
.map(|(k, v)| {
let mut dim = DimensionInfoJson::try_from(v.clone())?;
if dim.description.as_ref().is_none_or(|d| d.trim().is_empty()) {
dim.description = Some(k.clone());
}
Ok((k.clone(), dim))
})
.collect::<Result<_, FormatError>>()?;
let overrides = detailed_config
.contexts
.into_iter()
.map(|ctx| {
let override_key = ctx.override_with_keys.get_key();
let overrides = detailed_config
.overrides
.get(override_key)
.cloned()
.unwrap_or_default();
let condition_value =
serde_json::to_value(&ctx.condition).unwrap_or_default();
let context_map = match condition_value {
Value::Object(map) => map,
_ => Map::new(),
};
JsonContext {
context: context_map,
overrides: overrides.into(),
}
})
.collect();
let mut default_configs = detailed_config.default_configs;
for (k, info) in default_configs.iter_mut() {
if info.description.trim().is_empty() {
info.description = k.clone();
}
}
Ok(Self {
default_configs,
dimensions,
overrides,
})
}
}
impl ConfigFormat for JsonFormat {
fn parse_into_detailed(input: &str) -> Result<DetailedConfig, FormatError> {
let json_config: JsonConfig = serde_json::from_str(input)
.map_err(|e| Self::syntax_error(e.to_string(), None))?;
DetailedConfig::try_from(json_config)
}
fn serialize(detailed_config: DetailedConfig) -> Result<String, FormatError> {
let json_config = JsonConfig::try_from(detailed_config)?;
serde_json::to_string_pretty(&json_config)
.map_err(|e| Self::serialization_error(e.to_string()))
}
fn format_name() -> MarkupFormat {
MarkupFormat::Json
}
}