use schemars::JsonSchema;
use serde::Deserialize;
use serde::de::IgnoredAny;
use super::de::{Located, Loose, Map, Maybe, OneOf, UnknownKeys};
type JsonMap = serde_json::Map<String, serde_json::Value>;
pub type QueryItem = Located<Loose<OneOf<String, QueryEntry>>>;
#[derive(Default, Deserialize, JsonSchema)]
#[schemars(title = "DRE report", deny_unknown_fields)]
pub struct ReportFile {
#[schemars(length(min = 1))]
pub name: Option<Located<Loose<String>>>,
pub description: Option<Located<Loose<String>>>,
pub meta: Option<Located<Loose<JsonMap>>>,
pub tags: Option<Located<Loose<Vec<String>>>>,
#[serde(default)]
#[schemars(schema_with = "queries")]
pub queries: Option<Located<Loose<Vec<QueryItem>>>>,
#[serde(default)]
#[schemars(schema_with = "output_or_list")]
pub output: Option<IgnoredAny>,
pub profile: Option<Located<Loose<String>>>,
pub sets: Option<Located<Loose<Vec<SetItem>>>>,
pub default_set: Option<Located<Loose<String>>>,
pub vars: Option<Located<Loose<JsonMap>>>,
pub var_schema: Option<Located<Loose<crate::variables::Definitions>>>,
pub timezone: Option<Located<Loose<String>>>,
pub locale: Option<Located<Loose<String>>>,
#[serde(default)]
#[schemars(schema_with = "super::dependencies::plugins")]
pub plugins: Option<IgnoredAny>,
#[serde(default)]
#[schemars(schema_with = "super::project::sources_ref")]
pub sources: Option<IgnoredAny>,
#[schemars(skip)]
pub schedule: Option<Located<IgnoredAny>>,
#[serde(rename = "$unknown", default)]
#[schemars(skip)]
pub unknown: UnknownKeys,
}
#[derive(Deserialize, JsonSchema)]
#[schemars(rename = "queryEntry", deny_unknown_fields)]
pub struct QueryEntry {
#[schemars(required)]
pub query: Option<Loose<String>>,
pub profile: Option<Loose<String>>,
#[schemars(extend("default" = true))]
pub tab: Option<Loose<bool>>,
pub tab_name: Option<Loose<String>>,
#[schemars(regex(pattern = "^[A-Za-z]{1,3}[0-9]+$"))]
pub anchor: Option<Loose<String>>,
pub header: Option<Loose<bool>>,
#[serde(default)]
#[schemars(schema_with = "columns")]
pub columns: Option<serde_json::Value>,
pub autofit: Option<Loose<bool>>,
#[serde(default)]
#[schemars(schema_with = "sheet_style")]
pub style: Option<serde_json::Value>,
#[serde(rename = "$unknown", default)]
#[schemars(skip)]
pub unknown: UnknownKeys,
}
fn sheet_style(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "object",
"description": "How an xlsx tab looks. Cell keys apply to every data cell; `header` and `totals` take cell keys for those rows.",
"properties": {
"header": {"type": "object", "description": "Cell keys for the header row (default: bold)."},
"totals": {"type": "object", "description": "Cell keys for the totals row (default: bold, a thin top border)."},
"banded_rows": {"oneOf": [{"type": "string", "pattern": "^#?[0-9A-Fa-f]{6}$"}, {"const": false}], "description": "The fill of every other data row, `\"#RRGGBB\"`, or `false`."},
"borders": {"enum": ["none", "thin", "medium"], "description": "A border around every cell of the table."},
"bold": {"type": "boolean", "description": "Bold text in every data cell."},
"italic": {"type": "boolean", "description": "Italic text in every data cell."},
"underline": {"type": "boolean", "description": "Underlined text in every data cell."},
"font": {"type": "object", "description": "The font: `{name: Calibri, size: 11}`.", "properties": {"name": {"type": "string", "description": "The font's name."}, "size": {"type": "number", "description": "The size in points."}}, "additionalProperties": false},
"font_color": {"type": "string", "pattern": "^#?[0-9A-Fa-f]{6}$", "description": "The text colour, `\"#RRGGBB\"`."},
"fill": {"type": "string", "pattern": "^#?[0-9A-Fa-f]{6}$", "description": "The background colour, `\"#RRGGBB\"`."},
"align": {"enum": ["left", "center", "right"], "description": "Horizontal alignment."},
"border": {"enum": ["none", "thin", "medium"], "description": "A border around each data cell."}
},
"additionalProperties": false
})
}
fn columns(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "object",
"additionalProperties": {
"type": "object",
"description": "Settings for one column of an xlsx tab.",
"properties": {
"format": {"type": "string", "description": "The Excel number format of the column, e.g. `#,##0.00` or `dd/mm/yyyy`. See the xlsx column formats in the plugins reference."},
"formula": {"type": "string", "description": "An Excel formula for each row of this column; `{name}` stands for that column's cell on the same row, e.g. `=ROUND({qty}*{unit_price},2)`. The SQL selects a placeholder column where the formula goes."},
"total": {"type": "string", "description": "Puts a total under the column: one of `sum`, `count`, `average`, `min`, `max`, or a formula such as `=SUM({net:*})`."},
"style": {"type": "object", "description": "How the column's data cells look: `bold`, `italic`, `underline`, `font` (`{name, size}`), `font_color`, `fill` (`\"#RRGGBB\"`), `align` (`left`, `center`, `right`), `border` (`none`, `thin`, `medium`). Over the tab's and output's `style`."},
"width": {"oneOf": [{"const": "auto"}, {"type": "number", "exclusiveMinimum": 0, "maximum": 255}], "description": "The column's width: `auto` (sized from its content, at most 60 characters) or a number of characters. Over the tab's and output's `autofit`."}
},
"additionalProperties": false
},
"propertyNames": {"minLength": 1}
})
}
pub type SetItem = Located<Loose<OneOf<String, SetEntry>>>;
#[derive(Deserialize, JsonSchema)]
#[schemars(rename = "setEntry", deny_unknown_fields)]
pub struct SetEntry {
#[schemars(required)]
pub name: Option<Loose<String>>,
pub profile: Option<Located<Loose<String>>>,
#[serde(default)]
#[schemars(schema_with = "any_map")]
pub vars: Option<Loose<JsonMap>>,
pub exclude: Option<Located<Loose<Vec<String>>>>,
#[serde(default)]
#[schemars(schema_with = "set_queries")]
pub queries: Option<Located<Loose<Vec<SetQueryItem>>>>,
#[serde(default)]
#[schemars(schema_with = "tab_names")]
pub tab_names: Option<Loose<JsonMap>>,
#[serde(default)]
#[schemars(schema_with = "output_or_list")]
pub output: Option<serde_json::Value>,
pub locale: Option<Loose<String>>,
#[schemars(skip)]
pub schedule: Option<IgnoredAny>,
#[serde(rename = "$unknown", default)]
#[schemars(skip)]
pub unknown: UnknownKeys,
}
pub type SetQueryItem = Loose<OneOf<String, SetQuery>>;
#[derive(Deserialize)]
pub struct SetQuery {
pub query: Option<Loose<String>>,
pub tab: Option<Loose<bool>>,
pub tab_name: Option<Loose<String>>,
}
fn any_map(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({"type": "object", "additionalProperties": true})
}
fn tab_names(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({"type": "object", "additionalProperties": {"type": "string"}})
}
fn output_or_list(g: &mut schemars::SchemaGenerator) -> schemars::Schema {
let output = g.subschema_for::<Output>();
schemars::json_schema!({
"oneOf": [output, {"type": "array", "minItems": 1, "items": output}]
})
}
fn set_queries(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "array",
"items": {
"description": "A query of this Set, by name, or with its own settings.",
"oneOf": [
{"type": "string"},
{
"type": "object",
"description": "A query with settings.",
"properties": {
"query": {"type": "string", "description": "The query's name."},
"tab": {"type": "boolean", "description": "Whether the query makes a tab."},
"tab_name": {"type": "string", "description": "The tab name."}
},
"required": ["query"],
"additionalProperties": true
}
]
}
})
}
#[derive(Deserialize, JsonSchema)]
#[schemars(rename = "output", extend("additionalProperties" = true))]
pub struct Output {
#[schemars(regex(pattern = "^[A-Za-z_][A-Za-z0-9_]*$"))]
pub name: Option<Loose<String>>,
pub format: Option<Loose<String>>,
#[schemars(length(min = 1))]
pub queries: Option<Loose<Vec<String>>>,
#[serde(default)]
#[schemars(schema_with = "string_or_bool")]
pub when: Option<Loose<OneOf<String, bool>>>,
#[serde(default)]
#[schemars(schema_with = "destination_or_list")]
pub destination: Option<Loose<OneOf<Destination, Vec<Loose<Destination>>>>>,
pub template: Option<Loose<Template>>,
#[serde(default)]
#[schemars(schema_with = "extension")]
pub extension: Maybe<Loose<Option<OneOf<String, bool>>>>,
#[serde(rename = "$unknown", default)]
#[schemars(skip)]
pub options: Map<serde_json::Value>,
}
#[derive(Deserialize, JsonSchema)]
#[schemars(rename = "destination", extend("additionalProperties" = true))]
pub struct Destination {
#[schemars(required)]
pub profile: Option<Loose<String>>,
pub path: Option<Loose<String>>,
#[serde(default)]
#[schemars(schema_with = "attach")]
pub attach: Option<Loose<OneOf<String, Vec<String>>>>,
#[serde(rename = "$unknown", default)]
#[schemars(skip)]
pub options: Map<serde_json::Value>,
}
#[derive(Deserialize, JsonSchema)]
#[schemars(rename = "template", deny_unknown_fields)]
pub struct Template {
#[schemars(required)]
pub file: Option<Loose<String>>,
pub bindings: Option<Loose<Vec<Loose<TemplateBinding>>>>,
}
#[derive(Deserialize, JsonSchema)]
#[schemars(deny_unknown_fields, inline)]
pub struct TemplateBinding {
#[schemars(required)]
pub sheet: Option<Loose<String>>,
pub query: Option<Loose<String>>,
#[schemars(range(min = 1))]
pub result_index: Option<Loose<u64>>,
#[schemars(regex(pattern = "^[A-Za-z]{1,3}[0-9]+$"))]
pub anchor: Option<Loose<String>>,
pub header: Option<Loose<bool>>,
pub columns: Option<Loose<Vec<String>>>,
#[schemars(regex(pattern = "^[A-Za-z]{1,3}[0-9]+$"))]
pub cell: Option<Loose<String>>,
pub value: Option<Loose<String>>,
pub column: Option<Loose<String>>,
#[serde(rename = "$unknown", default)]
#[schemars(skip)]
pub unknown: UnknownKeys,
}
fn string_or_bool(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({"type": ["string", "boolean"]})
}
fn extension(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({"type": ["string", "boolean", "null"], "x-dre-null": true})
}
fn destination_or_list(g: &mut schemars::SchemaGenerator) -> schemars::Schema {
let destination = g.subschema_for::<Destination>();
schemars::json_schema!({
"oneOf": [destination, {"type": "array", "minItems": 1, "items": destination}]
})
}
fn attach(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"oneOf": [
{"type": "string"},
{"type": "array", "items": {"type": "string"}, "minItems": 1}
]
})
}
fn queries(g: &mut schemars::SchemaGenerator) -> schemars::Schema {
let entry = g.subschema_for::<QueryEntry>();
schemars::json_schema!({
"type": "array",
"items": {
"oneOf": [
{"type": "string", "description": "The name of a `.sql` file under `reports/`, without extension."},
entry
]
}
})
}