-
Notifications
You must be signed in to change notification settings - Fork 7
Adds position to AssignError, ResolveError variants, renames both errors to be less redundant
#93
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
45c4fde
e892a48
5dbf330
0b60a5c
b48e05b
16731e1
78704a4
1845e56
8699be5
f69fa55
112fec5
b78f2a6
516e9b3
8550151
f507257
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -133,55 +133,66 @@ pub trait Assign { | |
| ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | ||
| ╔══════════════════════════════════════════════════════════════════════════════╗ | ||
| ║ ║ | ||
| ║ AssignError ║ | ||
| ║ ¯¯¯¯¯¯¯¯¯¯¯¯¯ ║ | ||
| ║ Error ║ | ||
| ║ ¯¯¯¯¯¯¯ ║ | ||
| ╚══════════════════════════════════════════════════════════════════════════════╝ | ||
| ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ | ||
| */ | ||
|
|
||
| // TODO: should AssignError be deprecated? | ||
| /// Alias for [`Error`]. | ||
| /// | ||
| /// Possible error returned from [`Assign`] implementations for | ||
| /// [`serde_json::Value`] and | ||
| /// [`toml::Value`](https://docs.rs/toml/0.8.14/toml/index.html). | ||
| pub type AssignError = Error; | ||
|
|
||
| /// Possible error returned from [`Assign`] implementations for | ||
| /// [`serde_json::Value`] and | ||
| /// [`toml::Value`](https://docs.rs/toml/0.8.14/toml/index.html). | ||
| #[derive(Debug, PartialEq, Eq)] | ||
| pub enum AssignError { | ||
| /// A `Token` within the `Pointer` failed to be parsed as an array index. | ||
| pub enum Error { | ||
| /// A [`Token`] within the [`Pointer`] failed to be parsed as an array index. | ||
| FailedToParseIndex { | ||
| /// Position (index) of the token which failed to parse as an `Index` | ||
|
chanced marked this conversation as resolved.
Outdated
|
||
| position: usize, | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume this is to aid with the error message, right? Thinking about it, we could technically derive it on-the-fly from the offset + token. I know this was part of the raison d'etre for this PR, but I just realised I don't really understand the motivation. |
||
| /// Offset of the partial pointer starting with the invalid index. | ||
| offset: usize, | ||
| /// The source [`ParseIndexError`] | ||
| source: ParseIndexError, | ||
| }, | ||
|
|
||
| /// target array. | ||
| /// A [`Token`] within the [`Pointer`] contains an [`Index`] which is out of bounds. | ||
| /// | ||
| /// The current or resulting array's length is less than the index. | ||
| OutOfBounds { | ||
| /// Position (index) of the token which failed to parse as an `Index` | ||
| position: usize, | ||
| /// Offset of the partial pointer starting with the invalid index. | ||
| offset: usize, | ||
| /// The source [`OutOfBoundsError`] | ||
| source: OutOfBoundsError, | ||
| }, | ||
| } | ||
|
|
||
| impl fmt::Display for AssignError { | ||
| impl fmt::Display for Error { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| match self { | ||
| Self::FailedToParseIndex { offset, .. } => { | ||
| write!( | ||
| f, | ||
| "assignment failed due to an invalid index at offset {offset}" | ||
| ) | ||
| Self::FailedToParseIndex { .. } => { | ||
| write!(f, "assignment failed due to an invalid index") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, if I'm reading this correctly we'll be deprecating the
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I don't think we should deprecate it. It actually is incredibly useful to have and a positive consequence of my blunder to opt for Folks can have pretty printed errors that do something like without much additional effort to determine where that starts.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see! In that case, why did you remove it from the message? 😄
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I should have mentioned that I went through the exact same thought process as you. I started with "ugh, I guess I add Then it dawned on me that it's actually really useful.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope - I'm back on this was a mistake. I'd like to remove I just discovered
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No need to apologize about that at all.
So for
If we require the caller to keep a copy for their error path, we are causing an additional allocation on the happy path. Right now, in #93 I have pub struct ParseBufError {
pub value: String,
pub source: ParseError,
}and I've got to run for now - I'll reply to the other when I'm in front of my laptop.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Agreed, although including an owned copy there would allow us to report with the
Yep, hence the suggestion to make it use a
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
sorry, I missed this question and I can't recall :(
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh!! You meant the error message! Sorry, I should have held off on replying until I could devote enough energy to properly think through the question. I removed the offset from the error message initially because I wasn't sure how much value it was adding vs making the error message more verbose and confusing. In retrospect, I'm not sure either are the case. Some may appreciate the offset in the message and I think the error message is now longer than what it was before. I'll add them back.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, just to clarify it's not that I think we need the offset in the message, but just that the fact it's not used makes it seem like it's not useful (and if it isn't, perhaps it should be deprecated). But with miette in the picture I think it definitely is useful because then it can be used for the label. |
||
| } | ||
| Self::OutOfBounds { offset, .. } => { | ||
| Self::OutOfBounds { .. } => { | ||
| write!( | ||
| f, | ||
| "assignment failed due to index at offset {offset} being out of bounds" | ||
| "assignment failed due to index an index being out of bounds" | ||
| ) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| #[cfg(feature = "std")] | ||
| impl std::error::Error for AssignError { | ||
| impl std::error::Error for Error { | ||
| fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
| match self { | ||
| Self::FailedToParseIndex { source, .. } => Some(source), | ||
|
|
@@ -207,7 +218,7 @@ enum Assigned<'v, V> { | |
|
|
||
| #[cfg(feature = "json")] | ||
| mod json { | ||
| use super::{Assign, AssignError, Assigned}; | ||
| use super::{Assign, Assigned, Error}; | ||
| use crate::{Pointer, Token}; | ||
| use alloc::{ | ||
| string::{String, ToString}, | ||
|
|
@@ -235,7 +246,7 @@ mod json { | |
| } | ||
| impl Assign for Value { | ||
| type Value = Value; | ||
| type Error = AssignError; | ||
| type Error = Error; | ||
| fn assign<V>(&mut self, ptr: &Pointer, value: V) -> Result<Option<Self::Value>, Self::Error> | ||
| where | ||
| V: Into<Self::Value>, | ||
|
|
@@ -248,14 +259,15 @@ mod json { | |
| mut ptr: &Pointer, | ||
| mut dest: &mut Value, | ||
| mut value: Value, | ||
| ) -> Result<Option<Value>, AssignError> { | ||
| ) -> Result<Option<Value>, Error> { | ||
| let mut offset = 0; | ||
|
|
||
| let mut position = 0; | ||
| while let Some((token, tail)) = ptr.split_front() { | ||
| let tok_len = token.encoded().len(); | ||
|
|
||
| let assigned = match dest { | ||
| Value::Array(array) => assign_array(token, tail, array, value, offset)?, | ||
| Value::Array(array) => assign_array(token, tail, array, value, position, offset)?, | ||
| Value::Object(obj) => assign_object(token, tail, obj, value), | ||
| _ => assign_scalar(ptr, dest, value), | ||
| }; | ||
|
|
@@ -273,6 +285,7 @@ mod json { | |
| } | ||
| } | ||
| offset += 1 + tok_len; | ||
| position += 1; | ||
| } | ||
|
|
||
| // Pointer is root, we can replace `dest` directly | ||
|
|
@@ -285,14 +298,23 @@ mod json { | |
| remaining: &Pointer, | ||
| array: &'v mut Vec<Value>, | ||
| src: Value, | ||
| position: usize, | ||
| offset: usize, | ||
| ) -> Result<Assigned<'v, Value>, AssignError> { | ||
| ) -> Result<Assigned<'v, Value>, Error> { | ||
| // parsing the index | ||
| let idx = token | ||
| .to_index() | ||
| .map_err(|source| AssignError::FailedToParseIndex { offset, source })? | ||
| .map_err(|source| Error::FailedToParseIndex { | ||
| position, | ||
| offset, | ||
| source, | ||
| })? | ||
| .for_len_incl(array.len()) | ||
| .map_err(|source| AssignError::OutOfBounds { offset, source })?; | ||
| .map_err(|source| Error::OutOfBounds { | ||
| position, | ||
| offset, | ||
| source, | ||
| })?; | ||
|
|
||
| debug_assert!(idx <= array.len()); | ||
|
|
||
|
|
@@ -381,7 +403,7 @@ mod json { | |
|
|
||
| #[cfg(feature = "toml")] | ||
| mod toml { | ||
| use super::{Assign, AssignError, Assigned}; | ||
| use super::{Assign, Assigned, Error}; | ||
| use crate::{Pointer, Token}; | ||
| use alloc::{string::String, vec, vec::Vec}; | ||
| use core::mem; | ||
|
|
@@ -406,7 +428,7 @@ mod toml { | |
|
|
||
| impl Assign for Value { | ||
| type Value = Value; | ||
| type Error = AssignError; | ||
| type Error = Error; | ||
| fn assign<V>(&mut self, ptr: &Pointer, value: V) -> Result<Option<Self::Value>, Self::Error> | ||
| where | ||
| V: Into<Self::Value>, | ||
|
|
@@ -419,14 +441,15 @@ mod toml { | |
| mut ptr: &Pointer, | ||
| mut dest: &mut Value, | ||
| mut value: Value, | ||
| ) -> Result<Option<Value>, AssignError> { | ||
| ) -> Result<Option<Value>, Error> { | ||
| let mut offset = 0; | ||
| let mut position = 0; | ||
|
|
||
| while let Some((token, tail)) = ptr.split_front() { | ||
| let tok_len = token.encoded().len(); | ||
|
|
||
| let assigned = match dest { | ||
| Value::Array(array) => assign_array(token, tail, array, value, offset)?, | ||
| Value::Array(array) => assign_array(token, tail, array, value, position, offset)?, | ||
| Value::Table(tbl) => assign_object(token, tail, tbl, value), | ||
| _ => assign_scalar(ptr, dest, value), | ||
| }; | ||
|
|
@@ -444,6 +467,7 @@ mod toml { | |
| } | ||
| } | ||
| offset += 1 + tok_len; | ||
| position += 1; | ||
| } | ||
|
|
||
| // Pointer is root, we can replace `dest` directly | ||
|
|
@@ -457,14 +481,23 @@ mod toml { | |
| remaining: &Pointer, | ||
| array: &'v mut Vec<Value>, | ||
| src: Value, | ||
| position: usize, | ||
| offset: usize, | ||
| ) -> Result<Assigned<'v, Value>, AssignError> { | ||
| ) -> Result<Assigned<'v, Value>, Error> { | ||
| // parsing the index | ||
| let idx = token | ||
| .to_index() | ||
| .map_err(|source| AssignError::FailedToParseIndex { offset, source })? | ||
| .map_err(|source| Error::FailedToParseIndex { | ||
| position, | ||
| offset, | ||
| source, | ||
| })? | ||
| .for_len_incl(array.len()) | ||
| .map_err(|source| AssignError::OutOfBounds { offset, source })?; | ||
| .map_err(|source| Error::OutOfBounds { | ||
| position, | ||
| offset, | ||
| source, | ||
| })?; | ||
|
|
||
| debug_assert!(idx <= array.len()); | ||
|
|
||
|
|
@@ -550,7 +583,7 @@ mod toml { | |
| #[cfg(test)] | ||
| #[allow(clippy::too_many_lines)] | ||
| mod tests { | ||
| use super::{Assign, AssignError}; | ||
| use super::{Assign, Error}; | ||
| use crate::{ | ||
| index::{OutOfBoundsError, ParseIndexError}, | ||
| Pointer, | ||
|
|
@@ -574,9 +607,6 @@ mod tests { | |
| V::Error: Debug + PartialEq, | ||
| Result<Option<V>, V::Error>: PartialEq<Result<Option<V::Value>, V::Error>>, | ||
| { | ||
| fn all(tests: impl IntoIterator<Item = Test<V>>) { | ||
| tests.into_iter().enumerate().for_each(|(i, t)| t.run(i)); | ||
| } | ||
| fn run(self, i: usize) { | ||
| let Test { | ||
| ptr, | ||
|
|
@@ -607,7 +637,7 @@ mod tests { | |
| fn assign_json() { | ||
| use alloc::vec; | ||
| use serde_json::json; | ||
| Test::all([ | ||
| [ | ||
| Test { | ||
| ptr: "/foo", | ||
| data: json!({}), | ||
|
|
@@ -731,7 +761,8 @@ mod tests { | |
| ptr: "/1", | ||
| data: json!([]), | ||
| assign: json!("foo"), | ||
| expected: Err(AssignError::OutOfBounds { | ||
| expected: Err(Error::OutOfBounds { | ||
| position: 0, | ||
| offset: 0, | ||
| source: OutOfBoundsError { | ||
| index: 1, | ||
|
|
@@ -751,7 +782,8 @@ mod tests { | |
| ptr: "/a", | ||
| data: json!([]), | ||
| assign: json!("foo"), | ||
| expected: Err(AssignError::FailedToParseIndex { | ||
| expected: Err(Error::FailedToParseIndex { | ||
| position: 0, | ||
| offset: 0, | ||
| source: ParseIndexError::InvalidInteger(usize::from_str("foo").unwrap_err()), | ||
| }), | ||
|
|
@@ -761,7 +793,8 @@ mod tests { | |
| ptr: "/002", | ||
| data: json!([]), | ||
| assign: json!("foo"), | ||
| expected: Err(AssignError::FailedToParseIndex { | ||
| expected: Err(Error::FailedToParseIndex { | ||
| position: 0, | ||
| offset: 0, | ||
| source: ParseIndexError::LeadingZeros, | ||
| }), | ||
|
|
@@ -771,13 +804,17 @@ mod tests { | |
| ptr: "/+23", | ||
| data: json!([]), | ||
| assign: json!("foo"), | ||
| expected: Err(AssignError::FailedToParseIndex { | ||
| expected: Err(Error::FailedToParseIndex { | ||
| position: 0, | ||
| offset: 0, | ||
| source: ParseIndexError::InvalidCharacters("+".into()), | ||
| }), | ||
| expected_data: json!([]), | ||
| }, | ||
| ]); | ||
| ] | ||
| .into_iter() | ||
| .enumerate() | ||
| .for_each(|(i, t)| t.run(i)); | ||
| } | ||
|
|
||
| /* | ||
|
|
@@ -791,7 +828,7 @@ mod tests { | |
| fn assign_toml() { | ||
| use alloc::vec; | ||
| use toml::{toml, Table, Value}; | ||
| Test::all([ | ||
| [ | ||
| Test { | ||
| data: Value::Table(toml::Table::new()), | ||
| ptr: "/foo", | ||
|
|
@@ -910,7 +947,8 @@ mod tests { | |
| data: Value::Array(vec![]), | ||
| ptr: "/1", | ||
| assign: "foo".into(), | ||
| expected: Err(AssignError::OutOfBounds { | ||
| expected: Err(Error::OutOfBounds { | ||
| position: 0, | ||
| offset: 0, | ||
| source: OutOfBoundsError { | ||
| index: 1, | ||
|
|
@@ -923,12 +961,16 @@ mod tests { | |
| data: Value::Array(vec![]), | ||
| ptr: "/a", | ||
| assign: "foo".into(), | ||
| expected: Err(AssignError::FailedToParseIndex { | ||
| expected: Err(Error::FailedToParseIndex { | ||
| position: 0, | ||
| offset: 0, | ||
| source: ParseIndexError::InvalidInteger(usize::from_str("foo").unwrap_err()), | ||
| }), | ||
| expected_data: Value::Array(vec![]), | ||
| }, | ||
| ]); | ||
| ] | ||
| .into_iter() | ||
| .enumerate() | ||
| .for_each(|(i, t)| t.run(i)); | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.