//! Lenient, deserialize-only model of a Grafana panel, for the panel→Rust //! transpiler (`codegen`). //! //! A real Grafana export is far wider than what we emit: `unit` may be absent, //! `options.tooltip.sort` may be `"none"`, overrides carry property types we //! don't model, and there are dozens of fields we ignore. So this model is //! deliberately separate from the strict `schema` (sized for *output*): it //! captures only what codegen reads, makes every field optional, and lets serde //! drop everything else — including the whole `options` block, which codegen //! reconstructs from the panel type rather than reading. use serde::Deserialize; use serde_json::Value; use crate::schema::{ AxisPlacement, Color, GradientMode, LineInterpolation, PanelType, ShowPoints, StackingMode, }; #[derive(Deserialize)] pub struct PanelInput { #[serde(rename = "type")] pub panel_type: PanelType, #[serde(default)] pub title: String, #[serde(rename = "gridPos", default)] pub grid_pos: GridPos, #[serde(rename = "fieldConfig", default)] pub field_config: FieldConfig, #[serde(default)] pub targets: Vec, } #[derive(Deserialize, Default)] pub struct GridPos { #[serde(default)] pub w: u32, } #[derive(Deserialize, Default)] pub struct FieldConfig { #[serde(default)] pub defaults: Defaults, #[serde(default)] pub overrides: Vec, } #[derive(Deserialize, Default)] pub struct Defaults { #[serde(default)] pub unit: Option, #[serde(default)] pub decimals: Option, #[serde(default)] pub color: Option, #[serde(default)] pub custom: Option, } #[derive(Deserialize, Default)] #[serde(rename_all = "camelCase")] pub struct Custom { #[serde(default)] pub span_nulls: Option, #[serde(default)] pub line_interpolation: Option, #[serde(default)] pub show_points: Option, #[serde(default)] pub gradient_mode: Option, #[serde(default)] pub stacking: Option, #[serde(default)] pub axis_placement: Option, #[serde(default)] pub axis_label: Option, } #[derive(Deserialize, Default)] pub struct Stacking { #[serde(default)] pub mode: Option, } #[derive(Deserialize)] pub struct Override { #[serde(default)] pub matcher: Matcher, #[serde(default)] pub properties: Vec, } #[derive(Deserialize, Default)] pub struct Matcher { #[serde(default)] pub id: String, #[serde(default)] pub options: Value, } impl Matcher { /// The series name for a `byName` matcher; `None` for matcher kinds the /// builder can't express (which the caller skips). pub fn by_name(&self) -> Option<&str> { if self.id == "byName" { self.options.as_str() } else { None } } } /// One override property. `id`/`value` are kept raw so unknown kinds are simply /// ignored by codegen rather than failing the whole parse. #[derive(Deserialize)] pub struct Property { #[serde(default)] pub id: String, #[serde(default)] pub value: Value, } #[derive(Deserialize, Default)] pub struct Target { #[serde(default)] pub expr: String, #[serde(rename = "legendFormat", default)] pub legend: String, }