use crate::{macros::gen_hash_map_helpers, pbxproj::PBXObjectKind};
use anyhow::{bail, Result};
use derive_deref_rs::Deref;
use derive_is_enum_variant::is_enum_variant;
use enum_as_inner::EnumAsInner;
use enum_variant_macros::FromVariants;
use std::collections::HashMap;
#[derive(Debug, PartialEq, Eq, FromVariants, EnumAsInner, is_enum_variant)]
pub enum PBXValue {
String(String),
Object(PBXHashMap),
Vec(PBXVec),
Number(isize),
Bool(bool),
Kind(PBXObjectKind),
Null(()),
}
impl TryFrom<PBXValue> for String {
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> Result<Self, Self::Error> {
value.try_into_string()
}
}
impl TryFrom<PBXValue> for PBXHashMap {
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> Result<Self, Self::Error> {
value.try_into_object()
}
}
impl TryFrom<PBXValue> for PBXVec {
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> Result<Self, Self::Error> {
value.try_into_vec()
}
}
impl TryFrom<PBXValue> for bool {
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> Result<Self, Self::Error> {
value.try_into_bool()
}
}
impl TryFrom<PBXValue> for isize {
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> Result<Self, Self::Error> {
value.try_into_number()
}
}
impl TryFrom<PBXValue> for PBXObjectKind {
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> Result<Self, Self::Error> {
value.try_into_kind()
}
}
impl<T> TryFrom<PBXValue> for HashMap<String, T>
where
T: From<PBXValue>,
{
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> anyhow::Result<Self> {
Ok(value
.try_into_object()?
.0
.into_iter()
.map(|(k, v)| (k, v.into()))
.collect())
}
}
impl<T> From<HashMap<String, T>> for PBXValue
where
T: TryFrom<PBXValue>,
<T as TryFrom<PBXValue>>::Error: std::fmt::Debug,
PBXValue: From<T>,
{
fn from(v: HashMap<String, T>) -> Self {
let inner = v
.into_iter()
.map(|(k, v)| (k, v.try_into().unwrap()))
.collect();
PBXValue::Object(PBXHashMap(inner))
}
}
impl From<&str> for PBXValue {
fn from(v: &str) -> Self {
PBXValue::String(v.to_string())
}
}
impl<T> From<Vec<T>> for PBXValue
where
PBXValue: From<T>,
{
fn from(v: Vec<T>) -> Self {
let inner = v.into_iter().map(|v| v.into()).collect();
PBXValue::Vec(PBXVec(inner))
}
}
impl<T> TryFrom<PBXValue> for Vec<T>
where
T: TryFrom<PBXValue>,
<T as TryFrom<PBXValue>>::Error: std::fmt::Debug,
{
type Error = anyhow::Error;
fn try_from(value: PBXValue) -> anyhow::Result<Self> {
Ok(value
.try_into_vec()?
.0
.into_iter()
.map(|v| v.try_into().unwrap())
.collect())
}
}
impl<T> From<Option<T>> for PBXValue
where
PBXValue: From<T>,
{
fn from(t: Option<T>) -> Self {
if let Some(v) = t {
v.into()
} else {
Self::Null(())
}
}
}
impl PBXValue {
pub fn try_into_string(self) -> Result<String> {
if let Self::String(v) = self {
Ok(v)
} else {
bail!("expected string got {self:#?}")
}
}
pub fn try_into_object(self) -> Result<PBXHashMap> {
if let Self::Object(v) = self {
Ok(v)
} else {
bail!("expected object got {self:#?}")
}
}
pub fn try_into_vec(self) -> Result<PBXVec> {
if let Self::Vec(v) = self {
Ok(v)
} else {
bail!("expected Vec got {self:#?}")
}
}
pub fn try_into_number(self) -> Result<isize> {
if let Self::Number(v) = self {
Ok(v)
} else {
bail!("expected number got {self:#?}")
}
}
pub fn try_into_bool(self) -> Result<bool> {
if let Self::Bool(v) = self {
Ok(v)
} else {
bail!("expected bool got {self:#?}")
}
}
pub fn try_into_kind(self) -> Result<PBXObjectKind> {
if let Self::Kind(v) = self {
Ok(v)
} else {
bail!("expected kind got {self:#?}")
}
}
}
#[derive(Default, Debug, Deref, PartialEq, Eq)]
pub struct PBXHashMap(pub(crate) HashMap<String, PBXValue>);
impl PBXHashMap {
pub fn new(inner: HashMap<String, PBXValue>) -> Self {
Self(inner)
}
pub fn get_value(&self, key: &str) -> Option<&PBXValue> {
self.0.get(key)
}
pub fn try_get_value(&self, key: &str) -> Result<&PBXValue> {
self.0
.get(key)
.ok_or_else(|| anyhow::anyhow!("{key} is not found!"))
}
pub fn remove_value(&mut self, key: &str) -> Option<PBXValue> {
self.try_remove_value(key).ok()
}
pub fn try_remove_value(&mut self, key: &str) -> Result<PBXValue> {
self.0
.remove(key)
.ok_or_else(|| anyhow::anyhow!("{key} is not found!"))
}
}
gen_hash_map_helpers! {
[string, String],
[vec, PBXVec],
[bool, bool],
[number, isize],
[kind, PBXObjectKind],
[object, PBXHashMap]
}
#[derive(Default, Debug, Deref, PartialEq, Eq, derive_new::new)]
pub struct PBXVec(pub(crate) Vec<PBXValue>);
impl PBXVec {
pub(crate) fn try_into_vec_strings(self) -> Result<Vec<String>> {
let mut collector = vec![];
for value in self.0 {
collector.push(value.try_into_string()?);
}
Ok(collector)
}
pub(crate) fn as_vec_strings<'a>(&'a self) -> Vec<&'a String> {
let mut collector = vec![];
for value in self.0.iter() {
if let Some(str) = value.as_string() {
collector.push(str);
}
}
collector
}
pub(crate) fn try_into_vec<T: TryFrom<T> + From<PBXValue>>(self) -> Result<Vec<T>> {
let mut collector = vec![];
for value in self.0 {
collector.push(value.try_into()?);
}
Ok(collector)
}
}