use std::fmt::Display;
use crate::{
defs::{ResTableRef, ResourceMap},
res_value::{ResValue, ResValueType},
string_pool::{ResStringPoolRef, StringPoolHandler},
xmltree::{XMLTree, XMLTreeNode},
};
#[derive(Debug)]
pub enum FromResNodeErrorType {
NoAttr(&'static str, Vec<String>),
FromResValue(&'static str, FromResValueError),
NoString(ResStringPoolRef),
UnknownAttr(String),
UnknownChild(String),
UnknownAttrs(Vec<String>),
UnknownChildren(Vec<String>),
NoChild(&'static str, Vec<String>),
TooManyChildrenForOptional(usize),
}
impl Display for FromResNodeErrorType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
FromResNodeErrorType::NoAttr(attr, items) => format!(
"no attribute with name: {attr}. Expected attrs: {}",
items.join(",")
),
FromResNodeErrorType::FromResValue(value, e) =>
format!("failed to parse value: {value} due to {e}"),
FromResNodeErrorType::NoString(res_string_pool_ref) =>
format!("no string found with id: {}", res_string_pool_ref.index),
FromResNodeErrorType::UnknownAttr(a) => format!("unexpected attribute: {a}"),
FromResNodeErrorType::UnknownChild(c) => format!("unexpected child: {c}"),
FromResNodeErrorType::UnknownAttrs(items) =>
format!("unknown attributes: {}", items.join(",")),
FromResNodeErrorType::UnknownChildren(items) =>
format!("unknown children: {}", items.join(",")),
FromResNodeErrorType::NoChild(c, items) => format!(
"no child found with name: {c}, expected: {}",
items.join(",")
),
FromResNodeErrorType::TooManyChildrenForOptional(i) =>
format!("expected 1 or 0 children, got: {i}"),
}
)
}
}
#[derive(Debug)]
pub struct FromNodeError {
context: Vec<String>,
error: FromResNodeErrorType,
}
impl Display for FromNodeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"error: {}\nContext: {}",
self.error,
self.context.join("\n")
)
}
}
impl FromNodeError {
pub fn new(context: String, error: FromResNodeErrorType) -> Self {
Self {
context: vec![context],
error,
}
}
pub fn new_2(c1: String, c2: String, error: FromResNodeErrorType) -> Self {
Self {
context: vec![c1, c2],
error,
}
}
pub fn new_empty(error: FromResNodeErrorType) -> Self {
Self {
context: vec![],
error,
}
}
pub fn add_context_2(&mut self, c1: String, c2: String) {
self.context.push(c1);
self.context.push(c2);
}
pub fn with_context_2(mut self, c1: String, c2: String) -> Self {
self.add_context_2(c1, c2);
self
}
}
pub trait FromNode: Sized {
type Error;
fn from_node(node: XMLTreeNode, string_pool: &StringPoolHandler) -> Result<Self, Self::Error>;
fn from_tree(tree: XMLTree) -> Result<Self, Self::Error> {
Self::from_node(tree.root, &tree.string_pool)
}
}
impl XMLTreeNode {
pub fn to_typed<T: FromNode>(self, string_pool: &StringPoolHandler) -> Result<T, T::Error> {
T::from_node(self, string_pool)
}
}
pub trait IntoNode {
fn into_node(
self,
string_pool: &mut StringPoolHandler,
res_map: &mut ResourceMap,
) -> XMLTreeNode;
fn into_tree(self) -> XMLTree
where
Self: Sized,
{
let mut sp = StringPoolHandler::new_empty();
let mut res_map = ResourceMap::new();
let node = self.into_node(&mut sp, &mut res_map);
XMLTree {
string_pool: sp,
resource_map: Some(res_map),
root: node,
}
}
}
pub trait IntoResValue {
fn into_res_value(self, string_pool: &mut StringPoolHandler) -> ResValue;
}
pub trait FromResValue: Sized {
type Error;
fn from_res_value(
value: ResValue,
string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error>;
}
impl IntoResValue for ResTableRef {
fn into_res_value(self, _string_pool: &mut StringPoolHandler) -> ResValue {
ResValue::new(crate::res_value::ResValueType::Reference(self))
}
}
impl ResValue {
pub fn to_typed<T: FromResValue>(self, string_pool: &StringPoolHandler) -> Result<T, T::Error> {
T::from_res_value(self, string_pool)
}
}
#[derive(Debug)]
pub enum FromResValueError {
InvalidType(&'static str, ResValueType),
NoStringFound(crate::string_pool::ResStringPoolRef),
}
impl Display for FromResValueError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
FromResValueError::InvalidType(expected, res_value_type) =>
format!("invalid type: {res_value_type:?}, expected: {expected}"),
FromResValueError::NoStringFound(res_string_pool_ref) =>
format!("did not find string: {res_string_pool_ref:?} in string pool"),
}
)
}
}
impl FromResValue for ResTableRef {
type Error = FromResValueError;
fn from_res_value(
value: ResValue,
_string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error> {
if let ResValueType::Reference(table_ref) = value.data {
Ok(table_ref)
} else {
Err(FromResValueError::InvalidType("Reference", value.data))
}
}
}
impl IntoResValue for String {
fn into_res_value(self, string_pool: &mut StringPoolHandler) -> ResValue {
ResValue::new_str(self.into(), string_pool)
}
}
impl FromResValue for String {
type Error = FromResValueError;
fn from_res_value(
value: ResValue,
string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error> {
if let ResValueType::String(string_pool_ref) = value.data {
Ok(string_pool_ref
.resolve(string_pool)
.ok_or(FromResValueError::NoStringFound(string_pool_ref))?
.to_string())
} else {
Err(FromResValueError::InvalidType("String", value.data))
}
}
}
impl IntoResValue for bool {
fn into_res_value(self, _string_pool: &mut StringPoolHandler) -> ResValue {
ResValue::new_bool(self)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Referenced<T> {
T(T),
Referenced(ResTableRef),
}
impl<T: IntoResValue> IntoResValue for Referenced<T> {
fn into_res_value(self, string_pool: &mut StringPoolHandler) -> ResValue {
match self {
Referenced::T(t) => t.into_res_value(string_pool),
Referenced::Referenced(res_table_ref) => res_table_ref.into_res_value(string_pool),
}
}
}
impl<T: FromResValue> FromResValue for Referenced<T> {
type Error = T::Error;
fn from_res_value(
value: ResValue,
string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error> {
if let ResValueType::Reference(reference) = value.data {
Ok(Self::Referenced(reference))
} else {
Ok(Self::T(T::from_res_value(value, string_pool)?))
}
}
}
impl<T: Default> Default for Referenced<T> {
fn default() -> Self {
Self::T(T::default())
}
}
macro_rules! impl_to_int {
($type:ty) => {
impl FromResValue for $type {
type Error = FromResValueError;
fn from_res_value(
value: ResValue,
_string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error> {
if let ResValueType::IntDec(int) = value.data {
Ok(int as $type)
} else if let ResValueType::IntHex(int) = value.data {
Ok(int as $type)
} else {
Err(FromResValueError::InvalidType(
"IntDec or IntHex",
value.data,
))
}
}
}
};
}
impl_to_int!(u32);
impl_to_int!(u16);
impl_to_int!(u8);
impl_to_int!(usize);
impl_to_int!(i32);
impl_to_int!(i16);
impl_to_int!(i8);
impl FromResValue for f32 {
type Error = FromResValueError;
fn from_res_value(
value: ResValue,
_string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error> {
if let ResValueType::Float(val) = value.data {
Ok(val)
} else {
Err(FromResValueError::InvalidType("Float", value.data))
}
}
}
macro_rules! impl_int_dec {
($type:ty) => {
impl IntoResValue for $type {
fn into_res_value(self, _string_pool: &mut StringPoolHandler) -> ResValue {
ResValue::new(crate::res_value::ResValueType::IntDec(self as u32))
}
}
};
}
macro_rules! impl_float {
($type:ty) => {
impl IntoResValue for $type {
fn into_res_value(self, _string_pool: &mut StringPoolHandler) -> ResValue {
ResValue::new(crate::res_value::ResValueType::Float(self as f32))
}
}
};
}
impl FromResValue for bool {
type Error = FromResValueError;
fn from_res_value(
value: ResValue,
_string_pool: &StringPoolHandler,
) -> Result<Self, Self::Error> {
if let ResValueType::IntBoolean(val) = value.data {
Ok(val.into())
} else {
Err(FromResValueError::InvalidType("IntBoolean", value.data))
}
}
}
#[cfg(feature = "derive")]
#[macro_export]
macro_rules! impl_enum {
{
$enum:ident, $($name:ident => $val:expr),*
} => {
impl crate::traits::IntoResValue for $enum {
fn into_res_value(
self,
string_pool: &mut crate::string_pool::StringPoolHandler,
) -> crate::res_value::ResValue {
crate::traits::IntoResValue::into_res_value(self.0, string_pool)
}
}
impl crate::traits::FromResValue for $enum {
type Error = crate::traits::FromResValueError;
fn from_res_value(
value: crate::res_value::ResValue,
string_pool: &crate::string_pool::StringPoolHandler,
) -> Result<Self, Self::Error> {
let val = crate::traits::FromResValue::from_res_value(value, string_pool)?;
Ok(Self(val))
}
}
};
}
impl_int_dec!(i32);
impl_int_dec!(i16);
impl_int_dec!(i8);
impl_int_dec!(u32);
impl_int_dec!(u16);
impl_int_dec!(u8);
impl_int_dec!(usize);
impl_float!(f32);
pub trait Mergeable {
type Returns;
fn merge(&mut self, other: Self) -> Self::Returns;
}