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    let doc = text::parse(text)?;
139    from_doc(text, &doc)
140}
141
142/// Parses TOML `text` and reads it as `T`.
143///
144/// # Errors
145///
146/// [`Errors`]: the text's syntax error, or every problem reading it, placed.
147#[cfg(feature = "toml")]
148pub fn from_toml<T: Schema>(text: &str) -> Result<Read<T>, Errors> {
149    let doc = crate::toml::parse(text)?;
150    from_doc(text, &doc)
151}
152
153/// Reads a parsed document as `T`; `text` is its source, for positions.
154///
155/// # Errors
156///
157/// [`Errors`]: every problem reading it, placed.
158pub fn from_doc<T: Schema>(text: &str, doc: &Doc) -> Result<Read<T>, Errors> {
159    locate(text, doc, from_value(&doc.value))
160}
161
162/// `from_doc`, references and texts going through `resolver`.
163///
164/// # Errors
165///
166/// [`Errors`]: every problem reading it, placed.
167pub fn from_doc_with<T: Schema>(
168    text: &str,
169    doc: &Doc,
170    resolver: &mut dyn Resolve,
171) -> Result<Read<T>, Errors> {
172    locate(text, doc, from_value_with(&doc.value, resolver))
173}
174
175fn locate<T>(
176    text: &str,
177    doc: &Doc,
178    read: Result<Read<T>, Vec<Diagnostic>>,
179) -> Result<Read<T>, Errors> {
180    read.map_err(|problems| {
181        let mut located: Vec<Diagnostic> =
182            problems.into_iter().map(|p| placed(text, doc, p)).collect();
183        // In file order, as an editor lists them.
184        located.sort_by_key(|d| (d.line, d.col));
185        Errors(located)
186    })
187}
188
189/// `d` (about a value of `doc`, parsed from `text`) at its line and column:
190/// its key's for one about the key (an unknown field).
191pub fn placed(text: &str, doc: &Doc, d: Diagnostic) -> Diagnostic {
192    let spans = doc.spans_at(&d.path);
193    let span = match (d.on_key, spans.key) {
194        (true, Some(key)) => key,
195        _ => spans.span,
196    };
197    let at = LineCol::of(text, span.start as usize);
198    d.at(at)
199}