Skip to content

Commit 70629f7

Browse files
authored
Schema (#72)
1 parent 86f6f7d commit 70629f7

11 files changed

Lines changed: 121 additions & 49 deletions

File tree

document_tree/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ url = '2.1.0'
1818
serde = '1.0.104'
1919
serde_derive = '1.0.104'
2020
linearize = { version = "0.1.4", features = ["derive"] }
21+
schemars = "1.0.0"

document_tree/src/attribute_types.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@ use std::str::FromStr;
33
use anyhow::{Error, bail, format_err};
44
use linearize::Linearize;
55
use regex::Regex;
6+
use schemars::JsonSchema;
67
use serde_derive::Serialize;
78

89
use crate::url::Url;
910

10-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
11+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
1112
pub enum EnumeratedListType {
1213
Arabic,
1314
LowerAlpha,
@@ -16,7 +17,7 @@ pub enum EnumeratedListType {
1617
UpperRoman,
1718
}
1819

19-
#[derive(Linearize, Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
20+
#[derive(Clone, Copy, Linearize, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
2021
pub enum FootnoteType {
2122
Number,
2223
Symbol,
@@ -34,21 +35,21 @@ impl TryFrom<char> for FootnoteType {
3435
}
3536
}
3637

37-
#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
38+
#[derive(Clone, Copy, Default, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
3839
pub enum FixedSpace {
3940
Default,
4041
// yes, default really is not “Default”
4142
#[default]
4243
Preserve,
4344
}
4445

45-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
46+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
4647
pub enum AlignH {
4748
Left,
4849
Center,
4950
Right,
5051
}
51-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
52+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
5253
pub enum AlignHV {
5354
Top,
5455
Middle,
@@ -57,22 +58,22 @@ pub enum AlignHV {
5758
Center,
5859
Right,
5960
}
60-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
61+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
6162
pub enum AlignV {
6263
Top,
6364
Middle,
6465
Bottom,
6566
}
6667

67-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
68+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
6869
pub enum TableAlignH {
6970
Left,
7071
Right,
7172
Center,
7273
Justify,
7374
Char,
7475
}
75-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone, Copy)]
76+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
7677
pub enum TableBorder {
7778
Top,
7879
Bottom,
@@ -82,19 +83,21 @@ pub enum TableBorder {
8283
None,
8384
}
8485

85-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
86+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
8687
pub struct ID(pub String);
87-
#[derive(Debug, PartialEq, Eq, Hash, Serialize, Clone)]
88+
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
8889
pub struct NameToken(pub String);
8990

9091
// The table DTD has the cols attribute of tgroup as required, but having
9192
// TableGroupCols not implement Default would leave no possible implementation
9293
// for TableGroup::with_children.
93-
#[derive(Default, Debug, PartialEq, Eq, Hash, Serialize, Clone)]
94+
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, JsonSchema)]
9495
pub struct TableGroupCols(pub usize);
9596

9697
// no eq for f64
97-
#[derive(Debug, PartialEq, Serialize, Clone)]
98+
#[derive(Clone, Debug, PartialEq, Serialize, JsonSchema)]
99+
#[serde(tag = "unit", content = "value")]
100+
#[schemars(_unstable_ref_variants)]
98101
pub enum Measure {
99102
// https://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html#length-units
100103
Em(f64),
@@ -118,7 +121,7 @@ impl FromStr for AlignHV {
118121
"left" => A::Left,
119122
"center" => A::Center,
120123
"right" => A::Right,
121-
s => bail!("Invalid Alignment {}", s),
124+
s => bail!("Invalid Alignment {s}"),
122125
})
123126
}
124127
}

document_tree/src/element_categories.rs

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use std::fmt::{self, Debug, Formatter};
22

3+
use schemars::JsonSchema;
34
use serde_derive::Serialize;
45

56
#[allow(clippy::wildcard_imports)]
@@ -35,17 +36,26 @@ macro_rules! impl_into {
3536
}
3637

