Skip to main content

substrate_data/schema/convert/
read.rs

1//! A schema's value in: from a value, the binary form, text, a parsed document.
2
3use crate::binary::Decoder;
4use crate::diagnostic::{Diagnostic, Errors, Problem};
5use crate::model::borrowed::ValueRef;
6use crate::model::value::Value;
7use crate::schema::{Read, Reader, Resolve, Schema, Shape};
8use crate::text::{self, Doc, LineCol};
9
10/// The field names of a struct's shape (none for anything else).
11#[must_use]
12pub fn field_names(shape: &Shape) -> Vec<&'static str> {
13    match shape {
14        Shape::Struct(s) => s.fields.iter().map(|f| f.name).collect(),
15        _ => Vec::new(),
16    }
17}
18
19/// `A` and `B` read from the same object — each allowing the other's
20/// fields, like serde's `flatten`: a record's common fields and its kind's.
21pub fn read_both<A: Schema, B: Schema>(
22    v: &ValueRef<'_>,
23    cx: &mut Reader<'_>,
24    also: &[&'static str],
25) -> Option<(A, B)> {
26    let (an, bn) = (field_names(&A::shape()), field_names(&B::shape()));
27    let mut b_keys = bn.clone();
28    b_keys.extend_from_slice(also);
29    let mut a_keys = an;
30    a_keys.extend_from_slice(also);
31    let a = cx.with_allowed_all(&b_keys, |cx| A::read(v, cx));
32    let b = cx.with_allowed_all(&a_keys, |cx| B::read(v, cx));
33    Some((a?, b?))
34}
35
36/// Reads `v` as `T`: the value and its references, or every problem.
37///
38/// # Errors
39///
40/// Every problem reading it, with its path.
41pub fn from_value<T: Schema>(v: &Value) -> Result<Read<T>, Vec<Diagnostic>> {
42    let arena = bumpalo::Bump::new();
43    read_with(&v.borrow(&arena), Reader::new())
44}
45
46/// Reads `v` as `T`, nothing else: what a runtime wants of content (the
47/// references met aren't kept).
48///
49/// # Errors
50///
51/// Every problem reading it, with its path.
52pub fn read_value<T: Schema>(v: &Value) -> Result<T, Vec<Diagnostic>> {
53    let arena = bumpalo::Bump::new();
54    read_ref_value(&v.borrow(&arena))
55}
56
57/// [`read_value`] of a borrowed value.
58///
59/// # Errors
60///
61/// Every problem reading it, with its path.
62pub fn read_ref_value<T: Schema>(v: &ValueRef<'_>) -> Result<T, Vec<Diagnostic>> {
63    let reader = Reader {
64        value_only: true,
65        ..Reader::new()
66    };
67    read_with(v, reader).map(|r| r.value)
68}
69
70/// Reads the binary form as `T`, borrowed all the way: no string is
71/// copied but into `T` itself — content as the runtime takes it.
72///
73/// # Errors
74///
75/// The decoding error, or every problem reading it.
76pub fn read_bytes<T: Schema>(bytes: &[u8]) -> Result<T, Vec<Diagnostic>> {
77    // Straight, the usual case; the slow way says what's wrong.
78    if let Ok(mut d) = Decoder::open(bytes)
79        && let Some(v) = T::decode(&mut d)
80        && d.at_end()
81    {
82        return Ok(v);
83    }
84    let arena = bumpalo::Bump::new();
85    let v = crate::binary::decode_ref(bytes, &arena)
86        .map_err(|e| vec![Diagnostic::error(Problem::BadBinary, e)])?;
87    read_ref_value(&v)
88}
89
90/// [`read_bytes`] with `resolver` for the references: the binary form
91/// read through a value tree (the straight path has nowhere to ask), so
92/// references can be checked, rewritten and expanded
93/// ([`with::Expand`](crate::with::Expand)).
94///
95/// # Errors
96///
97/// The decoding error, or every problem reading it (the resolver's among them).
98pub fn from_bytes_with<T: Schema>(
99    bytes: &[u8],
100    resolver: &mut dyn Resolve,
101) -> Result<Read<T>, Vec<Diagnostic>> {
102    let arena = bumpalo::Bump::new();
103    let v = crate::binary::decode_ref(bytes, &arena)
104        .map_err(|e| vec![Diagnostic::error(Problem::BadBinary, e)])?;
105    read_with(&v, Reader::with_resolver(resolver))
106}
107
108/// `from_value`, references and texts going through `resolver`.
109///
110/// # Errors
111///
112/// Every problem reading it, with its path (the resolver's among them).
113pub fn from_value_with<T: Schema>(
114    v: &Value,
115    resolver: &mut dyn Resolve,
116) -> Result<Read<T>, Vec<Diagnostic>> {
117    let arena = bumpalo::Bump::new();
118    read_with(&v.borrow(&arena), Reader::with_resolver(resolver))
119}
120
121fn read_with<T: Schema>(v: &ValueRef<'_>, mut cx: Reader<'_>) -> Result<Read<T>, Vec<Diagnostic>> {
122    let value = T::read(v, &mut cx);
123    match value {
124        Some(value) if cx.problems.is_empty() => Ok(Read {
125            value,
126            refs: cx.refs,
127        }),
128        _ => Err(cx.problems),
129    }
130}
131
132/// Parses `text` and reads it as `T`; problems come with their line and column.
133///
134/// # Errors
135///
136/// [`Errors`]: the text's syntax error, or every problem reading it, placed.
137pub fn from_text<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
138    // The usual case, a text that's right: parsed straight to a borrowed
139    // value, no positions kept. Only a problem pays for them, by reading again
140    // the slow way, which says where.
141    let arena = bumpalo::Bump::new();
142    let v = text::parse_ref(text, &arena)?;
143    if let Ok(read) = read_with(&v, Reader::new()) {
144        return Ok(read);
145    }
146    let doc = text::parse(text)?;
147    from_doc(text, &doc)
148}
149
150/// Parses TOML `text` and reads it as `T`.
151///
152/// # Errors
153///
154/// [`Errors`]: the text's syntax error, or every problem reading it, placed.
155#[cfg(feature = "toml")]
156pub fn from_toml<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
157    let doc = crate::toml::parse(text)?;
158    from_doc(text, &doc)
159}
160
161/// Reads a parsed document as `T`; `text` is its source, for positions.
162///
163/// # Errors
164///
165/// [`Errors`]: every problem reading it, placed.
166pub fn from_doc<T: Schema>(text: &str, doc: &Doc) -> Result<Read<T>, Errors> {
167    locate(text, doc, from_value(&doc.value))
168}
169
170/// `from_doc`, references and texts going through `resolver`.
171///
172/// # Errors
173///
174/// [`Errors`]: every problem reading it, placed.
175pub fn from_doc_with<T: Schema>(
176    text: &str,
177    doc: &Doc,
178    resolver: &mut dyn Resolve,
179) -> Result<Read<T>, Errors> {
180    locate(text, doc, from_value_with(&doc.value, resolver))
181}
182
183fn locate<T>(
184    text: &str,
185    doc: &Doc,
186    read: Result<Read<T>, Vec<Diagnostic>>,
187) -> Result<Read<T>, Errors> {
188    read.map_err(|problems| {
189        let mut located: Vec<Diagnostic> =
190            problems.into_iter().map(|p| placed(text, doc, p)).collect();
191        // In file order, as an editor lists them.
192        located.sort_by_key(|d| (d.line, d.col));
193        Errors(located)
194    })
195}
196
197/// `d` (about a value of `doc`, parsed from `text`) at its line and column:
198/// its key's for one about the key (an unknown field).
199pub fn placed(text: &str, doc: &Doc, d: Diagnostic) -> Diagnostic {
200    let spans = doc.spans_at(&d.path);
201    let span = match (d.on_key, spans.key) {
202        (true, Some(key)) => key,
203        _ => spans.span,
204    };
205    let at = LineCol::of(text, span.start as usize);
206    d.at(at)
207}