Skip to content
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

remaining associated types #1238

Merged
merged 2 commits into from
Mar 20, 2024
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
81 changes: 42 additions & 39 deletions src/input/input_abstract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{PyMultiHostUrl, PyUrl};

use super::datetime::{EitherDate, EitherDateTime, EitherTime, EitherTimedelta};
use super::return_enums::{EitherBytes, EitherInt, EitherString};
use super::{EitherFloat, GenericIterable, GenericIterator, ValidationMatch};
use super::{EitherFloat, GenericIterator, ValidationMatch};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum InputType {
Expand Down Expand Up @@ -162,46 +162,19 @@ pub trait Input<'py>: fmt::Debug + ToPyObject {

fn validate_list(&self, strict: bool) -> ValMatch<Self::List<'_>>;

fn validate_tuple<'a>(&'a self, strict: bool) -> ValResult<GenericIterable<'a, 'py>> {
if strict {
self.strict_tuple()
} else {
self.lax_tuple()
}
}
fn strict_tuple<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>>;
#[cfg_attr(has_coverage_attribute, coverage(off))]
fn lax_tuple<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
self.strict_tuple()
}
type Tuple<'a>: ValidatedTuple<'py>
where
Self: 'a;

fn validate_set<'a>(&'a self, strict: bool) -> ValResult<GenericIterable<'a, 'py>> {
if strict {
self.strict_set()
} else {
self.lax_set()
}
}
fn strict_set<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>>;
#[cfg_attr(has_coverage_attribute, coverage(off))]
fn lax_set<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
self.strict_set()
}
fn validate_tuple(&self, strict: bool) -> ValMatch<Self::Tuple<'_>>;

fn validate_frozenset<'a>(&'a self, strict: bool) -> ValResult<GenericIterable<'a, 'py>> {
if strict {
self.strict_frozenset()
} else {
self.lax_frozenset()
}
}
fn strict_frozenset<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>>;
#[cfg_attr(has_coverage_attribute, coverage(off))]
fn lax_frozenset<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
self.strict_frozenset()
}
type Set<'a>: ValidatedSet<'py>
where
Self: 'a;

fn validate_set(&self, strict: bool) -> ValMatch<Self::Set<'_>>;

fn extract_generic_iterable<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>>;
fn validate_frozenset(&self, strict: bool) -> ValMatch<Self::Set<'_>>;

fn validate_iter(&self) -> ValResult<GenericIterator>;

Expand Down Expand Up @@ -299,14 +272,27 @@ pub trait ValidatedDict<'py> {
) -> ValResult<R>;
}

// Optimization pathway for inputs which are already python lists
/// For validations from a list
pub trait ValidatedList<'py> {
type Item: BorrowInput<'py>;
fn len(&self) -> Option<usize>;
fn as_py_list(&self) -> Option<&Bound<'py, PyList>>;
fn iterate<R>(self, consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R>;
}

/// For validations from a tuple
pub trait ValidatedTuple<'py> {
type Item: BorrowInput<'py>;
fn len(&self) -> Option<usize>;
fn iterate<R>(self, consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R>;
}

/// For validations from a set
pub trait ValidatedSet<'py> {
type Item: BorrowInput<'py>;
fn iterate<R>(self, consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R>;
}

/// This type is used for inputs which don't support certain types.
/// It implements all the associated traits, but never actually gets called.

Expand Down Expand Up @@ -342,6 +328,23 @@ impl<'py> ValidatedList<'py> for Never {
}
}

