mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-03 16:16:33 +00:00
Added new validation interface in dcb-bytes.
This commit is contained in:
parent
f98d8ae290
commit
aabaafc91a
@ -9,3 +9,6 @@ edition = "2018"
|
||||
# Bytes
|
||||
byteorder = "1.4.2"
|
||||
arrayref = "0.3.6"
|
||||
|
||||
# Util
|
||||
either = "1.6.1"
|
||||
@ -62,9 +62,11 @@
|
||||
pub mod byteorder_ext;
|
||||
pub mod bytes;
|
||||
pub mod derive;
|
||||
pub mod validate;
|
||||
|
||||
// Exports
|
||||
pub use byteorder_ext::ByteOrderExt;
|
||||
pub use bytes::{ByteArray, Bytes};
|
||||
pub use validate::{Validate, ValidateVisitor};
|
||||
#[doc(hidden)]
|
||||
pub use ::{arrayref, byteorder};
|
||||
|
||||
47
dcb-bytes/src/validate.rs
Normal file
47
dcb-bytes/src/validate.rs
Normal file
@ -0,0 +1,47 @@
|
||||
//! Validation interface
|
||||
|
||||
// Imports
|
||||
use either::Either;
|
||||
|
||||
/// Structures that are validatable before being written to bytes.
|
||||
///
|
||||
/// This works in tandem with the [`Bytes`](crate::Bytes) interface to allow
|
||||
/// applications which take user input to validate input before serializing it.
|
||||
///
|
||||
/// Although this information exists by calling [`Bytes::to_bytes`](crate::Bytes::to_bytes),
|
||||
/// this interface provides two main advantages:
|
||||
///
|
||||
/// 1. It is faster than serializing the data, as it doesn't need to write the raw bytes and
|
||||
/// can focus on simply parsing possible errors.
|
||||
/// 2. It provides warnings alongside the errors. These are also provided via `log::warn`, but
|
||||
/// these cannot be sent to the user easily.
|
||||
pub trait Validate<'a> {
|
||||
/// Error type for this validation
|
||||
type Error: 'a;
|
||||
|
||||
/// Warning type for this validation
|
||||
type Warning: 'a;
|
||||
|
||||
/// Validates this type with the visitor `visitor`
|
||||
fn validate<V: ValidateVisitor<'a, Self>>(&'a self, visitor: V);
|
||||
}
|
||||
|
||||
/// A validate visitor, used to collect errors and warnings
|
||||
pub trait ValidateVisitor<'a, T: ?Sized + Validate<'a>> {
|
||||
/// Visits a warning
|
||||
fn visit_warning(&mut self, warning: T::Warning);
|
||||
|
||||
/// Visits an error
|
||||
fn visit_error(&mut self, error: T::Error);
|
||||
}
|
||||
|
||||
/// A closure taking a `Either<Warning, Error>` is a visitor
|
||||
impl<'a, T: ?Sized + Validate<'a>, F: FnMut(Either<T::Warning, T::Error>)> ValidateVisitor<'a, T> for F {
|
||||
fn visit_warning(&mut self, warning: T::Warning) {
|
||||
self(Either::Left(warning));
|
||||
}
|
||||
|
||||
fn visit_error(&mut self, error: T::Error) {
|
||||
self(Either::Right(error));
|
||||
}
|
||||
}
|
||||
@ -5,9 +5,8 @@
|
||||
mod test;
|
||||
|
||||
// Imports
|
||||
use crate::{Validatable, Validation};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
use dcb_bytes::Bytes;
|
||||
use dcb_bytes::{Bytes, Validate, ValidateVisitor};
|
||||
use dcb_util::{
|
||||
array_split, array_split_mut,
|
||||
null_ascii_string::{self, NullAsciiString},
|
||||
@ -77,23 +76,17 @@ impl Bytes for Move {
|
||||
}
|
||||
}
|
||||
|
||||
impl Validatable for Move {
|
||||
type Error = ValidationError;
|
||||
impl<'a> Validate<'a> for Move {
|
||||
type Error = !;
|
||||
type Warning = ValidationWarning;
|
||||
|
||||
fn validate(&self) -> Validation<Self::Error, Self::Warning> {
|
||||
// Create the initial validation
|
||||
let mut validation = Validation::new();
|
||||
|
||||
fn validate<V: ValidateVisitor<'a, Self>>(&'a self, mut visitor: V) {
|
||||
// If the power isn't a multiple of 10, warn, as we don't know how the game handles
|
||||
// powers that aren't multiples of 10.
|
||||
// TODO: Verify if the game can handle non-multiple of 10 powers.
|
||||
if self.power % 10 != 0 {
|
||||
validation.emit_warning(ValidationWarning::PowerMultiple10);
|
||||
visitor.visit_warning(ValidationWarning::PowerMultiple10);
|
||||
}
|
||||
|
||||
// And return the validation
|
||||
validation
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,11 +97,3 @@ pub enum ValidationWarning {
|
||||
#[error("Power is not a multiple of 10.")]
|
||||
PowerMultiple10,
|
||||
}
|
||||
|
||||
/// All errors for [`Move`] validation
|
||||
#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
|
||||
pub enum ValidationError {
|
||||
/// Name length
|
||||
#[error("Name is too long. Must be at most 21 characters")]
|
||||
NameTooLong,
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
|
||||
// Imports
|
||||
use super::*;
|
||||
use crate::Validatable;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
#[test]
|
||||
@ -47,10 +46,9 @@ fn valid_bytes() {
|
||||
mov
|
||||
);
|
||||
|
||||
// Make sure the validation succeeds
|
||||
let validation = mov.validate();
|
||||
assert!(validation.successful());
|
||||
assert!(validation.warnings().is_empty());
|
||||
// Make sure the validation succeeds without warnings or errors
|
||||
let mut successful = true;
|
||||
mov.validate(|_| successful = false);
|
||||
|
||||
// Then serialize it to bytes and make sure it's equal
|
||||
let mut mov_bytes = <Move as Bytes>::ByteArray::default();
|
||||
|
||||
@ -69,9 +69,7 @@
|
||||
// Modules
|
||||
pub mod card;
|
||||
pub mod deck;
|
||||
pub mod validation;
|
||||
|
||||
// Exports
|
||||
pub use card::{Digimon, Digivolve, Item, Table as CardTable};
|
||||
pub use deck::{Deck, Table as DeckTable};
|
||||
pub use validation::{Validatable, Validation};
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
//! Error and warning validation for structures
|
||||
|
||||
/// Structures that are validatable to be written to bytes.
|
||||
///
|
||||
/// This works in tandem with the [`Bytes`](dcb_bytes::Bytes) interface to allow
|
||||
/// applications which take user input to validate input before serializing it.
|
||||
///
|
||||
/// Although this information exists by calling [`Bytes::to_bytes`](dcb_bytes::Bytes::to_bytes),
|
||||
/// this interface provides two main advantages:
|
||||
///
|
||||
/// 1. It is faster than serializing the data, as it doesn't need to write the raw bytes and
|
||||
/// can focus on simply parsing possible errors.
|
||||
/// 2. It provides warnings alongside the errors. These are also provided via `log::warn`, but
|
||||
/// these cannot be sent to the user easily.
|
||||
// TODO: Move to `dcb-bytes`.
|
||||
pub trait Validatable {
|
||||
/// Error type for this validation
|
||||
type Error;
|
||||
|
||||
/// Warning type for this validation
|
||||
type Warning;
|
||||
|
||||
/// Validates this structure
|
||||
fn validate(&self) -> Validation<Self::Error, Self::Warning>;
|
||||
}
|
||||
|
||||
/// A validation
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Validation<Error, Warning> {
|
||||
/// All warnings
|
||||
warnings: Vec<Warning>,
|
||||
|
||||
/// All errors
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
|
||||
impl<Error, Warning> Default for Validation<Error, Warning> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
warnings: vec![],
|
||||
errors: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Error, Warning> Validation<Error, Warning> {
|
||||
/// Creates an empty validation
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Emits a warning
|
||||
pub fn emit_warning(&mut self, warning: Warning) {
|
||||
self.warnings.push(warning);
|
||||
}
|
||||
|
||||
/// Emits an error
|
||||
pub fn emit_error(&mut self, error: Error) {
|
||||
self.errors.push(error);
|
||||
}
|
||||
|
||||
/// Returns all warnings
|
||||
#[must_use]
|
||||
pub fn warnings(&self) -> &[Warning] {
|
||||
&self.warnings
|
||||
}
|
||||
|
||||
/// Returns all errors
|
||||
#[must_use]
|
||||
pub fn errors(&self) -> &[Error] {
|
||||
&self.errors
|
||||
}
|
||||
|
||||
/// Returns if this validation was successful
|
||||
///
|
||||
/// A validation is considered successful if no errors occurred.
|
||||
#[must_use]
|
||||
pub fn successful(&self) -> bool {
|
||||
self.errors.is_empty()
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user