From 36395c7437218b86c832cc041f7a58f325a007bb Mon Sep 17 00:00:00 2001 From: SoniEx2 Date: Sat, 4 Dec 2021 17:46:00 -0300 Subject: Add integer and float arguments --- tests/arguments.rs | 158 +++++++++++++++++++++++++++++++++++++++++++++ tests/common/errorfunc.rs | 66 ++++++++++++++++++- tests/common/errorpanic.rs | 21 +++++- 3 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 tests/arguments.rs (limited to 'tests') diff --git a/tests/arguments.rs b/tests/arguments.rs new file mode 100644 index 0000000..4b5a2a0 --- /dev/null +++ b/tests/arguments.rs @@ -0,0 +1,158 @@ +// Copyright (c) 2021 Soni L. +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +// Documentation and comments licensed under CC BY-SA 4.0. + +// because we wanna use double underscore (__) for test names +#![allow(non_snake_case)] + +use ::std::io::Cursor; +use ::std::marker::PhantomData; + +use ::iosonism::args::ArgumentType; +use ::iosonism::args::BoolArgumentType; +use ::iosonism::args::FloatArgumentType; +use ::iosonism::args::IntegerArgumentType; +use ::iosonism::strcursor::StringReader; + +mod common; + +use self::common::ErrorCall; +use self::common::ErrorFunc; +use self::common::ErrorPanic; +use self::common::ErrorType; + +#[test] +fn test_bool__parse() { + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &BoolArgumentType, + &mut Cursor::new("true"), + ), + Ok(true), + ); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &BoolArgumentType, + &mut Cursor::new("false"), + ), + Ok(false), + ); +} + +#[test] +fn test_i32__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &IntegerArgumentType { range: .., _ty: PhantomData:: }, + &mut reader, + ), + Ok(15), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_i32__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall>::parse( + &IntegerArgumentType { range: 0..=100, _ty: PhantomData:: }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrori32(..))); + assert_eq!(context.position(), 0); + } + } +} + +#[test] +fn test_i64__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &IntegerArgumentType { range: .., _ty: PhantomData:: }, + &mut reader, + ), + Ok(15), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_i64__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall>::parse( + &IntegerArgumentType { range: 0..=100, _ty: PhantomData:: }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrori64(..))); + assert_eq!(context.position(), 0); + } + } +} + +#[test] +fn test_f32__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &FloatArgumentType { range: .., _ty: PhantomData:: }, + &mut reader, + ), + Ok(15.0), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_f32__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall>::parse( + &FloatArgumentType { range: 0.0..=100.0, _ty: PhantomData:: }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrorf32(..))); + assert_eq!(context.position(), 0); + } + } +} + +#[test] +fn test_f64__parse() { + let mut reader = Cursor::new("15"); + assert_eq!( + ArgumentType::<(), ErrorPanic>::parse( + &FloatArgumentType { range: .., _ty: PhantomData:: }, + &mut reader, + ), + Ok(15.0), + ); + assert!(!reader.can_read()); +} + +#[test] +fn test_f64__parse__range() { + let mut reader = Cursor::new("-5"); + assert!(ArgumentType::<(), ErrorCall>::parse( + &FloatArgumentType { range: 0.0..=100.0, _ty: PhantomData:: }, + &mut reader, + ).is_err()); + struct ErrFn; + impl<'a> ErrorFunc<'a, Cursor<&'a str>> for ErrFn { + fn call(context: &Cursor<&'a str>, ty: ErrorType) { + assert!(matches!(ty, ErrorType::RangeErrorf64(..))); + assert_eq!(context.position(), 0); + } + } +} + diff --git a/tests/common/errorfunc.rs b/tests/common/errorfunc.rs index 926c94c..5fb5fb6 100644 --- a/tests/common/errorfunc.rs +++ b/tests/common/errorfunc.rs @@ -4,8 +4,11 @@ // Documentation and comments licensed under CC BY-SA 4.0. use ::std::marker::PhantomData; +use ::std::ops::Bound; +use ::std::ops::RangeBounds; -use ::iosonism::strcursor::ReadError; +use ::iosonism::error::RangeError; +use ::iosonism::error::ReadError; use ::iosonism::strcursor::StringReader; /// An error callback. @@ -17,7 +20,7 @@ pub trait ErrorFunc<'a, C: StringReader<'a>> { pub struct ErrorCall(PhantomData); #[non_exhaustive] -#[derive(PartialEq, Eq, Debug)] +#[derive(PartialEq, Debug)] pub enum ErrorType<'a> { InvalidInteger(&'a str), ExpectedInteger, @@ -29,6 +32,17 @@ pub enum ErrorType<'a> { ExpectedEndOfQuote, InvalidEscape(&'a str), ExpectedSymbol(&'a str), + RangeErrori32(RangeErrorT), + RangeErrori64(RangeErrorT), + RangeErrorf32(RangeErrorT), + RangeErrorf64(RangeErrorT), +} + +#[derive(PartialEq, Debug)] +pub struct RangeErrorT { + pub value: T, + pub start: Bound, + pub end: Bound, } impl ::std::fmt::Display for ErrorCall { @@ -46,6 +60,54 @@ impl ::std::fmt::Debug for ErrorCall { impl ::std::error::Error for ErrorCall { } +impl<'a, C, R, T> RangeError<'a, C, i32, R> for ErrorCall +where C: StringReader<'a>, R: RangeBounds, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &i32, range: &R) -> Self { + T::call(context, ErrorType::RangeErrori32(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + +impl<'a, C, R, T> RangeError<'a, C, i64, R> for ErrorCall +where C: StringReader<'a>, R: RangeBounds, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &i64, range: &R) -> Self { + T::call(context, ErrorType::RangeErrori64(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + +impl<'a, C, R, T> RangeError<'a, C, f32, R> for ErrorCall +where C: StringReader<'a>, R: RangeBounds, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &f32, range: &R) -> Self { + T::call(context, ErrorType::RangeErrorf32(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + +impl<'a, C, R, T> RangeError<'a, C, f64, R> for ErrorCall +where C: StringReader<'a>, R: RangeBounds, T: ErrorFunc<'a, C> { + fn value_not_in_range(context: &C, from: &f64, range: &R) -> Self { + T::call(context, ErrorType::RangeErrorf64(RangeErrorT { + value: *from, + start: range.start_bound().cloned(), + end: range.end_bound().cloned(), + })); + Self(PhantomData) + } +} + impl<'a, C: StringReader<'a>, T> ReadError<'a, C> for ErrorCall where T: ErrorFunc<'a, C> { fn invalid_integer(context: &C, from: &str) -> Self { diff --git a/tests/common/errorpanic.rs b/tests/common/errorpanic.rs index faef603..6b4254b 100644 --- a/tests/common/errorpanic.rs +++ b/tests/common/errorpanic.rs @@ -3,11 +3,12 @@ // Licensed under the MIT license. // Documentation and comments licensed under CC BY-SA 4.0. -use ::iosonism::strcursor::ReadError; +use ::iosonism::error::RangeError; +use ::iosonism::error::ReadError; use ::iosonism::strcursor::StringReader; /// An implementation of various Iosonism errors that just panics. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum ErrorPanic { // uninhabitable! } @@ -22,6 +23,22 @@ impl ::std::fmt::Display for ErrorPanic { impl ::std::error::Error for ErrorPanic { } +impl<'a, C, T, R> RangeError<'a, C, T, R> for ErrorPanic +where C: StringReader<'a>, T: ::std::fmt::Display, R: ::std::fmt::Debug { + fn value_not_in_range(context: &C, from: &T, range: &R) -> Self { + if !context.get_remaining().is_empty() { + panic!( + "value ({}) not in range: {:?} at ...{}", + from, + range, + context.get_remaining(), + ); + } else { + panic!("value ({}) not in range: {:?}", from, range); + } + } +} + impl<'a, C: StringReader<'a>> ReadError<'a, C> for ErrorPanic { fn invalid_integer(context: &C, from: &str) -> Self { if !context.get_remaining().is_empty() { -- cgit 1.4.1