mod artist;
mod axes;
pub mod command;
mod data;
mod edit;
mod error;
mod figure;
mod ids;
mod link;
pub mod overlay;
pub mod selection;
mod style;
mod text;
mod validate;
pub mod wire;
pub use artist::{
Artist, Contour, ContourPlacement, Grid, Levels, Line, Quiver, QuiverScale, Scatter,
ScatterColor, ScatterSize, Surface,
};
pub use axes::{
Axes, Axis, Cell, ColormapName, Legend, LegendLocation, Limits, Projection, Scale, View3d,
};
pub use data::NdArray;
pub use edit::{
Choice, Edit, EditError, Node, NodeKind, PathError, Property, PropertyPath, Transaction, Value,
ValueType, choices, properties, property_choices,
};
pub use error::{IrError, ProtobufError};
pub use figure::{
Figure, FigureSize, FontSetId, NodeIdAllocator, Parameter, Provenance, SCHEMA_VERSION,
TileLayout,
};
pub use ids::{DataId, NodeId};
pub use link::{AxisLink, Dimension};
pub use style::{Color, ColorSpec, DashStyle, LineStyle, MarkerShape, MarkerStyle};
pub use text::{Interpreter, Text};
pub use validate::{IssueKind, ValidationIssue, ValidationReport};
use std::path::PathBuf;
pub fn json_schema() -> serde_json::Value {
schemars::schema_for!(Figure).to_value()
}
pub fn transaction_json_schema() -> serde_json::Value {
schemars::schema_for!(Transaction).to_value()
}
pub fn json_schema_files() -> Vec<(PathBuf, String)> {
use serde_json::{Map, Value};
const FIGURE: &str = "figure";
const EDIT: &str = "edit";
fn module_of(name: &str) -> &'static str {
match name {
"NodeId" | "DataId" => "ids",
"PropertyPath" => EDIT,
_ => wire::schema::module_declaring(name)
.unwrap_or_else(|| panic!("the JSON Schema definition {name} has no module")),
}
}
fn relocate_refs(value: &mut Value, module: &str) {
match value {
Value::Object(map) => {
for (key, inner) in map.iter_mut() {
match (key.as_str(), inner) {
("$ref", Value::String(reference)) => {
if let Some(name) = reference.strip_prefix("#/$defs/") {
let owner = module_of(name);
if owner != module {
*reference = format!("{owner}.schema.json#/$defs/{name}");
}
}
}
(_, inner) => relocate_refs(inner, module),
}
}
}
Value::Array(items) => items
.iter_mut()
.for_each(|item| relocate_refs(item, module)),
_ => {}
}
}
fn split_definitions(root: &mut Value) -> Map<String, Value> {
let object = root
.as_object_mut()
.expect("the root of a JSON Schema is an object");
match object.remove("$defs") {
Some(Value::Object(definitions)) => definitions,
_ => Map::new(),
}
}
let mut roots = std::collections::BTreeMap::from([
(FIGURE, json_schema()),
(EDIT, transaction_json_schema()),
]);
let dialect = roots[FIGURE].get("$schema").cloned();
let mut definitions = Map::new();
for root in roots.values_mut() {
definitions.extend(split_definitions(root));
}
let mut modules: std::collections::BTreeMap<&'static str, Map<String, Value>> =
roots.keys().map(|module| (*module, Map::new())).collect();
for (name, mut definition) in definitions {
let module = module_of(&name);
relocate_refs(&mut definition, module);
modules.entry(module).or_default().insert(name, definition);
}
for (module, root) in roots.iter_mut() {
relocate_refs(root, module);
}
modules
.into_iter()
.map(|(module, definitions)| {
let mut document = roots.remove(module).unwrap_or_else(|| {
let mut document = Map::new();
if let Some(dialect) = &dialect {
document.insert("$schema".to_owned(), dialect.clone());
}
document.insert(
"title".to_owned(),
Value::String(format!("The definitions of the {module} module")),
);
Value::Object(document)
});
document
.as_object_mut()
.expect("a schema document is an object")
.insert("$defs".to_owned(), Value::Object(definitions));
let mut text =
serde_json::to_string_pretty(&document).expect("a JSON value always serialises");
text.push('\n');
(PathBuf::from(format!("{module}.schema.json")), text)
})
.collect()
}
pub fn proto_files() -> Vec<(PathBuf, String)> {
wire::schema::render_files()
}