impl<'py> ValidatedTuple<'py> for Never {
type Item = Bound<'py, PyAny>;
fn len(&self) -> Option<usize> {
unreachable!()
}
fn iterate<R>(self, _consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R> {
unreachable!()
}
}

impl<'py> ValidatedSet<'py> for Never {
type Item = Bound<'py, PyAny>;
fn iterate<R>(self, _consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R> {
unreachable!()
}
}

impl Arguments<'_> for Never {
type Args = Never;
type Kwargs = Never;
Expand Down
78 changes: 40 additions & 38 deletions src/input/input_json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use super::input_abstract::{ConsumeIterator, Never, ValMatch};
use super::return_enums::ValidationMatch;
use super::shared::{float_as_int, int_as_bool, str_as_bool, str_as_float, str_as_int};
use super::{
Arguments, BorrowInput, EitherBytes, EitherFloat, EitherInt, EitherString, EitherTimedelta, GenericIterable,
GenericIterator, Input, KeywordArgs, PositionalArgs, ValidatedDict, ValidatedList,
Arguments, BorrowInput, EitherBytes, EitherFloat, EitherInt, EitherString, EitherTimedelta, GenericIterator, Input,
KeywordArgs, PositionalArgs, ValidatedDict, ValidatedList, ValidatedSet, ValidatedTuple,
};

/// This is required but since JSON object keys are always strings, I don't think it can be called
Expand Down Expand Up @@ -185,55 +185,38 @@ impl<'py> Input<'py> for JsonValue {

fn validate_list(&self, _strict: bool) -> ValMatch<&JsonArray> {
match self {
JsonValue::Array(a) => Ok(ValidationMatch::strict(a)),
JsonValue::Array(a) => Ok(ValidationMatch::exact(a)),
_ => Err(ValError::new(ErrorTypeDefaults::ListType, self)),
}
}

fn validate_tuple<'a>(&'a self, _strict: bool) -> ValResult<GenericIterable<'a, 'py>> {
type Tuple<'a> = &'a JsonArray;

fn validate_tuple(&self, _strict: bool) -> ValMatch<&JsonArray> {
// just as in set's case, List has to be allowed
match self {
JsonValue::Array(a) => Ok(GenericIterable::JsonArray(a)),
JsonValue::Array(a) => Ok(ValidationMatch::strict(a)),
_ => Err(ValError::new(ErrorTypeDefaults::TupleType, self)),
}
}
#[cfg_attr(has_coverage_attribute, coverage(off))]
fn strict_tuple<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
self.validate_tuple(false)
}

fn validate_set<'a>(&'a self, _strict: bool) -> ValResult<GenericIterable<'a, 'py>> {
type Set<'a> = &'a JsonArray;

fn validate_set(&self, _strict: bool) -> ValMatch<&JsonArray> {
// we allow a list here since otherwise it would be impossible to create a set from JSON
match self {
JsonValue::Array(a) => Ok(GenericIterable::JsonArray(a)),
JsonValue::Array(a) => Ok(ValidationMatch::strict(a)),
_ => Err(ValError::new(ErrorTypeDefaults::SetType, self)),
}
}
#[cfg_attr(has_coverage_attribute, coverage(off))]
fn strict_set<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
self.validate_set(false)
}

fn validate_frozenset<'a>(&'a self, _strict: bool) -> ValResult<GenericIterable<'a, 'py>> {
fn validate_frozenset(&self, _strict: bool) -> ValMatch<&JsonArray> {
// we allow a list here since otherwise it would be impossible to create a frozenset from JSON
match self {
JsonValue::Array(a) => Ok(GenericIterable::JsonArray(a)),
JsonValue::Array(a) => Ok(ValidationMatch::strict(a)),
_ => Err(ValError::new(ErrorTypeDefaults::FrozenSetType, self)),
}
}
#[cfg_attr(has_coverage_attribute, coverage(off))]
fn strict_frozenset<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
self.validate_frozenset(false)
}

fn extract_generic_iterable(&self) -> ValResult<GenericIterable<'_, 'py>> {
match self {
JsonValue::Array(a) => Ok(GenericIterable::JsonArray(a)),
JsonValue::Str(s) => Ok(GenericIterable::JsonString(s)),
JsonValue::Object(object) => Ok(GenericIterable::JsonObject(object)),
_ => Err(ValError::new(ErrorTypeDefaults::IterableType, self)),
}
}

fn validate_iter(&self) -> ValResult<GenericIterator> {
match self {
Expand Down Expand Up @@ -392,23 +375,23 @@ impl<'py> Input<'py> for str {
Err(ValError::new(ErrorTypeDefaults::ListType, self))
}

type Tuple<'a> = Never;

#[cfg_attr(has_coverage_attribute, coverage(off))]
fn strict_tuple<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
fn validate_tuple(&self, _strict: bool) -> ValMatch<Never> {
Err(ValError::new(ErrorTypeDefaults::TupleType, self))
}

type Set<'a> = Never;

#[cfg_attr(has_coverage_attribute, coverage(off))]
fn strict_set<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
fn validate_set(&self, _strict: bool) -> ValMatch<Never> {
Err(ValError::new(ErrorTypeDefaults::SetType, self))
}

#[cfg_attr(has_coverage_attribute, coverage(off))]
fn strict_frozenset<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
Err(ValError::new(ErrorTypeDefaults::FrozenSetType, self))
}

fn extract_generic_iterable<'a>(&'a self) -> ValResult<GenericIterable<'a, 'py>> {
Ok(GenericIterable::JsonString(self))
fn validate_frozenset(&self, _strict: bool) -> ValMatch<Never> {
Err(ValError::new(ErrorTypeDefaults::SetType, self))
}

fn validate_iter(&self) -> ValResult<GenericIterator> {
Expand Down Expand Up @@ -504,6 +487,25 @@ impl<'a, 'py> ValidatedList<'py> for &'a JsonArray {
}
}

impl<'a, 'py> ValidatedTuple<'py> for &'a JsonArray {
type Item = &'a JsonValue;

fn len(&self) -> Option<usize> {
Some(SmallVec::len(self))
}
fn iterate<R>(self, consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R> {
Ok(consumer.consume_iterator(self.iter().map(Ok)))
}
}

impl<'a, 'py> ValidatedSet<'py> for &'a JsonArray {
type Item = &'a JsonValue;

fn iterate<R>(self, consumer: impl ConsumeIterator<PyResult<Self::Item>, Output = R>) -> ValResult<R> {
Ok(consumer.consume_iterator(self.iter().map(Ok)))
}
}

#[cfg_attr(debug_assertions, derive(Debug))]
pub struct JsonArgs<'a> {
args: Option<&'a [JsonValue]>,
Expand Down
Loading
Loading