resand 0.3.0

Read and write ARSC and AXML binary files used for Android Resources
Documentation
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;
}