Skip to content

Commit

Permalink
refactor: using new generic string parser for tile color
Browse files Browse the repository at this point in the history
  • Loading branch information
codaishin committed Jan 16, 2025
1 parent e0d8a08 commit 4362963
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 238 deletions.
257 changes: 31 additions & 226 deletions src/dtos/tile_color.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
use crate::traits::load_from::LoadFrom;
use crate::{
parsers::{
hex_color::{
channels::{Alpha, Blue, Green, Red},
no_suffix::NoSuffix,
prefix::HexPrefix,
HexColorParseError,
},
string_parser::StringParser,
},
traits::load_from::LoadFrom,
};
use bevy::{asset::LoadContext, prelude::*};
use serde::{de::Error, Deserialize, Deserializer};
use std::{fmt::Display, str::Chars};
use serde::{Deserialize, Deserializer};

#[derive(Debug, PartialEq)]
pub struct TileColor {
color: Color,
}

impl TileColor {
fn from_parsed(_Parsed((_, Red(r), Green(g), Blue(b), Alpha(a), _)): _Parsed<All>) -> Self {
fn from_hex_color((_, Red(r), Green(g), Blue(b), Alpha(a), _): HexColor) -> Self {
let color = Color::Srgba(Srgba {
red: r as f32 / 255.,
green: g as f32 / 255.,
Expand All @@ -28,8 +38,8 @@ impl<'a> Deserialize<'a> for TileColor {
{
_TileColorString::deserialize(deserializer)?
.parse_color()
.map(TileColor::from_parsed)
.map_err(_Error::convert_to_error)
.map(TileColor::from_hex_color)
.map_err(HexColorParseError::convert_to_serde_error)
}
}

Expand All @@ -45,233 +55,28 @@ impl LoadFrom<TileColor> for ColorMaterial {
///
///
/// Let's be honest, the whole parsing process is hugely over-designed
/// and could have been done with way fewer lines of code.
/// and could have been done with way fewer lines of code. We could also
/// have just used bevy's [`Srgba::hex`] function.
///
/// But what is life, if we are not enjoying ourselves?
/// So scroll down and bask in the beauty of my nightly escapades :D
struct _TileColorString {
color: String,
}

impl _TileColorString {
fn parse_color(self) -> Result<_Parsed<All>, _Error> {
let chars = &mut self.color.chars();

_Parsed::parse_prefix(chars)?
.parse_red(chars)?
.parse_blue(chars)?
.parse_green(chars)?
.parse_alpha(chars)?
.parse_end_of_string(chars)
}
}

#[derive(Debug, PartialEq)]
struct Prefix;

#[derive(Debug, PartialEq)]
struct Red(u8);

impl From<u8> for Red {
fn from(value: u8) -> Self {
Self(value)
}
}

impl ChanelLabel for Red {
fn label() -> _Chanel {
_Chanel::Red
}
}

#[derive(Debug, PartialEq)]
struct Green(u8);

impl From<u8> for Green {
fn from(value: u8) -> Self {
Self(value)
}
}

impl ChanelLabel for Green {
fn label() -> _Chanel {
_Chanel::Green
}
}

#[derive(Debug, PartialEq)]
struct Blue(u8);

impl From<u8> for Blue {
fn from(value: u8) -> Self {
Self(value)
}
}

impl ChanelLabel for Blue {
fn label() -> _Chanel {
_Chanel::Blue
}
}

#[derive(Debug, PartialEq)]
struct Alpha(u8);

impl From<u8> for Alpha {
fn from(value: u8) -> Self {
Self(value)
}
}

impl ChanelLabel for Alpha {
fn label() -> _Chanel {
_Chanel::Alpha
}
}

#[derive(Debug, PartialEq)]
struct EndOfString;

type All = (Prefix, Red, Green, Blue, Alpha, EndOfString);

#[derive(Debug, PartialEq)]
struct _Parsed<TEvaluated = ()>(TEvaluated);

impl _Parsed {
fn parse_prefix(chars: &mut Chars) -> Result<_Parsed<Prefix>, _Error> {
let Some(prefix) = chars.next() else {
return Err(_Error::EmptyString);
};
type HexColor = (HexPrefix, Red, Green, Blue, Alpha, NoSuffix);

if prefix != '#' {
return Err(_Error::FaultyPrefix(prefix));
}

Ok(_Parsed(Prefix))
}
}

impl _Parsed<Prefix> {
fn parse_red(self, chars: &mut Chars) -> Result<_Parsed<(Prefix, Red)>, _Error> {
let _Parsed(prefix) = self;

Ok(_Parsed((prefix, parse_chanel(chars)?)))
}
}

impl _Parsed<(Prefix, Red)> {
fn parse_blue(self, chars: &mut Chars) -> Result<_Parsed<(Prefix, Red, Green)>, _Error> {
let _Parsed((prefix, red)) = self;

Ok(_Parsed((prefix, red, parse_chanel(chars)?)))
}
}

impl _Parsed<(Prefix, Red, Green)> {
fn parse_green(self, chars: &mut Chars) -> Result<_Parsed<(Prefix, Red, Green, Blue)>, _Error> {
let _Parsed((prefix, red, green)) = self;

Ok(_Parsed((prefix, red, green, parse_chanel(chars)?)))
}
}

impl _Parsed<(Prefix, Red, Green, Blue)> {
#[allow(clippy::type_complexity)]
fn parse_alpha(
self,
chars: &mut Chars,
) -> Result<_Parsed<(Prefix, Red, Green, Blue, Alpha)>, _Error> {
let _Parsed((prefix, red, green, blue)) = self;
let alpha = parse_chanel::<Alpha>(chars);

if let Err(_Error::Empty(_Chanel::Alpha)) = alpha {
return Ok(_Parsed((prefix, red, green, blue, Alpha(255))));
};

Ok(_Parsed((prefix, red, green, blue, alpha?)))
}
}

impl _Parsed<(Prefix, Red, Green, Blue, Alpha)> {
fn parse_end_of_string(self, chars: &mut Chars) -> Result<_Parsed<All>, _Error> {
let _Parsed((prefix, red, green, blue, alpha)) = self;

let rest: String = chars.collect();

if !rest.is_empty() {
return Err(_Error::RemainingChars(rest));
}

Ok(_Parsed((prefix, red, green, blue, alpha, EndOfString)))
}
}

#[derive(Clone, Copy)]
enum _Chanel {
Red,
Green,
Blue,
Alpha,
}

impl Display for _Chanel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
_Chanel::Red => f.write_str("Chanel Red (rr)"),
_Chanel::Green => f.write_str("Chanel Green (gg)"),
_Chanel::Blue => f.write_str("Chanel Blue (bb)"),
_Chanel::Alpha => f.write_str("Chanel Alpha (aa)"),
}
}
}

#[derive(Clone)]
enum _Error {
EmptyString,
FaultyPrefix(char),
Empty(_Chanel),
Incomplete(_Chanel),
FaultyBase(_Chanel),
RemainingChars(String),
}

impl _Error {
fn convert_to_error<TError>(self) -> TError
where
TError: Error,
{
let base = "expected hex coded color (#rrggbb or #rrggbbaa), but";

match self {
_Error::EmptyString => TError::custom(format!("{base} it was empty")),
_Error::FaultyPrefix(p) => TError::custom(format!("{base} it began with {p}")),
_Error::Empty(c) => TError::custom(format!("{base} {c} was empty")),
_Error::Incomplete(c) => TError::custom(format!("{base} {c} was incomplete")),
_Error::FaultyBase(c) => TError::custom(format!("{base} {c} was not base 16")),
_Error::RemainingChars(rest) => {
TError::custom(format!("{base} it had overflowing characters: \"{rest}\""))
}
}
impl _TileColorString {
fn parse_color(self) -> Result<HexColor, HexColorParseError> {
let chars = self.color.chars();

Ok(StringParser::new(chars)
.parse::<HexPrefix>()?
.parse::<Red>()?
.parse::<Green>()?
.parse::<Blue>()?
.parse::<Alpha>()?
.parse::<NoSuffix>()?
.unpack())
}
}

fn parse_chanel<TChanel>(chars: &mut Chars) -> Result<TChanel, _Error>
where
TChanel: From<u8> + ChanelLabel,
{
let Some(c0) = chars.next() else {
return Err(_Error::Empty(TChanel::label()));
};
let Some(c1) = chars.next() else {
return Err(_Error::Incomplete(TChanel::label()));
};
let Ok(c) = u8::from_str_radix(&format!("{c0}{c1}"), 16) else {
return Err(_Error::FaultyBase(TChanel::label()));
};

Ok(TChanel::from(c))
}

trait ChanelLabel {
fn label() -> _Chanel;
}
6 changes: 4 additions & 2 deletions src/parsers/hex_color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ impl HexColorParseError {
where
TError: Error,
{
let base = "expected hex coded color (#rrggbb or #rrggbbaa), but";
let base = "expected hex coded color (\"#rrggbb\" or \"#rrggbbaa\"), but";

match self {
Self::EmptyString => TError::custom(format!("{base} it was empty")),
Self::InvalidPrefix(p) => TError::custom(format!("{base} it began with {p}")),
Self::InvalidPrefix(p) => {
TError::custom(format!("{base} it began with '{p}' instead if '#'"))
}
Self::Empty(c) => TError::custom(format!("{base} {c} was empty")),
Self::Incomplete(c) => TError::custom(format!("{base} {c} was incomplete")),
Self::FaultyBase(c) => TError::custom(format!("{base} {c} was not base 16")),
Expand Down
10 changes: 5 additions & 5 deletions src/parsers/hex_color/channels.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{prefix::Prefix, HexColorChanel, HexColorParseError};
use super::{prefix::HexPrefix, HexColorChanel, HexColorParseError};
use crate::{parsers::parsed::Parsed, traits::parse::Parse};
use std::str::Chars;

Expand All @@ -21,7 +21,7 @@ macro_rules! parse_color {
pub struct Red(pub u8);

impl Parse for Red {
type TRequired = Parsed<(Prefix,)>;
type TRequired = Parsed<(HexPrefix,)>;
type TSource<'a> = Chars<'a>;
type TError = HexColorParseError;

Expand All @@ -33,7 +33,7 @@ impl Parse for Red {
pub struct Green(pub u8);

impl Parse for Green {
type TRequired = Parsed<(Prefix, Red)>;
type TRequired = Parsed<(HexPrefix, Red)>;
type TSource<'a> = Chars<'a>;
type TError = HexColorParseError;

Expand All @@ -45,7 +45,7 @@ impl Parse for Green {
pub struct Blue(pub u8);

impl Parse for Blue {
type TRequired = Parsed<(Prefix, Red, Green)>;
type TRequired = Parsed<(HexPrefix, Red, Green)>;
type TSource<'a> = Chars<'a>;
type TError = HexColorParseError;

Expand All @@ -57,7 +57,7 @@ impl Parse for Blue {
pub struct Alpha(pub u8);

impl Parse for Alpha {
type TRequired = Parsed<(Prefix, Red, Green, Blue)>;
type TRequired = Parsed<(HexPrefix, Red, Green, Blue)>;
type TSource<'a> = Chars<'a>;
type TError = HexColorParseError;

Expand Down
4 changes: 2 additions & 2 deletions src/parsers/hex_color/no_suffix.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{
channels::{Alpha, Blue, Green, Red},
prefix::Prefix,
prefix::HexPrefix,
HexColorParseError,
};
use crate::{parsers::parsed::Parsed, traits::parse::Parse};
Expand All @@ -9,7 +9,7 @@ use std::str::Chars;
pub struct NoSuffix;

impl Parse for NoSuffix {
type TRequired = Parsed<(Prefix, Red, Green, Blue, Alpha)>;
type TRequired = Parsed<(HexPrefix, Red, Green, Blue, Alpha)>;
type TSource<'a> = Chars<'a>;
type TError = HexColorParseError;

Expand Down
6 changes: 3 additions & 3 deletions src/parsers/hex_color/prefix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use crate::{parsers::parsed::ParsedNothing, traits::parse::Parse};
use std::str::Chars;

#[derive(Debug, PartialEq)]
pub struct Prefix;
pub struct HexPrefix;

impl Parse for Prefix {
impl Parse for HexPrefix {
type TRequired = ParsedNothing;
type TSource<'a> = Chars<'a>;
type TError = HexColorParseError;
Expand All @@ -18,6 +18,6 @@ impl Parse for Prefix {
return Err(HexColorParseError::InvalidPrefix(prefix));
}

Ok((Prefix, value))
Ok((HexPrefix, value))
}
}

0 comments on commit 4362963

Please sign in to comment.