summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Beyer <mail@beyermatthias.de>2020-01-10 19:23:39 +0100
committerMatthias Beyer <mail@beyermatthias.de>2020-05-09 10:41:08 +0200
commit96207596240651cde1f7c19bcb12ba2575a980eb (patch)
treed6a948237cfbeda1b14b3735c83748fef7fc56b7
parentba67e24138368d0c36f22fa9fab15604a0f5e94c (diff)
Switch to thiserror
-rw-r--r--Cargo.toml3
-rw-r--r--src/error.rs50
-rw-r--r--src/lib.rs4
-rw-r--r--src/object.rs25
-rw-r--r--src/read.rs49
-rw-r--r--src/resolver.rs613
6 files changed, 670 insertions, 74 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 98b0ff2..40da6c0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,8 +10,7 @@ license = "MPL-2.0"
repository = "https://github.com/matthiasbeyer/serde-select"
[dependencies]
-failure = "0.1"
-failure_derive = "0.1"
+thiserror = "1"
serde = "1"
regex = "1.0"
log = "0.4"
diff --git a/src/error.rs b/src/error.rs
index eb9d161..82eff3d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,69 +1,67 @@
/// Error types
+use thiserror::Error;
+
pub type Result<T> = ::std::result::Result<T, Error>;
-#[derive(Debug, Fail)]
+#[derive(Debug, Error)]
pub enum Error {
- #[cfg(feature = "typed")]
- #[fail(display = "{}", _0)]
- Serialize(#[cause] ::serde::ser::Error),
+ #[error("Serialization error")]
+ Serialize(/*#[from] Box<::serde::ser::Error>*/),
- #[cfg(feature = "typed")]
- #[fail(display = "{}", _0)]
- Deserialize(#[cause] ::serde::de::Error),
+ #[error("Deserialization error")]
+ Deserialize(/*#[from] Box<::serde::de::Error>*/),
// Errors for tokenizer
- #[fail(display = "Parsing the query '{}' failed", _0)]
+ #[error("Parsing the query '{}' failed", _0)]
QueryParsingError(String),
- #[fail(display = "The query on the document is empty")]
+ #[error("The query on the document is empty")]
EmptyQueryError,
- #[fail(display = "The passed query has an empty identifier")]
+ #[error("The passed query has an empty identifier")]
EmptyIdentifier,
- #[fail(display = "The passed query tries to access an array but does not specify the index")]
+ #[error("The passed query tries to access an array but does not specify the index")]
ArrayAccessWithoutIndex,
- #[fail(
- display = "The passed query tries to access an array but does not specify a valid index"
- )]
+ #[error("The passed query tries to access an array but does not specify a valid index")]
ArrayAccessWithInvalidIndex,
// Errors for Resolver
- #[fail(display = "The identfier '{}' is not present in the document", _0)]
+ #[error("The identfier '{}' is not present in the document", _0)]
IdentifierNotFoundInDocument(String),
- #[fail(display = "Got an index query '[{}]' but have table", _0)]
+ #[error("Got an index query '[{}]' but have table", _0)]
NoIndexInTable(usize),
- #[fail(display = "Got an identifier query '{}' but have array", _0)]
+ #[error("Got an identifier query '{}' but have array", _0)]
NoIdentifierInArray(String),
- #[fail(display = "Got an identifier query '{}' but have value", _0)]
+ #[error("Got an identifier query '{}' but have value", _0)]
QueryingValueAsTable(String),
- #[fail(display = "Got an index query '{}' but have value", _0)]
+ #[error("Got an index query '{}' but have value", _0)]
QueryingValueAsArray(usize),
- #[fail(display = "Cannot delete table '{:?}' which is not empty", _0)]
+ #[error("Cannot delete table '{:?}' which is not empty", _0)]
CannotDeleteNonEmptyTable(Option<String>),
- #[fail(display = "Cannot delete array '{:?}' which is not empty", _0)]
+ #[error("Cannot delete array '{:?}' which is not empty", _0)]
CannotDeleteNonEmptyArray(Option<String>),
- #[fail(display = "Cannot access {} because expected {}", _0, _1)]
+ #[error("Cannot access {} because expected {}", _0, _1)]
CannotAccessBecauseTypeMismatch(&'static str, &'static str),
- #[fail(display = "Cannot delete in array at {}, array has length {}", _0, _1)]
+ #[error("Cannot delete in array at {}, array has length {}", _0, _1)]
ArrayIndexOutOfBounds(usize, usize),
- #[fail(display = "Cannot access array at {}, array has length {}", _0, _1)]
+ #[error("Cannot access array at {}, array has length {}", _0, _1)]
IndexOutOfBounds(usize, usize),
- #[fail(display = "Type Error. Requested {}, but got {}", _0, _1)]
+ #[error("Type Error. Requested {}, but got {}", _0, _1)]
TypeError(&'static str, &'static str),
- #[fail(display = "Value at '{}' not there", _0)]
+ #[error("Value at '{}' not there", _0)]
NotAvailable(String),
}
diff --git a/src/lib.rs b/src/lib.rs
index 5e9f209..bfe3909 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,6 +1,5 @@
#[macro_use] extern crate log;
-#[macro_use] extern crate failure;
-#[macro_use] extern crate failure_derive;
+#[macro_use] extern crate thiserror;
#[macro_use] extern crate regex;
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate is_match;
@@ -16,3 +15,4 @@ pub mod query;
pub mod read;
mod tokenizer;
+mod resolver;
diff --git a/src/object.rs b/src/object.rs
index e0eae87..640ade5 100644
--- a/src/object.rs
+++ b/src/object.rs
@@ -1,12 +1,15 @@
use crate::error::*;
+use serde::Serialize;
+use serde::Deserialize;
+
pub enum ObjectType {
Atom,
Map,
Array,
}
-pub trait Object {
+pub trait Object<'doc>: Serialize + Deserialize<'doc> + Clone {
fn get_type(&self) -> ObjectType;
fn has_key(&self, key: &str) -> bool;
fn has_index(&self, idx: usize) -> bool;
@@ -15,10 +18,12 @@ pub trait Object {
fn at_index_mut<'a>(&'a mut self, idx: usize) -> Result<Option<&'a mut Self>>;
fn at_key<'a>(&'a self, key: &str) -> Result<Option<&'a Self>>;
fn at_key_mut<'a>(&'a mut self, key: &str) -> Result<Option<&'a mut Self>>;
+
+ fn array_len(&self) -> Option<usize>;
}
#[cfg(feature = "backend_toml")]
-impl Object for toml::Value {
+impl<'doc> Object<'doc> for toml::Value {
fn get_type(&self) -> ObjectType {
match self {
toml::Value::Boolean(_)
@@ -77,11 +82,18 @@ impl Object for toml::Value {
_ => Err(crate::error::Error::QueryingValueAsTable(key.to_string())),
}
}
+
+ fn array_len(&self) -> Option<usize> {
+ match self {
+ toml::Value::Array(a) => Some(a.len()),
+ _ => None ,
+ }
+ }
}
#[cfg(feature = "backend_serde_json")]
-impl Object for serde_json::Value {
+impl<'doc> Object<'doc> for serde_json::Value {
fn get_type(&self) -> ObjectType {
match self {
serde_json::Value::Null
@@ -139,5 +151,12 @@ impl Object for serde_json::Value {
_ => Err(crate::error::Error::QueryingValueAsTable(key.to_string())),
}
}
+
+ fn array_len(&self) -> Option<usize> {
+ match self {
+ serde_json::Value::Array(a) => Some(a.len()),
+ _ => None ,
+ }
+ }
}
diff --git a/src/read.rs b/src/read.rs
index 4779ce5..4847422 100644
--- a/src/read.rs
+++ b/src/read.rs
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
+use std::convert::TryInto;
use crate::error::*;
use crate::tokenizer::tokenize_with_seperator;
@@ -6,13 +7,13 @@ use crate::object::*;
use crate::tokenizer::Token;
use crate::query::Query;
-pub trait Read<'doc> : Object + Sized {
+pub trait Read<'doc> : Object<'doc> + Sized {
fn read(&'doc self, query: &Query) -> Result<Option<&'doc Self>> {
- query_with_token(self, query.token())
- }
-
- fn read_mut(&'doc mut self, query: &Query) -> Result<Option<&'doc mut Self>> {
- query_with_token_mut(self, query.token())
+ //match query_with_token(self, query.token())? {
+ // None => Ok(None),
+ // Some(o) => Ok(Some(o.as_deserialize())),
+ //}
+ unimplemented!()
}
}
@@ -23,7 +24,7 @@ impl<'doc> Read<'doc> for toml::Value { }
impl<'doc> Read<'doc> for serde_json::Value { }
fn query_with_token<'doc, O>(obj: &'doc O, token: &Token) -> Result<Option<&'doc O>>
- where O: Object
+ where O: Object<'doc>
{
match token {
Token::Identifier{ ident, .. } => {
@@ -54,37 +55,3 @@ fn query_with_token<'doc, O>(obj: &'doc O, token: &Token) -> Result<Option<&'doc
}
}
-fn query_with_token_mut<'doc, O>(obj: &'doc mut O, token: &Token) -> Result<Option<&'doc mut O>>
- where O: Object
-{
- match token {
- Token::Identifier{ ident, .. } => {
- let object = obj.at_key_mut(ident)?;
- if let Some(object) = object {
- if let Some(next_token) = token.next() {
- query_with_token_mut(object, next_token)
- } else {
- Ok(Some(object))
- }
- } else {
- Ok(None)
- }
- }
-
- Token::Index { idx, .. } => {
- let object = obj.at_index_mut(*idx)?;
- if let Some(object) = object {
- if let Some(next_token) = token.next() {
- query_with_token_mut(object, next_token)
- } else {
- Ok(Some(object))
- }
- } else {
- Ok(None)
- }
- }
- }
-}
-
-
-
diff --git a/src/resolver.rs b/src/resolver.rs
new file mode 100644
index 0000000..e34c6d0
--- /dev/null
+++ b/src/resolver.rs
@@ -0,0 +1,613 @@
+/// The query resolver that operates on the AST and the TOML object
+use std::ops::Index;
+
+use serde::Deserialize;
+
+use crate::error::{Error, Result};
+use crate::tokenizer::Token;
+use crate::object::{Object, ObjectType};
+
+pub fn resolve<'doc, O, D>(obj: &'doc O, tokens: &Token, error_if_not_found: bool)
+ -> Result<Option<&'doc D>>
+ where D: Deserialize<'doc>,
+ O: Object<'doc>
+{
+ match obj.get_type() {
+ ObjectType::Map => {
+ match tokens {
+ Token::Identifier { ref ident, .. } => match obj.at_key(ident)? {
+ None => if error_if_not_found {
+ Err(Error::IdentifierNotFoundInDocument(ident.to_owned()))
+ } else {
+ Ok(None)
+ }
+ Some(sub_document) => match tokens.next() {
+ Some(next) => resolve(sub_document, next, error_if_not_found),
+ None => Ok(Some(sub_document)),
+ },
+ },
+
+ Token::Index { idx, .. } => Err(Error::NoIndexInTable(*idx)),
+ }
+ },
+
+ ObjectType::Array => {
+ match tokens {
+ Token::Index { idx, .. } => match tokens.next() {
+ Some(next) => {
+ if let Some(subobj) = obj.at_index(*idx)? {
+ resolve(subobj, next, error_if_not_found)
+ } else {
+ Ok(None)
+ }
+ },
+ None => {
+ ary.at_index(*idx).ok_or_else(|| {
+ Error::IndexOutOfBounds(*idx, obj.array_len().unwrap()) // unwrap is safe here
+ })
+ }
+ },
+ Token::Identifier { ref ident, .. } => Err(Error::NoIdentifierInArray(ident.clone())),
+ }
+ },
+
+ ObjectType::Atom => {
+ match tokens {
+ Token::Identifier { ref ident, .. } => Err(Error::QueryingValueAsTable(ident.clone())),
+ Token::Index { idx, .. } => Err(Error::QueryingValueAsArray(*idx)),
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::resolve;
+ use crate::error::*;
+ use crate::tokenizer::*;
+ use toml::from_str as toml_from_str;
+ use toml::Value;
+
+ macro_rules! do_resolve {
+ ( $toml:ident => $query:expr ) => {
+ resolve(
+ &$toml,
+ &tokenize_with_seperator(&String::from($query), '.').unwrap(),
+ true,
+ )
+ };
+ }
+
+ #[test]
+ fn test_resolve_empty_toml_simple_query() {
+ let toml = toml_from_str("").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::IdentifierNotFoundInDocument { .. }));
+ }
+
+ #[test]
+ fn test_resolve_present_bool() {
+ let toml = toml_from_str("example = true").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Boolean(true)));
+ }
+
+ #[test]
+ fn test_resolve_present_integer() {
+ let toml = toml_from_str("example = 1").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Integer(1)));
+ }
+
+ #[test]
+ fn test_resolve_present_float() {
+ let toml = toml_from_str("example = 1.0").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Float(_)));
+ assert_eq!(result.as_float(), Some(1.0))
+ }
+
+ #[test]
+ fn test_resolve_present_string() {
+ let toml = toml_from_str("example = 'string'").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::String(_)));
+ match result {
+ Value::String(ref s) => assert_eq!("string", s),
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_present_array_bools() {
+ let toml = toml_from_str("example = [ true, false ]").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Array(_)));
+ match result {
+ Value::Array(ref ary) => {
+ assert_eq!(ary[0], Value::Boolean(true));
+ assert_eq!(ary[1], Value::Boolean(false));
+ }
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_present_array_integers() {
+ let toml = toml_from_str("example = [ 1, 1337 ]").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Array(_)));
+ match result {
+ Value::Array(ref ary) => {
+ assert_eq!(ary[0], Value::Integer(1));
+ assert_eq!(ary[1], Value::Integer(1337));
+ }
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_present_array_floats() {
+ let toml = toml_from_str("example = [ 1.0, 133.25 ]").unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Array(_)));
+ match result {
+ Value::Array(ref ary) => {
+ assert!(is_match!(ary[0], Value::Float(_)));
+ assert_eq!(ary[0].as_float(), Some(1.0));
+ assert!(is_match!(ary[1], Value::Float(_)));
+ assert_eq!(ary[1].as_float(), Some(133.25));
+ }
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_array_index_query_1() {
+ let toml = toml_from_str("example = [ 1 ]").unwrap();
+ let result = do_resolve!(toml => "example.[0]");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Integer(1)));
+ }
+
+ #[test]
+ fn test_resolve_array_index_query_2() {
+ let toml = toml_from_str("example = [ 1, 2, 3, 4, 5 ]").unwrap();
+ let result = do_resolve!(toml => "example.[4]");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Integer(5)));
+ }
+
+ #[test]
+ fn test_resolve_table_element_query() {
+ let toml = toml_from_str(
+ r#"
+ [table]
+ value = 42
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "table.value");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Integer(42)));
+ }
+
+ #[test]
+ fn test_resolve_table_with_many_elements_element_query() {
+ let toml = toml_from_str(
+ r#"
+ [table]
+ value1 = 42
+ value2 = 43
+ value3 = 44
+ value4 = 45
+ value5 = 46
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "table.value1");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Integer(42)));
+ }
+
+ #[test]
+ fn test_resolve_table_array_query() {
+ let toml = toml_from_str(
+ r#"
+ [table]
+ value1 = [ 42.0, 50.0 ]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "table.value1");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Array(_)));
+ match result {
+ Value::Array(ref ary) => {
+ assert!(is_match!(ary[0], Value::Float(_)));
+ assert_eq!(ary[0].as_float(), Some(42.0));
+ assert!(is_match!(ary[1], Value::Float(_)));
+ assert_eq!(ary[1].as_float(), Some(50.0));
+ }
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_table_array_element_query() {
+ let toml = toml_from_str(
+ r#"
+ [table]
+ value1 = [ 42 ]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "table.value1.[0]");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Integer(42)));
+ }
+
+ #[test]
+ fn test_resolve_multi_table_query() {
+ let toml = toml_from_str(
+ r#"
+ [table0]
+ value = [ 1 ]
+ [table1]
+ value = [ "Foo" ]
+ [table2]
+ value = [ 42.0 ]
+ [table3]
+ value = [ true ]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "table1.value.[0]");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::String(_)));
+ match result {
+ Value::String(ref s) => assert_eq!("Foo", s),
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ static FRUIT_TABLE: &str = r#"
+ [[fruit.blah]]
+ name = "apple"
+
+ [fruit.blah.physical]
+ color = "red"
+ shape = "round"
+
+ [[fruit.blah]]
+ name = "banana"
+
+ [fruit.blah.physical]
+ color = "yellow"
+ shape = "bent"
+ "#;
+
+ #[test]
+ fn test_resolve_array_table_query_1() {
+ let toml = toml_from_str(FRUIT_TABLE).unwrap();
+ let result = do_resolve!(toml => "fruit.blah.[0].name");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::String(_)));
+ match result {
+ Value::String(ref s) => assert_eq!("apple", s),
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_array_table_query_2() {
+ let toml = toml_from_str(FRUIT_TABLE).unwrap();
+ let result = do_resolve!(toml => "fruit.blah.[0].physical");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Table(_)));
+ match result {
+ Value::Table(ref tab) => {
+ match tab.get("color") {
+ Some(&Value::String(ref s)) => assert_eq!("red", s),
+ _ => unreachable!(),
+ }
+ match tab.get("shape") {
+ Some(&Value::String(ref s)) => assert_eq!("round", s),
+ _ => unreachable!(),
+ }
+ }
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_query_on_result() {
+ let toml = toml_from_str(FRUIT_TABLE).unwrap();
+ let result = do_resolve!(toml => "fruit.blah.[1].physical");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ let tokens = tokenize_with_seperator(&String::from("color"), '.').unwrap();
+ let result = resolve(result, &tokens, true);
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::String(_)));
+ match result {
+ Value::String(ref s) => assert_eq!("yellow", s),
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_query_empty_table() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example");
+
+ assert!(result.is_ok());
+ let result = result.unwrap();
+
+ assert!(result.is_some());
+ let result = result.unwrap();
+
+ assert!(is_match!(result, Value::Table(_)));
+ match result {
+ Value::Table(ref t) => assert!(t.is_empty()),
+ _ => panic!("What just happened?"),
+ }
+ }
+
+ #[test]
+ fn test_resolve_query_member_of_empty_table() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::IdentifierNotFoundInDocument { .. }));
+ }
+
+ #[test]
+ fn test_resolve_query_index_in_table() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.[0]");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::NoIndexInTable { .. }));
+ }
+
+ #[test]
+ fn test_resolve_query_identifier_in_array() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ foo = [ 1, 2, 3 ]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo.bar");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::NoIdentifierInArray { .. }));
+ }
+
+ #[test]
+ fn test_resolve_query_value_as_table() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ foo = 1
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo.bar");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::QueryingValueAsTable { .. }));
+ }
+
+ #[test]
+ fn test_resolve_query_value_as_array() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ foo = 1
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo.[0]");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::QueryingValueAsArray { .. }));
+ }
+
+ #[test]
+ fn test_indexing_out_of_bounds() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ foo = [ 1, 2, 3 ]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo.[12]");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::IndexOutOfBounds { .. }));
+ }
+
+ #[test]
+ fn test_indexing_out_of_bounds_edgecase_1() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ foo = []
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo.[0]");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::IndexOutOfBounds { .. }));
+ }
+
+ #[test]
+ fn test_indexing_out_of_bounds_edgecase_2() {
+ let toml = toml_from_str(
+ r#"
+ [example]
+ foo = [ 1 ]
+ "#,
+ )
+ .unwrap();
+ let result = do_resolve!(toml => "example.foo.[1]");
+
+ assert!(result.is_err());
+ let result = result.unwrap_err();
+
+ assert!(is_match!(result, Error::IndexOutOfBounds { .. }));
+ }
+}