use ltk_hash::HashWidth;
use ltk_meta::{property::values, PropertyKind, PropertyValueEnum};
use std::fmt::Display;
mod hash;
mod container;
mod rito_type;
mod scalar;
pub use container::*;
pub use hash::*;
pub use rito_type::*;
pub use scalar::*;
#[cfg(test)]
mod tests;
pub trait RitobinName {
fn from_rito_name(name: &str) -> Option<Self>
where
Self: Sized;
fn to_rito_name(&self) -> &'static str;
}
impl RitobinName for PropertyKind {
fn from_rito_name(name: &str) -> Option<Self> {
match name {
"none" => Some(Self::None),
"bool" => Some(Self::Bool),
"i8" => Some(Self::I8),
"u8" => Some(Self::U8),
"i16" => Some(Self::I16),
"u16" => Some(Self::U16),
"i32" => Some(Self::I32),
"u32" => Some(Self::U32),
"i64" => Some(Self::I64),
"u64" => Some(Self::U64),
"f32" => Some(Self::F32),
"vec2" => Some(Self::Vector2),
"vec3" => Some(Self::Vector3),
"vec4" => Some(Self::Vector4),
"mtx44" => Some(Self::Matrix44),
"rgba" => Some(Self::Color),
"string" => Some(Self::String),
"hash" => Some(Self::Hash),
"file" => Some(Self::WadChunkLink),
"list" => Some(Self::Container),
"list2" => Some(Self::UnorderedContainer),
"pointer" => Some(Self::Struct),
"embed" => Some(Self::Embedded),
"link" => Some(Self::ObjectLink),
"option" => Some(Self::Optional),
"map" => Some(Self::Map),
"flag" => Some(Self::BitBool),
_ => None,
}
}
fn to_rito_name(&self) -> &'static str {
match self {
Self::None => "none",
Self::Bool => "bool",
Self::I8 => "i8",
Self::U8 => "u8",
Self::I16 => "i16",
Self::U16 => "u16",
Self::I32 => "i32",
Self::U32 => "u32",
Self::I64 => "i64",
Self::U64 => "u64",
Self::F32 => "f32",
Self::Vector2 => "vec2",
Self::Vector3 => "vec3",
Self::Vector4 => "vec4",
Self::Matrix44 => "mtx44",
Self::Color => "rgba",
Self::String => "string",
Self::Hash => "hash",
Self::WadChunkLink => "file",
Self::Container => "list",
Self::UnorderedContainer => "list2",
Self::Struct => "pointer",
Self::Embedded => "embed",
Self::ObjectLink => "link",
Self::Optional => "option",
Self::Map => "map",
Self::BitBool => "flag",
}
}
}
#[macro_export]
macro_rules! rito {
($kind:ident) => {
const {
match $crate::RitoType::try_from_scalar($crate::__private::PropertyKind::$kind) {
Ok(t) => t,
Err(_) => panic!("invalid simple rito type"),
}
}
};
($kind:ident [ $sub:ident ]) => {
const {
match $crate::RitoType::try_from_parts(
$crate::__private::PropertyKind::$kind,
[Some($crate::__private::PropertyKind::$sub), None],
) {
Ok(t) => t,
Err(_) => panic!("invalid rito type"),
}
}
};
($kind:ident [ $a:ident, $b:ident ]) => {
const {
match $crate::RitoType::try_from_parts(
$crate::__private::PropertyKind::$kind,
[
Some($crate::__private::PropertyKind::$a),
Some($crate::__private::PropertyKind::$b),
],
) {
Ok(t) => t,
Err(_) => panic!("invalid rito type"),
}
}
};
}
impl RitoType {
#[inline(always)]
#[must_use]
pub const fn from_property_kind(kind: PropertyKind) -> Self {
use ContainerType as C;
use ScalarType as S;
match kind {
PropertyKind::None => Self::Scalar(S::None),
PropertyKind::Bool => Self::Scalar(S::Bool),
PropertyKind::I8 => Self::Scalar(S::I8),
PropertyKind::U8 => Self::Scalar(S::U8),
PropertyKind::I16 => Self::Scalar(S::I16),
PropertyKind::U16 => Self::Scalar(S::U16),
PropertyKind::I32 => Self::Scalar(S::I32),
PropertyKind::U32 => Self::Scalar(S::U32),
PropertyKind::I64 => Self::Scalar(S::I64),
PropertyKind::U64 => Self::Scalar(S::U64),
PropertyKind::F32 => Self::Scalar(S::F32),
PropertyKind::Vector2 => Self::Scalar(S::Vector2),
PropertyKind::Vector3 => Self::Scalar(S::Vector3),
PropertyKind::Vector4 => Self::Scalar(S::Vector4),
PropertyKind::Matrix44 => Self::Scalar(S::Matrix44),
PropertyKind::Color => Self::Scalar(S::Color),
PropertyKind::String => Self::Scalar(S::String),
PropertyKind::WadChunkLink => Self::Scalar(S::WadChunkLink),
PropertyKind::Struct => Self::Scalar(S::Struct),
PropertyKind::Embedded => Self::Scalar(S::Embedded),
PropertyKind::ObjectLink => Self::Scalar(S::ObjectLink),
PropertyKind::BitBool => Self::Scalar(S::BitBool),
PropertyKind::Hash => Self::Scalar(S::Hash(HashType::DEFAULT)),
PropertyKind::Container => Self::Container(C::Container(None)),
PropertyKind::UnorderedContainer => Self::Container(C::OrderedList(None)),
PropertyKind::Optional => Self::Container(C::Optional(None)),
PropertyKind::Map => Self::Container(C::Map(None)),
}
}
}
impl RitoType {
pub const fn as_scalar(self) -> Option<ScalarType> {
match self {
RitoType::Scalar(kind) => Some(kind),
_ => None,
}
}
pub const fn as_container(self) -> Option<ContainerType> {
match self {
RitoType::Container(kind) => Some(kind),
_ => None,
}
}
pub fn equivalent_to(&self, other: &Self) -> bool {
match (self, other) {
(RitoType::Scalar(a), RitoType::Scalar(b)) => a == b,
(RitoType::Container(a), RitoType::Container(b)) => a.equivalent_to(b),
_ => false,
}
}
#[inline(always)]
pub const fn try_from_scalar(kind: PropertyKind) -> Result<Self, BadScalarType> {
match ScalarType::try_from_kind(kind) {
Ok(v) => Ok(Self::Scalar(v)),
Err(e) => Err(e),
}
}
pub const fn hash_type(&self) -> Option<HashType> {
match self {
RitoType::Scalar(ScalarType::Hash(hash_type)) => Some(*hash_type),
_ => None,
}
}
pub const fn subtype(&self, n: usize) -> Option<ScalarType> {
match self {
RitoType::Scalar(_) => None,
RitoType::Container(container) => container.subtype(n),
}
}
pub fn with_default_hash_types(self) -> Self {
match self {
RitoType::Scalar(scalar) => Self::Scalar(scalar.with_default_hash_type()),
RitoType::Container(container) => Self::Container(container.with_default_hash_types()),
}
}
pub fn with_subtype_hash_types(self, hash_types: [HashType; 2]) -> Self {
match self {
RitoType::Scalar(_) => self,
RitoType::Container(container) => {
Self::Container(container.with_hash_types(hash_types))
}
}
}
pub const fn try_from_parts(
base: PropertyKind,
subtypes: [Option<PropertyKind>; 2],
) -> Result<Self, BadRitoType> {
use BadRitoType as E;
match subtypes[0] {
Some(sub) => match ContainerType::try_from_parts(base, (sub, subtypes[1])) {
Ok(container) => Ok(Self::Container(container)),
Err(e) => Err(E::BadContainerType(e)),
},
None => match ScalarType::try_from_kind(base) {
Ok(scalar) => Ok(Self::Scalar(scalar)),
Err(e) => Err(E::BadScalarType(e)),
},
}
}
pub fn from_parts(base: PropertyKind, subtypes: [Option<PropertyKind>; 2]) -> Self {
match ContainerType::from_parts(base, subtypes) {
Some(container) => container.into(),
None => Self::from_property_kind(base),
}
}
pub const fn list(item: ScalarType) -> Self {
Self::Container(ContainerType::Container(Some(item)))
}
pub const fn map(key: ScalarType, value: ScalarType) -> Self {
Self::Container(ContainerType::Map(Some((key, value))))
}
pub const fn base(&self) -> PropertyKind {
match self {
RitoType::Scalar(scalar_type) => scalar_type.kind(),
RitoType::Container(container_type) => container_type.base_kind(),
}
}
pub const fn value_subtype(&self) -> Option<ScalarType> {
match self {
RitoType::Scalar(_) => None,
RitoType::Container(container) => match container {
ContainerType::Map(types) => match types {
Some((_, value)) => Some(*value),
None => None,
},
ContainerType::Container(value) => *value,
ContainerType::OrderedList(value) => *value,
ContainerType::Optional(value) => *value,
},
}
}
pub fn item_shape(&self) -> Option<ItemShape> {
use PropertyKind as K;
Some(match self.base() {
K::Struct | K::Embedded | K::Map => ItemShape::Entry,
K::Container | K::UnorderedContainer | K::Optional => ItemShape::Value,
K::Vector2 | K::Vector3 | K::Vector4 | K::Color | K::Matrix44 => ItemShape::Value,
_ => return None,
})
}
pub fn make_default(&self) -> PropertyValueEnum {
use ContainerType as C;
use PropertyValueEnum as P;
let kind = |item: Option<ScalarType>| item.unwrap_or_default().kind();
match *self {
RitoType::Scalar(ScalarType::Hash(hash_type)) => {
P::Hash(values::Hash::new(hash_type.from_hash(0)))
}
RitoType::Scalar(scalar) => scalar.kind().default_value(),
RitoType::Container(C::Map(types)) => {
let (key, value) = types.unwrap_or_default();
P::Map(values::Map::empty(key.kind(), value.kind()).unwrap_or_default())
}
RitoType::Container(C::Container(item)) => {
P::Container(values::Container::empty(kind(item)).unwrap_or_default())
}
RitoType::Container(C::OrderedList(item)) => {
P::UnorderedContainer(values::UnorderedContainer(
values::Container::empty(kind(item)).unwrap_or_default(),
))
}
RitoType::Container(C::Optional(item)) => {
P::Optional(values::Optional::empty(kind(item)).unwrap_or_default())
}
}
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub enum ItemShape {
Entry,
Value,
}
impl Display for ItemShape {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
ItemShape::Entry => "an entry ('name: type = value')",
ItemShape::Value => "a value",
})
}
}
pub trait PropertyValueExt {
fn rito_type(&self) -> RitoType;
}
impl PropertyValueExt for PropertyValueEnum {
fn rito_type(&self) -> RitoType {
let item = |kind: PropertyKind, width: Option<HashWidth>| {
ScalarType::try_from_kind(kind)
.ok()
.map(|item| item.with_hash_type(hash_type_for_width(width.unwrap_or_default())))
};
match self {
PropertyValueEnum::Hash(hash) => {
ScalarType::Hash(hash_type_for_width(hash.width())).into()
}
PropertyValueEnum::Map(map) => ContainerType::Map(
item(map.key_kind(), map.key_hash_width())
.zip(item(map.value_kind(), map.value_hash_width())),
)
.into(),
PropertyValueEnum::UnorderedContainer(values::UnorderedContainer(container)) => {
ContainerType::OrderedList(item(container.item_kind(), container.hash_width()))
.into()
}
PropertyValueEnum::Container(container) => {
ContainerType::Container(item(container.item_kind(), container.hash_width())).into()
}
PropertyValueEnum::Optional(optional) => {
let width = match optional.value() {
Some(PropertyValueEnum::Hash(hash)) => Some(hash.width()),
_ => None,
};
ContainerType::Optional(item(optional.item_kind(), width)).into()
}
_ => RitoType::from_property_kind(self.kind()),
}
}
}