use anyhow::Result;
use minijinja::value::StructObject;
use minijinja::Value;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct Document<T: Serialize> {
pub meta: Metadata,
pub content: T,
pub code_outputs: HashMap<String, CodeOutput>,
pub data: HashMap<String, Value>,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Metadata {
pub title: String,
#[serde(default)]
pub draft: bool,
#[serde(default = "default_true")]
pub exercises: bool,
#[serde(default)]
pub code_solutions: Option<bool>,
#[serde(default = "default_true")]
pub cell_outputs: bool,
#[serde(default)]
pub interactive: bool,
#[serde(default)]
pub editable: bool,
#[serde(default)]
pub layout: LayoutSettings,
#[serde(default)]
pub exclude_outputs: Option<Vec<String>>,
#[serde(default)]
pub user: HashMap<String, serde_json::Value>,
}
impl<T: Send + Sync + Serialize> StructObject for Document<T> {
fn get_field(&self, name: &str) -> Option<Value> {
match name {
"meta" => Some(Value::from_struct_object(self.meta.clone())),
s => self.data.get(s).cloned(),
}
}
}
impl StructObject for Metadata {
fn get_field(&self, name: &str) -> Option<Value> {
match name {
"title" => Some(Value::from(self.title.as_str())),
"draft" => Some(Value::from(self.draft)),
"exercises" => Some(Value::from(self.exercises)),
"code_solutions" => self.code_solutions.map(|s| Value::from(s)),
"interactive" => Some(Value::from(self.interactive)),
"editable" => Some(Value::from(self.editable)),
"layout" => Some(Value::from_serializable(&self.layout)),
"exclude_outputs" => self
.exclude_outputs
.as_ref()
.map(|ex| Value::from_serializable(ex)),
s => self.user.get(s).map(Value::from_serializable),
}
}
}
const fn default_true() -> bool {
true
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct LayoutSettings {
pub hide_sidebar: bool,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize, Default)]
pub struct CodeOutput {
pub values: Vec<OutputValue>,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum OutputValue {
Plain(String),
Text(String),
Image(Image),
Json(serde_json::Value),
Html(String),
Javascript(String),
Error(String),
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub enum Image {
Png(String),
Svg(String),
}
impl<T: Serialize> Document<T> {
pub fn map<O: Serialize, F: Fn(T) -> O>(self, f: F) -> Document<O> {
Document {
content: f(self.content),
meta: self.meta,
code_outputs: self.code_outputs,
data: HashMap::new(),
}
}
pub fn try_map<O: Serialize, F: Fn(T) -> Result<O>>(self, f: F) -> Result<Document<O>> {
Ok(Document {
content: f(self.content)?,
meta: self.meta,
code_outputs: self.code_outputs,
data: HashMap::new(),
})
}
}