1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use super::doc::*;
use super::schema::{
Schema,
Track,
};
use failure::Error;
use std::collections::HashSet;
#[derive(Clone)]
pub struct ValidateContext<S: Schema> {
stack: Vec<S::GroupProperties>,
carets: HashSet<String>,
}
impl<S: Schema> ValidateContext<S> {
pub fn new() -> ValidateContext<S> {
ValidateContext {
stack: vec![],
carets: hashset![],
}
}
}
pub fn validate_doc_span<S: Schema>(
ctx: &mut ValidateContext<S>,
span: &DocSpan<S>,
) -> Result<(), Error> {
for elem in span {
match *elem {
DocGroup(ref attrs, ref span) => {
ctx.stack.push(attrs.clone());
validate_doc_span(ctx, span)?;
ctx.stack.pop();
if let Some(parent) = ctx.stack.last() {
let parent_type = S::track_type_from_attrs(parent).unwrap();
let cur_type = S::track_type_from_attrs(attrs).unwrap();
ensure!(
cur_type.parents().contains(&parent_type),
"Block has incorrect parent"
);
} else {
ensure!(
S::track_type_from_attrs(attrs).unwrap().allowed_in_root(),
"Root block has incorrect parent"
);
}
}
DocText(ref _styles, ref text) => {
ensure!(text.char_len() > 0, "Empty char string");
if let Some(block) = ctx.stack.last() {
ensure!(
S::track_type_from_attrs(block).unwrap().supports_text(),
"Char found outside block"
);
} else {
bail!("Found char in root");
}
}
}
}
Ok(())
}
pub fn validate_doc<S: Schema>(doc: &Doc<S>) -> Result<(), Error> {
let mut ctx = ValidateContext::new();
validate_doc_span(&mut ctx, &doc.0)
}