use crate::legacy::float::BdatReal;
use crate::{BdatError, BdatResult, BdatVersion, Label, RowRef};
use enum_kinds::EnumKind;
use num_enum::TryFromPrimitive;
use std::borrow::Cow;
use std::fmt::Display;
#[derive(Debug, Clone, PartialEq)]
pub enum Cell<'b> {
Single(Value<'b>),
List(Vec<Value<'b>>),
Flags(Vec<u32>),
}
#[derive(EnumKind, Debug, Clone, PartialEq)]
#[enum_kind(
ValueType,
derive(TryFromPrimitive),
repr(u8),
cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize)),
cfg_attr(feature = "serde", serde(into = "u8", try_from = "u8"))
)]
pub enum Value<'b> {
Unknown,
UnsignedByte(u8),
UnsignedShort(u16),
UnsignedInt(u32),
SignedByte(i8),
SignedShort(i16),
SignedInt(i32),
String(Utf<'b>),
Float(BdatReal),
HashRef(u32),
Percent(u8),
DebugString(Utf<'b>),
Unknown2(u8),
Unknown3(u16),
}
pub type Utf<'t> = Cow<'t, str>;
pub type ModernCell<'t, 'tb> = &'t Value<'tb>;
pub type LegacyCell<'t, 'tb> = &'t Cell<'tb>;
pub trait FromValue<'t, 'tb>
where
Self: Sized,
{
fn extract(value: &'t Value<'tb>) -> Option<Self>;
}
pub trait FromCell<'t, 'tb> {
fn from_cell(cell: &'t Cell<'tb>) -> Self;
}
impl<'b> Cell<'b> {
pub fn as_single(&self) -> Option<&Value> {
match self {
Self::Single(v) => Some(v),
_ => None,
}
}
pub fn into_single(self) -> Option<Value<'b>> {
match self {
Self::Single(v) => Some(v),
_ => None,
}
}
pub fn as_list(&self) -> Option<&[Value<'b>]> {
match self {
Self::List(v) => Some(v),
_ => None,
}
}
pub fn into_list(self) -> Option<Vec<Value<'b>>> {
match self {
Self::List(v) => Some(v),
_ => None,
}
}
pub fn as_flags(&self) -> Option<&[u32]> {
match self {
Self::Flags(v) => Some(v),
_ => None,
}
}
pub fn into_flags(self) -> Option<Vec<u32>> {
match self {
Self::Flags(v) => Some(v),
_ => None,
}
}
}
impl<'b> Value<'b> {
pub fn get_as<'t, V: FromValue<'t, 'b>>(&'t self) -> V {
self.try_get_as().unwrap()
}
pub fn try_get_as<'t, V: FromValue<'t, 'b>>(&'t self) -> BdatResult<V> {
V::extract(self).ok_or_else(|| BdatError::ValueCast(self.into()))
}
pub fn to_integer(&self) -> u32 {
match self {
Self::SignedByte(b) => *b as u32,
Self::Percent(b) | Self::UnsignedByte(b) | Self::Unknown2(b) => *b as u32,
Self::SignedShort(s) => *s as u32,
Self::UnsignedShort(s) | Self::Unknown3(s) => *s as u32,
Self::SignedInt(i) => *i as u32,
Self::UnsignedInt(i) | Self::HashRef(i) => *i,
_ => panic!("value is not an integer"),
}
}
pub fn to_float(&self) -> f32 {
match self {
Self::Float(f) => (*f).into(),
_ => panic!("value is not a float"),
}
}
pub fn into_string(self) -> String {
match self {
Self::String(s) | Self::DebugString(s) => s.into_owned(),
_ => panic!("value is not a string"),
}
}
pub fn as_str(&self) -> &str {
match self {
Self::String(s) | Self::DebugString(s) => s.as_ref(),
_ => panic!("value is not a string"),
}
}
}
impl ValueType {
pub fn data_len(self) -> usize {
use ValueType::*;
match self {
Unknown => 0,
UnsignedByte | SignedByte | Percent | Unknown2 => 1,
UnsignedShort | SignedShort | Unknown3 => 2,
UnsignedInt | SignedInt | String | Float | HashRef | DebugString => 4,
}
}
pub fn is_supported(self, version: BdatVersion) -> bool {
use ValueType::*;
match self {
Percent | Unknown2 | Unknown3 | HashRef | DebugString => version == BdatVersion::Modern,
_ => true,
}
}
}
impl From<ValueType> for u8 {
fn from(t: ValueType) -> Self {
t as u8
}
}
impl<'t, 'tb> RowRef<'t, 'tb> {
pub fn into_modern(self) -> RowRef<'t, 'tb, ModernCell<'t, 'tb>> {
self.down_cast()
}
}
impl<'t, 'tb> FromCell<'t, 'tb> for ModernCell<'t, 'tb> {
fn from_cell(cell: &'t Cell<'tb>) -> Self {
match cell {
Cell::Single(v) => v,
_ => panic!("only Cell::Single supported in modern bdats"),
}
}
}
impl<'t, 'tb: 't, T> FromCell<'t, 'tb> for T
where
T: From<&'t Cell<'tb>>,
{
fn from_cell(cell: &'t Cell<'tb>) -> Self {
Self::from(cell)
}
}
macro_rules! default_display {
($fmt:expr, $val:expr, $($variants:tt ) *) => {
match $val {
$(
Value::$variants(a) => a.fmt($fmt),
)*
v => panic!("Unsupported Display {:?}", v)
}
};
}
impl<'b> Display for Value<'b> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Unknown => Ok(()),
Self::HashRef(h) => Label::Hash(*h).fmt(f),
Self::Percent(v) => write!(f, "{}%", v),
v => {
default_display!(f, v, SignedByte SignedShort SignedInt UnsignedByte UnsignedShort UnsignedInt DebugString Unknown2 Unknown3 String Float)
}
}
}
}
impl<'b> Display for Cell<'b> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Single(val) => val.fmt(f),
Cell::List(list) => {
write!(f, "[")?;
for (i, value) in list.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
value.fmt(f)?;
}
write!(f, "]")
}
Cell::Flags(nums) => {
write!(f, "{{")?;
for (i, value) in nums.iter().enumerate() {
if i != 0 {
write!(f, ", ")?;
}
value.fmt(f)?;
}
write!(f, "}}")
}
}
}
}
macro_rules! from_value {
($val:ty, $($variants:path ) *) => {
impl<'t, 'tb> FromValue<'t, 'tb> for $val {
fn extract(value: &Value<'_>) -> Option<Self> {
match value {
$( | $variants(v) )* => Some(*v),
_ => None,
}
}
}
};
}
from_value!(u32, Value::UnsignedInt Value::HashRef);
from_value!(u16, Value::UnsignedShort Value::Unknown3);
from_value!(u8, Value::UnsignedByte Value::Unknown2 Value::Percent);
from_value!(i32, Value::SignedInt);
from_value!(i16, Value::SignedShort);
from_value!(i8, Value::SignedByte);
from_value!(BdatReal, Value::Float);
impl<'t, 'tb> FromValue<'t, 'tb> for f32 {
fn extract(value: &Value<'_>) -> Option<Self> {
BdatReal::extract(value).map(Into::into)
}
}
impl<'t, 'tb> FromValue<'t, 'tb> for Utf<'tb> {
fn extract(value: &Value<'tb>) -> Option<Self> {
match value {
Value::String(s) | Value::DebugString(s) => Some(s.clone()),
_ => None,
}
}
}
impl<'t, 'tb> FromValue<'t, 'tb> for &'t str {
fn extract(value: &'t Value<'tb>) -> Option<Self> {
match value {
Value::String(s) | Value::DebugString(s) => Some(s.as_ref()),
_ => None,
}
}
}