mirror of
https://github.com/Zenithsiz/dcb.git
synced 2026-02-09 03:40:23 +00:00
Improved validation interface.
Implemented new validation interface for `Move`. Added more tests to `Move`.
This commit is contained in:
@@ -10,7 +10,7 @@ use crate::game::{
|
||||
array_split, array_split_mut,
|
||||
null_ascii_string::{self, NullAsciiString},
|
||||
},
|
||||
Bytes,
|
||||
Bytes, Validatable, Validation,
|
||||
};
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
@@ -44,7 +44,6 @@ pub enum ToBytesError {
|
||||
Name(#[source] null_ascii_string::WriteError),
|
||||
}
|
||||
|
||||
// Bytes
|
||||
impl Bytes for Move {
|
||||
type ByteArray = [u8; 0x1c];
|
||||
type FromError = FromBytesError;
|
||||
@@ -84,26 +83,47 @@ impl Bytes for Move {
|
||||
// And return Ok
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
fn validate(&self) -> Validation {
|
||||
impl Validatable for Move {
|
||||
type Error = ValidationError;
|
||||
type Warning = ValidationWarning;
|
||||
|
||||
fn validate(&self) -> Validation<Self::Error, Self::Warning> {
|
||||
// Create the initial validation
|
||||
let mut validation = Validation::new();
|
||||
|
||||
// If our name is longer or equal to `0x16` bytes, emit error
|
||||
if self.name.len() >= 0x16 {
|
||||
validation.add_error("Name must be at most 21 characters.");
|
||||
validation.emit_error(ValidationError::NameTooLong);
|
||||
}
|
||||
|
||||
// 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.add_warning("Powers that are not a multiple of 10 are not fully supported.");
|
||||
validation.emit_warning(ValidationWarning::PowerMultiple10);
|
||||
}
|
||||
|
||||
// And return the validation
|
||||
validation
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
/// All warnings for [`Move`] validation
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum ValidationWarning {
|
||||
/// Power is not a multiple of 10
|
||||
#[display(fmt = "Power is not a multiple of 10.")]
|
||||
PowerMultiple10,
|
||||
}
|
||||
|
||||
/// All errors for [`Move`] validation
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
#[derive(derive_more::Display)]
|
||||
pub enum ValidationError {
|
||||
/// Name length
|
||||
#[display(fmt = "Name is too long. Must be at most 21 characters")]
|
||||
NameTooLong,
|
||||
}
|
||||
|
||||
@@ -5,44 +5,53 @@
|
||||
|
||||
// Imports
|
||||
use super::*;
|
||||
use crate::Validatable;
|
||||
|
||||
#[test]
|
||||
fn bytes() {
|
||||
// Valid moves with no warnings
|
||||
#[rustfmt::skip]
|
||||
let valid_moves: &[(Move, <Move as Bytes>::ByteArray)] = &[(
|
||||
Move {
|
||||
name: ascii::AsciiString::from_ascii("Digimon").expect("Unable to convert string to ascii"),
|
||||
power: LittleEndian::read_u16(&[1, 2]),
|
||||
unknown: LittleEndian::read_u32(&[1, 2, 3, 4]),
|
||||
},
|
||||
[
|
||||
// Power
|
||||
1, 2,
|
||||
|
||||
// Unknown,
|
||||
1, 2, 3, 4,
|
||||
|
||||
// Name
|
||||
b'D', b'i', b'g', b'i', b'm', b'o', b'n', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0',
|
||||
b'\0', b'\0',
|
||||
],
|
||||
)];
|
||||
let valid_moves: &[(Move, <Move as Bytes>::ByteArray)] = &[
|
||||
(
|
||||
Move {
|
||||
name: ascii::AsciiString::from_ascii("Digimon").expect("Unable to convert string to ascii"),
|
||||
power: LittleEndian::read_u16(&[4, 1]),
|
||||
unknown: LittleEndian::read_u32(&[1, 2, 3, 4]),
|
||||
},
|
||||
[
|
||||
4, 1, 1, 2, 3, 4, b'D', b'i', b'g', b'i', b'm', b'o', b'n', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
],
|
||||
),
|
||||
(
|
||||
Move {
|
||||
name: ascii::AsciiString::from_ascii("123456789012345678901").expect("Unable to convert string to ascii"),
|
||||
power: 0,
|
||||
unknown: 0,
|
||||
},
|
||||
[
|
||||
0, 0, 0, 0, 0, 0, b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9',
|
||||
b'0', b'1', 0,
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
for (mov, bytes) in valid_moves {
|
||||
// Print the move and move bytes
|
||||
println!("Move: {:?}", mov);
|
||||
println!("Bytes: {:?}", bytes);
|
||||
|
||||
for (mov, move_bytes) in valid_moves {
|
||||
// Check that we can create the move from bytes
|
||||
assert_eq!(&Move::from_bytes(move_bytes).expect("Unable to convert move from bytes"), mov);
|
||||
assert_eq!(&Move::from_bytes(bytes).expect("Unable to convert move from bytes"), mov);
|
||||
|
||||
// Make sure the validation succeeds
|
||||
/*
|
||||
let validation = mov.validate();
|
||||
println!("Errors: {:?}", validation.errors());
|
||||
println!("Warnings: {:?}", validation.warnings());
|
||||
assert!(validation.successful());
|
||||
assert!(validation.warnings().is_empty());
|
||||
*/
|
||||
|
||||
// Then serialize it to bytes and make sure it's equal
|
||||
let mut bytes = <Move as Bytes>::ByteArray::default();
|
||||
Move::to_bytes(mov, &mut bytes).expect("Unable to convert move to bytes");
|
||||
assert_eq!(&bytes, move_bytes);
|
||||
let mut mov_bytes = <Move as Bytes>::ByteArray::default();
|
||||
Move::to_bytes(mov, &mut mov_bytes).expect("Unable to convert move to bytes");
|
||||
assert_eq!(&mov_bytes, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,37 +13,69 @@
|
||||
/// 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 Validatable {
|
||||
/// Validation type
|
||||
type Output: Validation;
|
||||
/// Error type for this validation
|
||||
type Error;
|
||||
|
||||
/// Warning type for this validation
|
||||
type Warning;
|
||||
|
||||
/// Validates this structure
|
||||
fn validate(&self) -> Self::Output;
|
||||
fn validate(&self) -> Validation<Self::Error, Self::Warning>;
|
||||
}
|
||||
|
||||
/// A validation type.
|
||||
///
|
||||
/// This is the output of structures which may be validated.
|
||||
/// It is a trait to offer more flexibility to each structure to report
|
||||
/// errors and warnings in it's preferred manner.
|
||||
pub trait Validation: Clone {
|
||||
/// Warnings type
|
||||
type Warnings;
|
||||
/// A validation
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Validation<Error, Warning> {
|
||||
/// All warnings
|
||||
warnings: Vec<Warning>,
|
||||
|
||||
/// Errors type
|
||||
type Errors;
|
||||
/// All errors
|
||||
errors: Vec<Error>,
|
||||
}
|
||||
|
||||
/// If this validation was successful.
|
||||
///
|
||||
/// A successful validation is one that, although may emit warnings, did not emit
|
||||
/// any errors. Conversely, this also indicates that calling [`to_bytes`] will _not_
|
||||
/// produce a `Err` value.
|
||||
fn successful(&self) -> bool {
|
||||
self.errors().is_none()
|
||||
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()
|
||||
}
|
||||
|
||||
/// Returns any warnings
|
||||
fn warnings(&self) -> Option<Self::Warnings>;
|
||||
/// Emits a warning
|
||||
pub fn emit_warning(&mut self, warning: Warning) {
|
||||
self.warnings.push(warning);
|
||||
}
|
||||
|
||||
/// Returns any errors
|
||||
fn errors(&self) -> Option<Self::Errors>;
|
||||
/// 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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user