Skip to main content

substrate_data/schema/plain/
text.rs

1//! The schemas of text: a leading `@` doubled so it reads back as text.
2
3use crate::binary::{Decoder, Writer};
4use crate::frame::{Packer, Unpacker};
5use crate::model::borrowed::ValueRef;
6use crate::model::value::Value;
7use crate::schema::{Expected, Reader, Schema, Shape};
8use crate::typed;
9
10/// What a typed value's string starts with (`@file:…`).
11fn typed_mark() -> char {
12    crate::names::TYPED
13}
14
15/// Plain text as a value: a leading `@` doubled, so it reads back as text
16/// and not as a typed value.
17fn plain(s: &str) -> Value {
18    Value::Str(typed::escape_at(s))
19}
20
21/// Plain text written straight, its leading `@` doubled like [`plain`]'s.
22fn put_plain(w: &mut Writer, s: &str) {
23    if s.starts_with(typed_mark()) {
24        w.str(&typed::escape_at(s));
25    } else {
26        w.str(s);
27    }
28}
29
30/// Plain text written as text, its leading `@` doubled like [`plain`]'s.
31fn put_plain_text(w: &mut crate::text::Writer, s: &str) {
32    if s.starts_with(typed_mark()) {
33        w.str(&typed::escape_at(s));
34    } else {
35        w.str(s);
36    }
37}
38
39impl Schema for String {
40    fn shape() -> Shape {
41        Shape::Str
42    }
43    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
44        match v {
45            // Plain text, the usual case: copied once, into the result.
46            ValueRef::Str(s) if !s.starts_with('@') => Some(s.to_string()),
47            ValueRef::Str(s) => match typed::classify(s) {
48                Ok(typed::Spelled::Plain(s)) => Some(s),
49                Ok(other) => {
50                    cx.problem(format!(
51                        "expected plain text, found {} (write `@@` for a literal `@`)",
52                        other.kind()
53                    ));
54                    None
55                }
56                Err(e) => {
57                    cx.problem_of(crate::diagnostic::Problem::BadTyped, e.to_string());
58                    None
59                }
60            },
61            other => {
62                cx.expected(Expected::Str, other);
63                None
64            }
65        }
66    }
67    fn write(&self) -> Value {
68        plain(self)
69    }
70    fn write_text(&self, w: &mut crate::text::Writer) {
71        put_plain_text(w, self);
72    }
73    fn encode(&self, w: &mut Writer) {
74        put_plain(w, self);
75    }
76    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
77        // A typed value (`@…`) is the slow way's to tell apart.
78        d.str()
79            .filter(|s| !s.starts_with(typed_mark()))
80            .map(str::to_owned)
81    }
82    fn pack(&self, p: &mut Packer) {
83        p.put_str(self);
84    }
85    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
86        u.str().map(str::to_owned)
87    }
88}
89
90/// Text that may be a constant: borrowed where it's written, owned once read.
91impl Schema for std::borrow::Cow<'static, str> {
92    fn shape() -> Shape {
93        Shape::Str
94    }
95    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
96        String::read(v, cx).map(Self::Owned)
97    }
98    fn write(&self) -> Value {
99        plain(self)
100    }
101    fn write_text(&self, w: &mut crate::text::Writer) {
102        put_plain_text(w, self);
103    }
104    fn encode(&self, w: &mut Writer) {
105        put_plain(w, self);
106    }
107    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
108        String::decode(d).map(Self::Owned)
109    }
110    fn pack(&self, p: &mut Packer) {
111        p.put_str(self);
112    }
113    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
114        String::unpack(u).map(Self::Owned)
115    }
116}
117
118/// A file's path, as text (a path that isn't Unicode loses what isn't).
119impl Schema for std::path::PathBuf {
120    fn shape() -> Shape {
121        Shape::Str
122    }
123    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
124        String::read(v, cx).map(Self::from)
125    }
126    fn write(&self) -> Value {
127        plain(&self.to_string_lossy())
128    }
129    fn write_text(&self, w: &mut crate::text::Writer) {
130        put_plain_text(w, &self.to_string_lossy());
131    }
132    fn encode(&self, w: &mut Writer) {
133        put_plain(w, &self.to_string_lossy());
134    }
135    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
136        String::decode(d).map(Self::from)
137    }
138    fn pack(&self, p: &mut Packer) {
139        p.put_str(&self.to_string_lossy());
140    }
141    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
142        u.str().map(Self::from)
143    }
144}
145
146/// Text everywhere but in a frame, which carries its number.
147impl Schema for crate::Atom {
148    fn shape() -> Shape {
149        Shape::Atom
150    }
151    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
152        String::read(v, cx).map(|s| crate::Atom::new(&s))
153    }
154    fn write(&self) -> Value {
155        plain(self.as_str())
156    }
157    fn write_text(&self, w: &mut crate::text::Writer) {
158        put_plain_text(w, self.as_str());
159    }
160    fn encode(&self, w: &mut Writer) {
161        put_plain(w, self.as_str());
162    }
163    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
164        d.str()
165            .filter(|s| !s.starts_with(typed_mark()))
166            .map(crate::Atom::new)
167    }
168    fn pack(&self, p: &mut Packer) {
169        p.put_u(u64::from(self.number()));
170    }
171    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
172        crate::Atom::from_number(u32::try_from(u.uint()?).ok()?)
173    }
174}
175
176impl Schema for Box<str> {
177    fn shape() -> Shape {
178        Shape::Str
179    }
180    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
181        String::read(v, cx).map(String::into_boxed_str)
182    }
183    fn write(&self) -> Value {
184        plain(self)
185    }
186    fn write_text(&self, w: &mut crate::text::Writer) {
187        put_plain_text(w, self);
188    }
189    fn encode(&self, w: &mut Writer) {
190        put_plain(w, self);
191    }
192    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
193        String::decode(d).map(String::into_boxed_str)
194    }
195    fn pack(&self, p: &mut Packer) {
196        p.put_str(self);
197    }
198    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
199        u.str().map(Into::into)
200    }
201}
202
203/// A string shared without copying.
204impl Schema for crate::sync::Arc<str> {
205    fn shape() -> Shape {
206        Shape::Str
207    }
208    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
209        String::read(v, cx).map(Into::into)
210    }
211    fn write(&self) -> Value {
212        plain(self)
213    }
214    fn write_text(&self, w: &mut crate::text::Writer) {
215        put_plain_text(w, self);
216    }
217    fn encode(&self, w: &mut Writer) {
218        put_plain(w, self);
219    }
220    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
221        String::decode(d).map(Into::into)
222    }
223    fn pack(&self, p: &mut Packer) {
224        p.put_str(self);
225    }
226    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
227        u.shared_str()
228    }
229}