Skip to main content

substrate_data/model/
reference.rs

1//! A reference's two parts, each checked once where it's made: its kind
2//! ([`KindName`], interned like an [`Atom`]) and its id ([`RefKey`]).
3
4use std::fmt;
5
6use crate::Atom;
7use crate::typed::TypedError;
8
9/// A reference's kind: lower case ASCII letters, digits and `_`, starting
10/// with a letter (`user`, `doc`, `file`). Checked when it's made, then a
11/// number: copied and compared as one.
12#[derive(Clone, Copy, PartialEq, Eq, Hash)]
13pub struct KindName(Atom);
14
15impl KindName {
16    /// `name` as a kind, if it's one.
17    ///
18    /// # Errors
19    ///
20    /// [`TypedError::BadKind`] when `name` isn't lower case letters, digits and `_`
21    /// starting with a letter.
22    pub fn new(name: &str) -> Result<KindName, TypedError> {
23        if Self::is_valid(name) {
24            Ok(KindName(Atom::new(name)))
25        } else {
26            Err(TypedError::BadKind {
27                kind: name.to_owned(),
28            })
29        }
30    }
31
32    /// A name `kind!` already checked at compile time.
33    #[must_use]
34    pub fn declared(name: &'static str) -> KindName {
35        debug_assert!(Self::is_valid(name), "`{name}` isn't a kind");
36        KindName(Atom::new(name))
37    }
38
39    /// Whether `name` is a kind — at compile time too (`kind!` checks its
40    /// names with it).
41    #[must_use]
42    pub const fn is_valid(name: &str) -> bool {
43        let b = name.as_bytes();
44        if b.is_empty() || !b[0].is_ascii_lowercase() {
45            return false;
46        }
47        let mut i = 1;
48        while i < b.len() {
49            let c = b[i];
50            if !(c.is_ascii_lowercase() || c.is_ascii_digit() || c == b'_') {
51                return false;
52            }
53            i += 1;
54        }
55        true
56    }
57
58    /// As written.
59    #[must_use]
60    pub fn as_str(self) -> &'static str {
61        self.0.as_str()
62    }
63
64    /// Its number in this process's atom table.
65    #[must_use]
66    pub fn atom(self) -> Atom {
67        self.0
68    }
69}
70
71impl fmt::Debug for KindName {
72    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73        write!(f, "KindName({:?})", self.as_str())
74    }
75}
76
77impl fmt::Display for KindName {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        f.write_str(self.as_str())
80    }
81}
82
83impl PartialOrd for KindName {
84    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
85        Some(self.cmp(other))
86    }
87}
88
89impl Ord for KindName {
90    /// By name, not by number.
91    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
92        self.as_str().cmp(other.as_str())
93    }
94}
95
96impl PartialEq<str> for KindName {
97    fn eq(&self, other: &str) -> bool {
98        self.as_str() == other
99    }
100}
101
102impl PartialEq<&str> for KindName {
103    fn eq(&self, other: &&str) -> bool {
104        self.as_str() == *other
105    }
106}
107
108/// A reference's id as written, untyped: anything but empty, whitespace or
109/// control characters. What it names is the format's business; a typed
110/// field reads it as its kind's id type ([`crate::RefId`]).
111#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
112pub struct RefKey(Box<str>);
113
114impl RefKey {
115    /// `id` as a reference's id, if it's one.
116    ///
117    /// # Errors
118    ///
119    /// [`TypedError::BadId`] when `id` is empty or has whitespace or control characters.
120    pub fn new(id: &str) -> Result<RefKey, TypedError> {
121        if Self::is_valid(id) {
122            Ok(RefKey(id.into()))
123        } else {
124            Err(TypedError::BadId {
125                kind: None,
126                id: id.to_owned(),
127            })
128        }
129    }
130
131    /// Whether `id` may be a reference's id.
132    #[must_use]
133    pub fn is_valid(id: &str) -> bool {
134        !id.is_empty() && !id.chars().any(|c| c.is_whitespace() || c.is_control())
135    }
136
137    /// A typed id's spelling, as is: it's checked when it's read back.
138    pub(crate) fn spelled(id: &str) -> RefKey {
139        RefKey(id.into())
140    }
141
142    /// As written.
143    #[must_use]
144    pub fn as_str(&self) -> &str {
145        &self.0
146    }
147}
148
149impl fmt::Debug for RefKey {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        write!(f, "RefKey({:?})", &*self.0)
152    }
153}
154
155impl fmt::Display for RefKey {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157        f.write_str(&self.0)
158    }
159}
160
161impl PartialEq<str> for RefKey {
162    fn eq(&self, other: &str) -> bool {
163        &*self.0 == other
164    }
165}
166
167impl PartialEq<&str> for RefKey {
168    fn eq(&self, other: &&str) -> bool {
169        &*self.0 == *other
170    }
171}
172
173impl std::ops::Deref for KindName {
174    type Target = str;
175    fn deref(&self) -> &str {
176        self.as_str()
177    }
178}
179
180impl AsRef<str> for KindName {
181    fn as_ref(&self) -> &str {
182        self.as_str()
183    }
184}
185
186impl PartialEq<String> for KindName {
187    fn eq(&self, other: &String) -> bool {
188        self.as_str() == other
189    }
190}
191
192impl std::ops::Deref for RefKey {
193    type Target = str;
194    fn deref(&self) -> &str {
195        &self.0
196    }
197}
198
199impl AsRef<str> for RefKey {
200    fn as_ref(&self) -> &str {
201        &self.0
202    }
203}
204
205/// A map keyed by ids is looked up by a `&str`: an id hashes and orders as its text.
206impl std::borrow::Borrow<str> for RefKey {
207    fn borrow(&self) -> &str {
208        &self.0
209    }
210}
211
212impl PartialEq<String> for RefKey {
213    fn eq(&self, other: &String) -> bool {
214        &*self.0 == other
215    }
216}
217
218impl PartialEq<RefKey> for String {
219    fn eq(&self, other: &RefKey) -> bool {
220        self.as_str() == &*other.0
221    }
222}
223
224impl PartialEq<RefKey> for str {
225    fn eq(&self, other: &RefKey) -> bool {
226        self == &*other.0
227    }
228}