3738
macro_rules! synonymous_enum {
38-
( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident { $($entry:ident),+ $(,)* } ) => {
39-
synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($entry,)* });
39+
( $subcat:ident : $($supcat:ident),+ ; $midcat:ident : $supsupcat:ident {
40+
$($(#[$attr:meta])? $entry:ident),+ $(,)*
41+
} ) => {
42+
synonymous_enum!($subcat : $( $supcat ),+ , $midcat { $($(#[$attr])? $entry,)+ });
4043
$( impl_into!($midcat::$entry => $supsupcat); )+
4144
};
42-
( $subcat:ident : $($supcat:ident),+ { $($entry:ident),+ $(,)* } ) => {
43-
synonymous_enum!($subcat { $( $entry, )* });
45+
( $subcat:ident : $($supcat:ident),+ {
46+
$($(#[$attr:meta])? $entry:ident),+ $(,)*
47+
} ) => {
48+
synonymous_enum!($subcat { $($(#[$attr])? $entry,)* });
4449
cartesian!(impl_into, [ $( ($subcat::$entry) ),+ ], [ $($supcat),+ ]);
4550
};
46-
( $name:ident { $( $entry:ident ),+ $(,)* } ) => {
47-
#[derive(PartialEq,Serialize,Clone)]
51+
( $name:ident {
52+
$($(#[$attr:meta])? $entry:ident),+ $(,)*
53+
} ) => {
54+
#[derive(Clone, PartialEq, Serialize, JsonSchema)]
55+
#[serde(tag = "type")]
56+
#[schemars(_unstable_ref_variants)]
4857
pub enum $name { $(
58+
$(#[$attr])?
4959
$entry(Box<$entry>),
5060
)* }
5161

@@ -70,9 +80,12 @@ synonymous_enum!(StructuralSubElement {
7080
Subtitle,
7181
Decoration,
7282
Docinfo,
83+
#[serde(untagged)]
7384
SubStructure
7485
});
75-
synonymous_enum!(SubStructure: StructuralSubElement { Topic, Sidebar, Transition, Section, BodyElement });
86+
synonymous_enum!(SubStructure: StructuralSubElement {
87+
Topic, Sidebar, Transition, Section, #[serde(untagged)] BodyElement
88+
});
7689
synonymous_enum!(BodyElement: SubTopic, SubSidebar, SubBlockQuote, SubFootnote, SubFigure; SubStructure: StructuralSubElement {
7790
//Simple
7891
Paragraph, LiteralBlock, DoctestBlock, MathBlock, Rubric, SubstitutionDefinition, Comment, Pending, Target, Raw, Image,

document_tree/src/elements.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use schemars::JsonSchema;
12
use serde_derive::Serialize;
23
use std::path::PathBuf;
34

@@ -26,15 +27,15 @@ pub trait Element {
2627
fn classes_mut(&mut self) -> &mut Vec<String>;
2728
}
2829

29-
#[derive(Debug, Default, PartialEq, Serialize, Clone)]
30+
#[derive(Clone, Debug, Default, PartialEq, Serialize, JsonSchema)]
3031
pub struct CommonAttributes {
31-
#[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
32+
#[serde(default, skip_serializing_if = "CanBeEmpty::is_empty")]
3233
ids: Vec<ID>,
33-
#[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
34+
#[serde(default, skip_serializing_if = "CanBeEmpty::is_empty")]
3435
names: Vec<NameToken>,
35-
#[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
36+
#[serde(default, skip_serializing_if = "CanBeEmpty::is_empty")]
3637
source: Option<PathBuf>,
37-
#[serde(skip_serializing_if = "CanBeEmpty::is_empty")]
38+
#[serde(default, skip_serializing_if = "CanBeEmpty::is_empty")]
3839
classes: Vec<String>,
3940
//TODO: dupnames
4041
}
@@ -128,7 +129,7 @@ macro_rules! impl_new {(
128129
),* $(,)* }
129130
) => (
130131
$(#[$attr])*
131-
#[derive(Debug,PartialEq,Serialize,Clone)]
132+
#[derive(Clone, Debug, PartialEq, Serialize, JsonSchema)]
132133
pub struct $name { $(
133134
$(#[$fattr])* $field: $typ,
134135
)* }
@@ -181,6 +182,7 @@ macro_rules! impl_elem {
181182
pub struct $name {
182183
#[serde(flatten)]
183184
common: CommonAttributes,
185+
#[serde(default, skip_serializing_if = "CanBeEmpty::is_empty")]
184186
children: Vec<$childtype>,
185187
}
186188
);
@@ -195,6 +197,7 @@ macro_rules! impl_elem {
195197
common: CommonAttributes,
196198
#[serde(flatten)]
197199
extra: extra_attributes::$name,
200+
#[serde(default, skip_serializing_if = "CanBeEmpty::is_empty")]
198201
children: Vec<$childtype>,
199202
}
200203
);
@@ -208,7 +211,7 @@ macro_rules! impl_elems { ( $( ($($args:tt)*) )* ) => (
208211
$( impl_elem!($($args)*); )*
209212
)}
210213

211-
#[derive(Default, Debug, Serialize)]
214+
#[derive(Default, Debug, Serialize, JsonSchema)]
212215
pub struct Document {
213216
children: Vec<StructuralSubElement>,
214217
}

document_tree/src/extra_attributes.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use schemars::JsonSchema;
12
use serde_derive::Serialize;
23

34
use crate::attribute_types::{
@@ -16,7 +17,7 @@ pub trait ExtraAttributes<A> {
1617
macro_rules! impl_extra {
1718
( $name:ident { $( $(#[$pattr:meta])* $param:ident : $type:ty ),* $(,)* } ) => (
1819
impl_extra!(
19-
#[derive(Default,Debug,PartialEq,Serialize,Clone)]
20+
#[derive(Clone, Default, Debug, PartialEq, Serialize, JsonSchema)]
2021
$name { $( $(#[$pattr])* $param : $type, )* }
2122
);
2223
);
@@ -48,7 +49,7 @@ impl_extra!(Target {
4849
anonymous: bool,
4950
});
5051
impl_extra!(Raw { space: FixedSpace, format: Vec<NameToken> });
51-
impl_extra!(#[derive(Debug,PartialEq,Serialize,Clone)] Image {
52+
impl_extra!(#[derive(Clone, Debug, PartialEq, Serialize, JsonSchema)] Image {
5253
uri: Url,
5354
align: Option<AlignHV>,
5455
alt: Option<String>,

document_tree/src/url.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::fmt;
22
use std::str::FromStr;
33

4+
use schemars::JsonSchema;
45
use serde_derive::Serialize;
56
use url::{self, ParseError};
67

@@ -24,7 +25,7 @@ fn starts_with_scheme(input: &str) -> bool {
2425

2526
/// The string representation of a URL, either absolute or relative, that has
2627
/// been verified as a valid URL on construction.
27-
#[derive(Debug, PartialEq, Serialize, Clone)]
28+
#[derive(Clone, Debug, PartialEq, Serialize, JsonSchema)]
2829
#[serde(transparent)]
2930
pub struct Url(String);
3031

parser/src/conversion/block.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ where
189189
"scale" => image.extra_mut().scale = Some(parse_scale(&opt_val)?),
190190
"align" => image.extra_mut().align = Some(opt_val.parse()?),
191191
"target" => image.extra_mut().target = Some(opt_val.parse()?),
192-
name => bail!("Unknown Image option {}", name),
192+
name => bail!("Unknown Image option {name}"),
193193
}
194194
}
195195
Ok(image)

parser/src/pair_ext_parse.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ pub trait PairExt<R>
88
where
99
R: pest::RuleType,
1010
{
11-
fn parse<T, E>(&self) -> Result<T, Box<Error<R>>>
11+
fn parse<T, E>(&self) -> Result<T, Error<R>>
1212
where
1313
T: FromStr<Err = E>,
1414
E: ToString;
@@ -18,7 +18,7 @@ impl<R> PairExt<R> for Pair<'_, R>
1818
where
1919
R: pest::RuleType,
2020
{
21-
fn parse<T, E>(&self) -> Result<T, Box<Error<R>>>
21+
fn parse<T, E>(&self) -> Result<T, Error<R>>
2222
where
2323
T: FromStr<Err = E>,
2424
E: ToString,
@@ -29,13 +29,13 @@ where
2929
}
3030
}
3131

32-
pub(crate) fn to_parse_error<E, R>(span: Span, e: &E) -> Box<Error<R>>
32+
pub(crate) fn to_parse_error<E, R>(span: Span, e: &E) -> Error<R>
3333
where
3434
E: ToString,
3535
R: pest::RuleType,
3636
{
3737
let var: ErrorVariant<R> = ErrorVariant::CustomError {
3838
message: e.to_string(),
3939
};
40-
Box::new(Error::new_from_span(var, span))
40+
Error::new_from_span(var, span)
4141
}

renderer/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ document_tree = { path = '../document_tree', version = "0.4.2" }
1717
anyhow = '1.0.86'
1818
serde_json = '1.0.44'
1919
serde-xml-rs = '0.5'
20+
schemars = "1.0.0-alpha.17"
2021

2122
[dev-dependencies]
2223
rst_parser = { path = '../parser' }

renderer/src/lib.rs

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ mod html;
55
use std::io::Write;
66

77
use anyhow::{Error, anyhow};
8-
98
use document_tree::Document;
109

10+
pub use crate::html::render_html;
11+
pub use schemars::generate::SchemaSettings;
12+
1113
/// Render a document tree as JSON.
1214
///
1315
/// # Errors
@@ -20,6 +22,22 @@ where
2022
Ok(())
2123
}
2224

25+
#[expect(clippy::missing_panics_doc, reason = "infallible")]
26+
/// Render the JSON schema for [`document_tree::Document`].
27+
pub fn render_json_schema_document<W>(stream: W, settings: SchemaSettings, pretty: bool)
28+
where
29+
W: Write,
30+
{
31+
let generator = settings.into_generator();
32+
let schema = generator.into_root_schema_for::<document_tree::Document>();
33+
let w = if pretty {
34+
serde_json::to_writer_pretty
35+
} else {
36+
serde_json::to_writer
37+
};
38+
w(stream, &schema).unwrap();
39+
}
40+
2341
/// Render a document tree as XML.
2442
///
2543
/// # Errors
@@ -29,8 +47,6 @@ where
2947
W: Write,
3048
{
3149
serde_xml_rs::to_writer(stream, &document)
32-
.map_err(|e| anyhow!("Failed to serialize XML: {}", e))?;
50+
.map_err(|e| anyhow!("Failed to serialize XML: {e}"))?;
3351
Ok(())
3452
}
35-
36-
pub use html::render_html;

0 commit comments

Comments
 (0)