Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/errors/line_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ impl ValError {
}

/// helper function to call with_outer on line items if applicable
pub fn with_outer_location(self, loc_item: LocItem) -> Self {
pub fn with_outer_location(self, into_loc_item: impl Into<LocItem>) -> Self {
let loc_item = into_loc_item.into();
match self {
Self::LineErrors(mut line_errors) => {
for line_error in &mut line_errors {
Expand Down Expand Up @@ -120,8 +121,8 @@ impl ValLineError {

/// location is stored reversed so it's quicker to add "outer" items as that's what we always do
/// hence `push` here instead of `insert`
pub fn with_outer_location(mut self, loc_item: LocItem) -> Self {
self.location.with_outer(loc_item);
pub fn with_outer_location(mut self, into_loc_item: impl Into<LocItem>) -> Self {
self.location.with_outer(into_loc_item.into());
self
}

Expand Down
16 changes: 8 additions & 8 deletions src/errors/location.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,18 @@ impl fmt::Display for LocItem {
}
}

// TODO rename to ToLocItem
pub trait AsLocItem {
// TODO rename to to_loc_item
fn as_loc_item(&self) -> LocItem;
}

impl From<String> for LocItem {
fn from(s: String) -> Self {
Self::S(s)
}
}

impl From<&String> for LocItem {
fn from(s: &String) -> Self {
s.to_string().into()
}
}

impl From<&str> for LocItem {
fn from(s: &str) -> Self {
Self::S(s.to_string())
Expand Down Expand Up @@ -201,9 +201,9 @@ impl TryFrom<Option<&PyAny>> for Location {
fn try_from(location: Option<&PyAny>) -> PyResult<Self> {
if let Some(location) = location {
let mut loc_vec: Vec<LocItem> = if let Ok(tuple) = location.downcast::<PyTuple>() {
tuple.iter().map(AsLocItem::as_loc_item).collect()
tuple.iter().map(Into::into).collect()
} else if let Ok(list) = location.downcast::<PyList>() {
list.iter().map(AsLocItem::as_loc_item).collect()
list.iter().map(Into::into).collect()
} else {
return Err(PyTypeError::new_err(
"Location must be a list or tuple of strings and ints",
Expand Down
2 changes: 1 addition & 1 deletion src/errors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ mod validation_exception;
mod value_exception;

pub use self::line_error::{AsErrorValue, InputValue, ValError, ValLineError, ValResult};
pub use self::location::{AsLocItem, LocItem};
pub use self::location::LocItem;
pub use self::types::{list_all_errors, ErrorType, ErrorTypeDefaults, Number};
pub use self::validation_exception::ValidationError;
pub use self::value_exception::{PydanticCustomError, PydanticKnownError, PydanticOmit, PydanticUseDefault};
Expand Down
4 changes: 2 additions & 2 deletions src/input/input_abstract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use pyo3::exceptions::PyValueError;
use pyo3::types::{PyDict, PyType};
use pyo3::{intern, prelude::*};

use crate::errors::{AsLocItem, ErrorTypeDefaults, InputValue, ValError, ValResult};
use crate::errors::{ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::tools::py_err;
use crate::{PyMultiHostUrl, PyUrl};

Expand Down Expand Up @@ -46,7 +46,7 @@ impl TryFrom<&str> for InputType {
/// the convention is to either implement:
/// * `strict_*` & `lax_*` if they have different behavior
/// * or, `validate_*` and `strict_*` to just call `validate_*` if the behavior for strict and lax is the same
pub trait Input<'a>: fmt::Debug + ToPyObject + AsLocItem + Sized {
pub trait Input<'a>: fmt::Debug + ToPyObject + Into<LocItem> + Sized {
fn as_error_value(&self) -> InputValue;

fn identity(&self) -> Option<usize> {
Expand Down
40 changes: 14 additions & 26 deletions src/input/input_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use pyo3::types::{PyDict, PyString};
use speedate::MicrosecondsPrecisionOverflowBehavior;
use strum::EnumMessage;

use crate::errors::{AsLocItem, ErrorType, ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::errors::{ErrorType, ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::validators::decimal::create_decimal;

use super::datetime::{
Expand All @@ -21,19 +21,19 @@ use super::{
};

/// This is required but since JSON object keys are always strings, I don't think it can be called
impl AsLocItem for JsonValue {
fn as_loc_item(&self) -> LocItem {
match self {
impl From<&JsonValue> for LocItem {
fn from(json_value: &JsonValue) -> Self {
match json_value {
JsonValue::Int(i) => (*i).into(),
JsonValue::Str(s) => s.as_str().into(),
v => format!("{v:?}").into(),
}
}
}

impl AsLocItem for &JsonValue {
fn as_loc_item(&self) -> LocItem {
AsLocItem::as_loc_item(*self)
impl From<JsonValue> for LocItem {
fn from(json_value: JsonValue) -> Self {
(&json_value).into()
}
}

Expand Down Expand Up @@ -84,13 +84,6 @@ impl<'a> Input<'a> for JsonValue {
}
}

fn exact_str(&'a self) -> ValResult<EitherString<'a>> {
match self {
JsonValue::Str(s) => Ok(s.as_str().into()),
_ => Err(ValError::new(ErrorTypeDefaults::StringType, self)),
}
}

fn validate_str(
&'a self,
strict: bool,
Expand Down Expand Up @@ -144,6 +137,13 @@ impl<'a> Input<'a> for JsonValue {
}
}

fn exact_str(&'a self) -> ValResult<EitherString<'a>> {
match self {
JsonValue::Str(s) => Ok(s.as_str().into()),
_ => Err(ValError::new(ErrorTypeDefaults::StringType, self)),
}
}

fn validate_float(&'a self, strict: bool) -> ValResult<ValidationMatch<EitherFloat<'a>>> {
match self {
JsonValue::Float(f) => Ok(ValidationMatch::exact(EitherFloat::F64(*f))),
Expand Down Expand Up @@ -319,18 +319,6 @@ impl BorrowInput for &'_ JsonValue {
}
}

impl AsLocItem for String {
fn as_loc_item(&self) -> LocItem {
self.to_string().into()
}
}

impl AsLocItem for &String {
fn as_loc_item(&self) -> LocItem {
AsLocItem::as_loc_item(*self)
}
}

/// Required for JSON Object keys so the string can behave like an Input
impl<'a> Input<'a> for String {
fn as_error_value(&self) -> InputValue {
Expand Down
50 changes: 25 additions & 25 deletions src/input/input_python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use pyo3::{intern, PyTypeInfo};

use speedate::MicrosecondsPrecisionOverflowBehavior;

use crate::errors::{AsLocItem, ErrorType, ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::errors::{ErrorType, ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::tools::{extract_i64, safe_repr};
use crate::validators::decimal::{create_decimal, get_decimal_type};
use crate::validators::Exactness;
Expand Down Expand Up @@ -92,21 +92,21 @@ macro_rules! extract_dict_items {
};
}

impl AsLocItem for PyAny {
fn as_loc_item(&self) -> LocItem {
if let Ok(py_str) = self.downcast::<PyString>() {
impl From<&PyAny> for LocItem {
fn from(py_any: &PyAny) -> Self {
if let Ok(py_str) = py_any.downcast::<PyString>() {
py_str.to_string_lossy().as_ref().into()
} else if let Some(key_int) = extract_i64(self) {
} else if let Some(key_int) = extract_i64(py_any) {
key_int.into()
} else {
safe_repr(self).to_string().into()
safe_repr(py_any).to_string().into()
}
}
}

impl AsLocItem for &'_ PyAny {
fn as_loc_item(&self) -> LocItem {
AsLocItem::as_loc_item(*self)
impl From<PyAny> for LocItem {
fn from(py_any: PyAny) -> Self {
(&py_any).into()
}
}

Expand Down Expand Up @@ -244,22 +244,6 @@ impl<'a> Input<'a> for PyAny {
Err(ValError::new(ErrorTypeDefaults::StringType, self))
}

fn exact_str(&'a self) -> ValResult<EitherString<'a>> {
if let Ok(py_str) = <PyString as PyTryFrom>::try_from_exact(self) {
Ok(EitherString::Py(py_str))
} else {
Err(ValError::new(ErrorTypeDefaults::IntType, self))
}
}

fn exact_int(&'a self) -> ValResult<EitherInt<'a>> {
if PyInt::is_exact_type_of(self) {
Ok(EitherInt::Py(self))
} else {
Err(ValError::new(ErrorTypeDefaults::IntType, self))
}
}

fn validate_bytes(&'a self, strict: bool) -> ValResult<ValidationMatch<EitherBytes<'a>>> {
if let Ok(py_bytes) = self.downcast_exact::<PyBytes>() {
return Ok(ValidationMatch::exact(py_bytes.into()));
Expand Down Expand Up @@ -347,6 +331,22 @@ impl<'a> Input<'a> for PyAny {
Err(ValError::new(ErrorTypeDefaults::IntType, self))
}

fn exact_int(&'a self) -> ValResult<EitherInt<'a>> {
if PyInt::is_exact_type_of(self) {
Ok(EitherInt::Py(self))
} else {
Err(ValError::new(ErrorTypeDefaults::IntType, self))
}
}

fn exact_str(&'a self) -> ValResult<EitherString<'a>> {
if let Ok(py_str) = <PyString as PyTryFrom>::try_from_exact(self) {
Ok(EitherString::Py(py_str))
} else {
Err(ValError::new(ErrorTypeDefaults::IntType, self))
}
}

fn validate_float(&'a self, strict: bool) -> ValResult<ValidationMatch<EitherFloat<'a>>> {
if let Ok(float) = self.downcast_exact::<PyFloat>() {
return Ok(ValidationMatch::exact(EitherFloat::Py(float)));
Expand Down
14 changes: 7 additions & 7 deletions src/input/input_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use pyo3::types::{PyDict, PyString};

use speedate::MicrosecondsPrecisionOverflowBehavior;

use crate::errors::{AsLocItem, ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::errors::{ErrorTypeDefaults, InputValue, LocItem, ValError, ValResult};
use crate::input::py_string_str;
use crate::tools::safe_repr;
use crate::validators::decimal::create_decimal;
Expand All @@ -17,7 +17,7 @@ use super::{
GenericIterator, GenericMapping, Input, ValidationMatch,
};

#[derive(Debug)]
#[derive(Debug, Clone)]
pub enum StringMapping<'py> {
String(&'py PyString),
Mapping(&'py PyDict),
Expand Down Expand Up @@ -52,11 +52,11 @@ impl<'py> StringMapping<'py> {
}
}

impl AsLocItem for StringMapping<'_> {
fn as_loc_item(&self) -> LocItem {
match self {
Self::String(s) => s.to_string_lossy().as_ref().into(),
Self::Mapping(d) => safe_repr(d).to_string().into(),
impl From<StringMapping<'_>> for LocItem {
fn from(string_mapping: StringMapping<'_>) -> Self {
match string_mapping {
StringMapping::String(s) => s.to_string_lossy().as_ref().into(),
StringMapping::Mapping(d) => safe_repr(d).to_string().into(),
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/input/return_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ fn validate_iter_to_vec<'a, 's>(
}
Err(ValError::LineErrors(line_errors)) => {
max_length_check.incr()?;
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index.into())));
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index)));
}
Err(ValError::Omit) => (),
Err(err) => return Err(err),
Expand Down Expand Up @@ -284,7 +284,7 @@ fn validate_iter_to_set<'a, 's>(
}
}
Err(ValError::LineErrors(line_errors)) => {
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index.into())));
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index)));
}
Err(ValError::Omit) => (),
Err(err) => return Err(err),
Expand Down
4 changes: 2 additions & 2 deletions src/lookup_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,11 @@ impl LookupPath {
pub fn apply_error_loc(&self, mut line_error: ValLineError, loc_by_alias: bool, field_name: &str) -> ValLineError {
if loc_by_alias {
for path_item in self.iter().rev() {
line_error = line_error.with_outer_location(path_item.clone().into());
line_error = line_error.with_outer_location(path_item.clone());
}
line_error
} else {
line_error.with_outer_location(field_name.to_string().into())
line_error.with_outer_location(field_name)
}
}

Expand Down
12 changes: 6 additions & 6 deletions src/validators/arguments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ahash::AHashSet;

use crate::build_tools::py_schema_err;
use crate::build_tools::{schema_or_config_same, ExtraBehavior};
use crate::errors::{AsLocItem, ErrorTypeDefaults, ValError, ValLineError, ValResult};
use crate::errors::{ErrorTypeDefaults, ValError, ValLineError, ValResult};
use crate::input::{GenericArguments, Input, ValidationMatch};
use crate::lookup_key::LookupKey;

Expand Down Expand Up @@ -209,7 +209,7 @@ impl Validator for ArgumentsValidator {
{
Ok(value) => output_args.push(value),
Err(ValError::LineErrors(line_errors)) => {
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index.into())));
errors.extend(line_errors.into_iter().map(|err| err.with_outer_location(index)));
}
Err(err) => return Err(err),
}
Expand Down Expand Up @@ -263,7 +263,7 @@ impl Validator for ArgumentsValidator {
errors.extend(
line_errors
.into_iter()
.map(|err| err.with_outer_location((index + self.positional_params_count).into())),
.map(|err| err.with_outer_location(index + self.positional_params_count)),
);
}
Err(err) => return Err(err),
Expand All @@ -289,7 +289,7 @@ impl Validator for ArgumentsValidator {
Err(ValError::LineErrors(line_errors)) => {
for err in line_errors {
errors.push(
err.with_outer_location(raw_key.as_loc_item())
err.with_outer_location(raw_key)
.with_type(ErrorTypeDefaults::InvalidKey),
);
}
Expand All @@ -303,7 +303,7 @@ impl Validator for ArgumentsValidator {
Ok(value) => output_kwargs.set_item(either_str.as_py_string(py), value)?,
Err(ValError::LineErrors(line_errors)) => {
for err in line_errors {
errors.push(err.with_outer_location(raw_key.as_loc_item()));
errors.push(err.with_outer_location(raw_key));
}
}
Err(err) => return Err(err),
Expand All @@ -313,7 +313,7 @@ impl Validator for ArgumentsValidator {
errors.push(ValLineError::new_with_loc(
ErrorTypeDefaults::UnexpectedKeywordArgument,
value,
raw_key.as_loc_item(),
raw_key,
));
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/validators/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl Validator for CallValidator {
if let Some(return_validator) = &self.return_validator {
return_validator
.validate(py, return_value.into_ref(py), state)
.map_err(|e| e.with_outer_location("return".into()))
.map_err(|e| e.with_outer_location("return"))
} else {
Ok(return_value.to_object(py))
}
Expand Down
Loading