> */ = PointerBuf::parse(ptr_str).unwrap_err();
166 | assert!(err.original().is_no_leading_slash());
167 | ```
168 |
169 | ## Feature Flags
170 |
171 | | Flag | Description | Enables | Default |
172 | | :---------: | ----------------------------------------------------------------------------------------------------------------------------------------- | --------------- | :-----: |
173 | | `"std"` | Implements `std::error::Error` for error types | | ✓ |
174 | | `"serde"` | Enables [`serde`] support for types | | ✓ |
175 | | `"json"` | Implements ops for [`serde_json::Value`] | `"serde"` | ✓ |
176 | | `"toml"` | Implements ops for [`toml::Value`] | `"std"`, `toml` | |
177 | | `"assign"` | Enables the [`assign`] module and related pointer methods, providing a means to assign a value to a specific location within a document | | ✓ |
178 | | `"resolve"` | Enables the [`resolve`] module and related pointer methods, providing a means to resolve a value at a specific location within a document | | ✓ |
179 | | `"delete"` | Enables the [`delete`] module and related pointer methods, providing a means to delete a value at a specific location within a document | `"resolve"` | ✓ |
180 | | `"miette"` | Enables integration with [`miette`](https://docs.rs/miette) for error reporting | `"std"` | |
181 |
182 |
183 |
184 | ## License
185 |
186 | Licensed under either of
187 |
188 | - Apache License, Version 2.0
189 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
190 | - MIT license
191 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
192 |
193 | at your convenience.
194 |
195 | ## Contribution
196 |
197 | Contributions and feedback are always welcome and appreciated. If you find an
198 | issue, please open a ticket or a pull request.
199 |
200 | Unless you explicitly state otherwise, any contribution intentionally submitted
201 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
202 | dual licensed as above, without any additional terms or conditions.
203 |
204 | [LICENSE-APACHE]: LICENSE-APACHE
205 | [LICENSE-MIT]: LICENSE-MIT
206 |
207 |
208 |
209 | [`Pointer::components`]: https://docs.rs/jsonptr/latest/jsonptrstruct.Pointer.html#method.components
210 | [`Pointer::tokens`]: https://docs.rs/jsonptr/latest/jsonptrstruct.Pointer.html#method.tokens
211 | [`Pointer`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html
212 | [`Pointer::parse`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.parse
213 | [`Pointer::resolve`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.resolve
214 | [`Pointer::resolve_mut`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.resolve_mut
215 | [`Pointer::assign`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.assign
216 | [`Pointer::delete`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.delete
217 | [`PointerBuf::parse`]: https://docs.rs/jsonptr/latest/jsonptr/struct.PointerBuf.html#method.parse
218 | [`from_tokens`]: https://docs.rs/jsonptr/latest/jsonptr/struct.PointerBuf.html#method.from_tokens
219 | [`PointerBuf`]: https://docs.rs/jsonptr/latest/jsonptr/struct.PointerBuf.html
220 | [`Token`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Token.html
221 | [`Tokens`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Tokens.html
222 | [`Components`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Components.html
223 | [`Component`]: https://docs.rs/jsonptr/latest/jsonptr/enum.Component.html
224 | [`index`]: https://docs.rs/jsonptr/latest/jsonptr/index/index.html
225 | [`tokens`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.tokens
226 | [`components`]: https://docs.rs/jsonptr/latest/jsonptr/struct.Pointer.html#method.components
227 | [`resolve`]: https://docs.rs/jsonptr/latest/jsonptr/resolve/index.html
228 | [`assign`]: https://docs.rs/jsonptr/latest/jsonptr/assign/index.html
229 | [`delete`]: https://docs.rs/jsonptr/latest/jsonptr/delete/index.html
230 | [`Resolve`]: https://docs.rs/jsonptr/latest/jsonptr/resolve/trait.Resolve.html
231 | [`ResolveMut`]: https://docs.rs/jsonptr/latest/jsonptr/resolve/trait.ResolveMut.html
232 | [`Assign`]: https://docs.rs/jsonptr/latest/jsonptr/assign/trait.Assign.html
233 | [`Delete`]: https://docs.rs/jsonptr/latest/jsonptr/delete/trait.Delete.html
234 | [`serde`]: https://docs.rs/serde/1.0/serde/index
235 | [`serde_json`]: https://docs.rs/serde_json/1.0/serde_json/enum.Value.html
236 | [`serde_json::Value`]: https://docs.rs/serde_json/1.0/serde_json/enum.Value.html
237 | [`toml`]: https://docs.rs/toml/0.8/toml/enum.Value.html
238 | [`toml::Value`]: https://docs.rs/toml/0.8/toml/enum.Value.html
239 | [`Path`]: https://doc.rust-lang.org/std/path/struct.Path.html
240 | [`PathBuf`]: https://doc.rust-lang.org/std/path/struct.PathBuf.html
241 |
--------------------------------------------------------------------------------
/src/arbitrary.rs:
--------------------------------------------------------------------------------
1 | use crate::{PointerBuf, Token};
2 | use alloc::{boxed::Box, string::String, vec::Vec};
3 | use quickcheck::Arbitrary;
4 |
5 | impl Arbitrary for Token<'static> {
6 | fn arbitrary(g: &mut quickcheck::Gen) -> Self {
7 | Self::new(String::arbitrary(g))
8 | }
9 |
10 | fn shrink(&self) -> Box> {
11 | Box::new(self.decoded().into_owned().shrink().map(Self::new))
12 | }
13 | }
14 |
15 | impl Arbitrary for PointerBuf {
16 | fn arbitrary(g: &mut quickcheck::Gen) -> Self {
17 | let size = usize::arbitrary(g) % g.size();
18 | Self::from_tokens((0..size).map(|_| Token::arbitrary(g)).collect::>())
19 | }
20 |
21 | fn shrink(&self) -> Box> {
22 | let tokens: Vec<_> = self.tokens().map(Token::into_owned).collect();
23 | Box::new((0..self.count()).map(move |i| {
24 | let subset: Vec<_> = tokens
25 | .iter()
26 | .enumerate()
27 | .filter_map(|(j, t)| (i != j).then_some(t.clone()))
28 | .collect();
29 | Self::from_tokens(subset)
30 | }))
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/component.rs:
--------------------------------------------------------------------------------
1 | use crate::{Pointer, Token, Tokens};
2 |
3 | /// A single [`Token`] or the root of a JSON Pointer
4 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
5 | pub enum Component<'t> {
6 | /// The document root
7 | Root,
8 | /// A segment of a JSON Pointer
9 | Token(Token<'t>),
10 | }
11 | impl<'t> From> for Component<'t> {
12 | fn from(token: Token<'t>) -> Self {
13 | Self::Token(token)
14 | }
15 | }
16 |
17 | /// An iterator over the [`Component`]s of a JSON Pointer
18 | #[derive(Debug)]
19 | pub struct Components<'t> {
20 | tokens: Tokens<'t>,
21 | sent_root: bool,
22 | }
23 |
24 | impl<'t> Iterator for Components<'t> {
25 | type Item = Component<'t>;
26 | fn next(&mut self) -> Option {
27 | if !self.sent_root {
28 | self.sent_root = true;
29 | return Some(Component::Root);
30 | }
31 | self.tokens.next().map(Component::Token)
32 | }
33 | }
34 |
35 | impl<'t> From<&'t Pointer> for Components<'t> {
36 | fn from(pointer: &'t Pointer) -> Self {
37 | Self {
38 | sent_root: false,
39 | tokens: pointer.tokens(),
40 | }
41 | }
42 | }
43 |
44 | #[cfg(test)]
45 | mod tests {
46 | use super::*;
47 |
48 | #[test]
49 | fn components() {
50 | let ptr = Pointer::from_static("");
51 | let components: Vec<_> = Components::from(ptr).collect();
52 | assert_eq!(components, vec![Component::Root]);
53 |
54 | let ptr = Pointer::from_static("/foo");
55 | let components = ptr.components().collect::>();
56 | assert_eq!(
57 | components,
58 | vec![Component::Root, Component::Token("foo".into())]
59 | );
60 |
61 | let ptr = Pointer::from_static("/foo/bar/-/0/baz");
62 | let components = ptr.components().collect::>();
63 | assert_eq!(
64 | components,
65 | vec![
66 | Component::Root,
67 | Component::from(Token::from("foo")),
68 | Component::Token("bar".into()),
69 | Component::Token("-".into()),
70 | Component::Token("0".into()),
71 | Component::Token("baz".into())
72 | ]
73 | );
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/delete.rs:
--------------------------------------------------------------------------------
1 | //! # Delete values based on JSON Pointers
2 | //!
3 | //! This module provides the [`Delete`] trait which is implemented by types that
4 | //! can internally remove a value based on a JSON Pointer.
5 | //!
6 | //! The rules of deletion are determined by the implementation, with the
7 | //! provided implementations (`"json"` & `"toml"`) operating as follows:
8 | //! - If the [`Pointer`] can be resolved, then the [`Value`](`Delete::Value`) is
9 | //! deleted and returned as `Some(value)`.
10 | //! - If the [`Pointer`] fails to resolve for any reason, `None` is
11 | //! returned.
12 | //! - If the [`Pointer`] is root, `value` is replaced:
13 | //! - `"json"` - `serde_json::Value::Null`
14 | //! - `"toml"` - `toml::Value::Table::Default`
15 | //!
16 | //! This module is enabled by default with the `"delete"` feature flag.
17 | //!
18 | //! ## Usage
19 | //! Deleting a resolved pointer:
20 | //! ```rust
21 | //! use jsonptr::{Pointer, delete::Delete};
22 | //! use serde_json::json;
23 | //!
24 | //! let mut data = json!({ "foo": { "bar": { "baz": "qux" } } });
25 | //! let ptr = Pointer::from_static("/foo/bar/baz");
26 | //! assert_eq!(data.delete(&ptr), Some("qux".into()));
27 | //! assert_eq!(data, json!({ "foo": { "bar": {} } }));
28 | //! ```
29 | //! Deleting a non-existent Pointer returns `None`:
30 | //! ```rust
31 | //! use jsonptr::{ Pointer, delete::Delete };
32 | //! use serde_json::json;
33 | //!
34 | //! let mut data = json!({});
35 | //! let ptr = Pointer::from_static("/foo/bar/baz");
36 | //! assert_eq!(ptr.delete(&mut data), None);
37 | //! assert_eq!(data, json!({}));
38 | //! ```
39 | //! Deleting a root pointer replaces the value with `Value::Null`:
40 | //! ```rust
41 | //! use jsonptr::{Pointer, delete::Delete};
42 | //! use serde_json::json;
43 | //!
44 | //! let mut data = json!({ "foo": { "bar": "baz" } });
45 | //! let ptr = Pointer::root();
46 | //! assert_eq!(data.delete(&ptr), Some(json!({ "foo": { "bar": "baz" } })));
47 | //! assert!(data.is_null());
48 | //! ```
49 | //!
50 | //! ## Provided implementations
51 | //!
52 | //! | Lang | value type | feature flag | Default |
53 | //! | ----- |: ----------------- :|: ---------- :| ------- |
54 | //! | JSON | `serde_json::Value` | `"json"` | ✓ |
55 | //! | TOML | `toml::Value` | `"toml"` | |
56 |
57 | use crate::Pointer;
58 |
59 | /*
60 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
61 | ╔══════════════════════════════════════════════════════════════════════════════╗
62 | ║ ║
63 | ║ Delete ║
64 | ║ ¯¯¯¯¯¯¯¯ ║
65 | ╚══════════════════════════════════════════════════════════════════════════════╝
66 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
67 | */
68 |
69 | /// Delete is implemented by types which can internally remove a value based on
70 | /// a JSON Pointer
71 | pub trait Delete {
72 | /// The type of value that this implementation can operate on.
73 | type Value;
74 |
75 | /// Attempts to internally delete a value based upon a [Pointer].
76 | fn delete(&mut self, ptr: &Pointer) -> Option;
77 | }
78 |
79 | /*
80 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
81 | ╔══════════════════════════════════════════════════════════════════════════════╗
82 | ║ ║
83 | ║ json impl ║
84 | ║ ¯¯¯¯¯¯¯¯¯¯¯ ║
85 | ╚══════════════════════════════════════════════════════════════════════════════╝
86 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
87 | */
88 |
89 | #[cfg(feature = "json")]
90 | mod json {
91 | use super::Delete;
92 | use crate::Pointer;
93 | use core::mem;
94 | use serde_json::Value;
95 |
96 | impl Delete for Value {
97 | type Value = Value;
98 | fn delete(&mut self, ptr: &Pointer) -> Option {
99 | let Some((parent_ptr, last)) = ptr.split_back() else {
100 | // deleting at root
101 | return Some(mem::replace(self, Value::Null));
102 | };
103 | parent_ptr
104 | .resolve_mut(self)
105 | .ok()
106 | .and_then(|parent| match parent {
107 | Value::Array(children) => {
108 | let idx = last.to_index().ok()?.for_len_incl(children.len()).ok()?;
109 | children.remove(idx).into()
110 | }
111 | Value::Object(children) => children.remove(last.decoded().as_ref()),
112 | _ => None,
113 | })
114 | }
115 | }
116 | }
117 |
118 | /*
119 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
120 | ╔══════════════════════════════════════════════════════════════════════════════╗
121 | ║ ║
122 | ║ toml impl ║
123 | ║ ¯¯¯¯¯¯¯¯¯¯¯ ║
124 | ╚══════════════════════════════════════════════════════════════════════════════╝
125 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
126 | */
127 | #[cfg(feature = "toml")]
128 | mod toml {
129 | use super::Delete;
130 | use crate::Pointer;
131 | use core::mem;
132 | use toml::{Table, Value};
133 |
134 | impl Delete for Value {
135 | type Value = Value;
136 | fn delete(&mut self, ptr: &Pointer) -> Option {
137 | let Some((parent_ptr, last)) = ptr.split_back() else {
138 | // deleting at root
139 | return Some(mem::replace(self, Table::default().into()));
140 | };
141 | parent_ptr
142 | .resolve_mut(self)
143 | .ok()
144 | .and_then(|parent| match parent {
145 | Value::Array(children) => {
146 | let idx = last.to_index().ok()?.for_len_incl(children.len()).ok()?;
147 | children.remove(idx).into()
148 | }
149 | Value::Table(children) => children.remove(last.decoded().as_ref()),
150 | _ => None,
151 | })
152 | }
153 | }
154 | }
155 |
156 | /*
157 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
158 | ╔══════════════════════════════════════════════════════════════════════════════╗
159 | ║ ║
160 | ║ Tests ║
161 | ║ ¯¯¯¯¯¯¯ ║
162 | ╚══════════════════════════════════════════════════════════════════════════════╝
163 | ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
164 | */
165 |
166 | #[cfg(test)]
167 | mod tests {
168 | use super::Delete;
169 | use crate::Pointer;
170 | use core::fmt;
171 |
172 | use serde_json::json;
173 | struct Test {
174 | data: V,
175 | ptr: &'static str,
176 | expected_data: V,
177 | expected_deleted: Option,
178 | }
179 | impl Test
180 | where
181 | V: Delete + Clone + PartialEq + fmt::Display + fmt::Debug,
182 | {
183 | fn all(tests: impl IntoIterator- >) {
184 | tests.into_iter().enumerate().for_each(|(i, t)| t.run(i));
185 | }
186 | fn run(self, _i: usize) {
187 | let Test {
188 | mut data,
189 | ptr,
190 | expected_data,
191 | expected_deleted,
192 | } = self;
193 |
194 | let ptr = Pointer::from_static(ptr);
195 | let deleted = ptr.delete(&mut data);
196 | assert_eq!(expected_data, data);
197 | assert_eq!(expected_deleted, deleted);
198 | }
199 | }
200 | /*
201 | ╔═══════════════════════════════════════════════════╗
202 | ║ json ║
203 | ╚═══════════════════════════════════════════════════╝
204 | */
205 | #[test]
206 | #[cfg(feature = "json")]
207 | fn delete_json() {
208 | Test::all([
209 | // 0
210 | Test {
211 | ptr: "/foo",
212 | data: json!({"foo": "bar"}),
213 | expected_data: json!({}),
214 | expected_deleted: Some(json!("bar")),
215 | },
216 | // 1
217 | Test {
218 | ptr: "/foo/bar",
219 | data: json!({"foo": {"bar": "baz"}}),
220 | expected_data: json!({"foo": {}}),
221 | expected_deleted: Some(json!("baz")),
222 | },
223 | // 2
224 | Test {
225 | ptr: "/foo/bar",
226 | data: json!({"foo": "bar"}),
227 | expected_data: json!({"foo": "bar"}),
228 | expected_deleted: None,
229 | },
230 | // 3
231 | Test {
232 | ptr: "/foo/bar",
233 | data: json!({"foo": {"bar": "baz"}}),
234 | expected_data: json!({"foo": {}}),
235 | expected_deleted: Some(json!("baz")),
236 | },
237 | // 4
238 | Test {
239 | ptr: "/foo/bar/0",
240 | data: json!({"foo": {"bar": ["baz", "qux"]}}),
241 | expected_data: json!({"foo": {"bar": ["qux"]}}),
242 | expected_deleted: Some(json!("baz")),
243 | },
244 | // 5
245 | Test {
246 | ptr: "/foo/0",
247 | data: json!({"foo": "bar"}),
248 | expected_data: json!({"foo": "bar"}),
249 | expected_deleted: None,
250 | },
251 | // 6
252 | Test {
253 | ptr: "/foo/bar/0/baz",
254 | data: json!({"foo": { "bar": [{"baz": "qux", "remaining": "field"}]}}),
255 | expected_data: json!({"foo": { "bar": [{"remaining": "field"}]} }),
256 | expected_deleted: Some(json!("qux")),
257 | },
258 | // 7
259 | // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18
260 | Test {
261 | ptr: "/Example",
262 | data: json!({"Example": 21, "test": "test"}),
263 | expected_data: json!({"test": "test"}),
264 | expected_deleted: Some(json!(21)),
265 | },
266 | Test {
267 | ptr: "",
268 | data: json!({"Example": 21, "test": "test"}),
269 | expected_data: json!(null),
270 | expected_deleted: Some(json!({"Example": 21, "test": "test"})),
271 | },
272 | ]);
273 | }
274 | /*
275 | ╔═══════════════════════════════════════════════════╗
276 | ║ toml ║
277 | ╚═══════════════════════════════════════════════════╝
278 | */
279 | #[test]
280 | #[cfg(feature = "toml")]
281 | fn delete_toml() {
282 | use toml::{toml, Table, Value};
283 |
284 | Test::all([
285 | // 0
286 | Test {
287 | data: toml! {"foo" = "bar"}.into(),
288 | ptr: "/foo",
289 | expected_data: Value::Table(Table::new()),
290 | expected_deleted: Some("bar".into()),
291 | },
292 | // 1
293 | Test {
294 | data: toml! {"foo" = {"bar" = "baz"}}.into(),
295 | ptr: "/foo/bar",
296 | expected_data: toml! {"foo" = {}}.into(),
297 | expected_deleted: Some("baz".into()),
298 | },
299 | // 2
300 | Test {
301 | data: toml! {"foo" = "bar"}.into(),
302 | ptr: "/foo/bar",
303 | expected_data: toml! {"foo" = "bar"}.into(),
304 | expected_deleted: None,
305 | },
306 | // 3
307 | Test {
308 | data: toml! {"foo" = {"bar" = "baz"}}.into(),
309 | ptr: "/foo/bar",
310 | expected_data: toml! {"foo" = {}}.into(),
311 | expected_deleted: Some("baz".into()),
312 | },
313 | // 4
314 | Test {
315 | data: toml! {"foo" = {"bar" = ["baz", "qux"]}}.into(),
316 | ptr: "/foo/bar/0",
317 | expected_data: toml! {"foo" = {"bar" = ["qux"]}}.into(),
318 | expected_deleted: Some("baz".into()),
319 | },
320 | // 5
321 | Test {
322 | data: toml! {"foo" = "bar"}.into(),
323 | ptr: "/foo/0",
324 | expected_data: toml! {"foo" = "bar"}.into(),
325 | expected_deleted: None,
326 | },
327 | // 6
328 | Test {
329 | data: toml! {"foo" = { "bar" = [{"baz" = "qux", "remaining" = "field"}]}}.into(),
330 | ptr: "/foo/bar/0/baz",
331 | expected_data: toml! {"foo" = { "bar" = [{"remaining" = "field"}]} }.into(),
332 | expected_deleted: Some("qux".into()),
333 | },
334 | // 7
335 | // issue #18 - unable to delete root token https://github.com/chanced/jsonptr/issues/18
336 | Test {
337 | data: toml! {"Example" = 21 "test" = "test"}.into(),
338 | ptr: "/Example",
339 | expected_data: toml! {"test" = "test"}.into(),
340 | expected_deleted: Some(21.into()),
341 | },
342 | ]);
343 | }
344 | }
345 |
--------------------------------------------------------------------------------
/src/diagnostic.rs:
--------------------------------------------------------------------------------
1 | //! Error reporting data structures and miette integration.
2 | //!
3 |
4 | use alloc::{boxed::Box, string::String};
5 | use core::{fmt, ops::Deref};
6 |
7 | /// Implemented by errors which can be converted into a [`Report`].
8 | pub trait Diagnostic: Sized {
9 | /// The value which caused the error.
10 | type Subject: Deref;
11 |
12 | /// Combine the error with its subject to generate a [`Report`].
13 | fn into_report(self, subject: impl Into) -> Report {
14 | Report::new(self, subject.into())
15 | }
16 |
17 | /// The docs.rs URL for this error
18 | fn url() -> &'static str;
19 |
20 | /// Returns the label for the given [`Subject`] if applicable.
21 | fn labels(&self, subject: &Self::Subject) -> Option>>;
22 | }
23 |
24 | /// A label for a span within a JSON Pointer or malformed string.
25 | #[derive(Debug, PartialEq, Eq, Clone)]
26 | pub struct Label {
27 | text: String,
28 | offset: usize,
29 | len: usize,
30 | }
31 |
32 | impl Label {
33 | /// Creates a new instance of a [`Label`] from its parts
34 | // NOTE: this is deliberately public, so that users can use
35 | // the `Assign` and `Resolve` traits with custom types and errors,
36 | // and then implement `Diagnostic` for those errors.
37 | pub fn new(text: String, offset: usize, len: usize) -> Self {
38 | Self { text, offset, len }
39 | }
40 | }
41 |
42 | #[cfg(feature = "miette")]
43 | impl From