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
30impl Schema for String {
31    fn shape() -> Shape {
32        Shape::Str
33    }
34    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
35        match v {
36            // Plain text, the usual case: copied once, into the result.
37            ValueRef::Str(s) if !s.starts_with('@') => Some(s.to_string()),
38            ValueRef::Str(s) => match typed::classify(s) {
39                Ok(typed::Spelled::Plain(s)) => Some(s),
40                Ok(other) => {
41                    cx.problem(format!(
42                        "expected plain text, found {} (write `@@` for a literal `@`)",
43                        other.kind()
44                    ));
45                    None
46                }
47                Err(e) => {
48                    cx.problem_of(crate::diagnostic::Problem::BadTyped, e.to_string());
49                    None
50                }
51            },
52            other => {
53                cx.expected(Expected::Str, other);
54                None
55            }
56        }
57    }
58    fn write(&self) -> Value {
59        plain(self)
60    }
61    fn encode(&self, w: &mut Writer) {
62        put_plain(w, self);
63    }
64    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
65        // A typed value (`@…`) is the slow way's to tell apart.
66        d.str()
67            .filter(|s| !s.starts_with(typed_mark()))
68            .map(str::to_owned)
69    }
70    fn pack(&self, p: &mut Packer) {
71        p.put_str(self);
72    }
73    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
74        u.str().map(str::to_owned)
75    }
76}
77
78/// Text that may be a constant: borrowed where it's written, owned once read.
79impl Schema for std::borrow::Cow<'static, str> {
80    fn shape() -> Shape {
81        Shape::Str
82    }
83    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
84        String::read(v, cx).map(Self::Owned)
85    }
86    fn write(&self) -> Value {
87        plain(self)
88    }
89    fn encode(&self, w: &mut Writer) {
90        put_plain(w, self);
91    }
92    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
93        String::decode(d).map(Self::Owned)
94    }
95    fn pack(&self, p: &mut Packer) {
96        p.put_str(self);
97    }
98    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
99        String::unpack(u).map(Self::Owned)
100    }
101}
102
103/// A file's path, as text (a path that isn't Unicode loses what isn't).
104impl Schema for std::path::PathBuf {
105    fn shape() -> Shape {
106        Shape::Str
107    }
108    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
109        String::read(v, cx).map(Self::from)
110    }
111    fn write(&self) -> Value {
112        plain(&self.to_string_lossy())
113    }
114    fn encode(&self, w: &mut Writer) {
115        put_plain(w, &self.to_string_lossy());
116    }
117    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
118        String::decode(d).map(Self::from)
119    }
120    fn pack(&self, p: &mut Packer) {
121        p.put_str(&self.to_string_lossy());
122    }
123    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
124        u.str().map(Self::from)
125    }
126}
127
128/// Text everywhere but in a frame, which carries its number.
129impl Schema for crate::Atom {
130    fn shape() -> Shape {
131        Shape::Atom
132    }
133    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
134        String::read(v, cx).map(|s| crate::Atom::new(&s))
135    }
136    fn write(&self) -> Value {
137        plain(self.as_str())
138    }
139    fn encode(&self, w: &mut Writer) {
140        put_plain(w, self.as_str());
141    }
142    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
143        d.str()
144            .filter(|s| !s.starts_with(typed_mark()))
145            .map(crate::Atom::new)
146    }
147    fn pack(&self, p: &mut Packer) {
148        p.put_u(u64::from(self.number()));
149    }
150    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
151        crate::Atom::from_number(u32::try_from(u.uint()?).ok()?)
152    }
153}
154
155impl Schema for Box<str> {
156    fn shape() -> Shape {
157        Shape::Str
158    }
159    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
160        String::read(v, cx).map(String::into_boxed_str)
161    }
162    fn write(&self) -> Value {
163        plain(self)
164    }
165    fn encode(&self, w: &mut Writer) {
166        put_plain(w, self);
167    }
168    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
169        String::decode(d).map(String::into_boxed_str)
170    }
171    fn pack(&self, p: &mut Packer) {
172        p.put_str(self);
173    }
174    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
175        u.str().map(Into::into)
176    }
177}
178
179/// A string shared without copying.
180impl Schema for crate::sync::Arc<str> {
181    fn shape() -> Shape {
182        Shape::Str
183    }
184    fn read(v: &ValueRef<'_>, cx: &mut Reader<'_>) -> Option<Self> {
185        String::read(v, cx).map(Into::into)
186    }
187    fn write(&self) -> Value {
188        plain(self)
189    }
190    fn encode(&self, w: &mut Writer) {
191        put_plain(w, self);
192    }
193    fn decode(d: &mut Decoder<'_>) -> Option<Self> {
194        String::decode(d).map(Into::into)
195    }
196    fn pack(&self, p: &mut Packer) {
197        p.put_str(self);
198    }
199    fn unpack(u: &mut Unpacker<'_>) -> Option<Self> {
200        u.shared_str()
201    }
202}