use crate::{Parser, PaxParser, Rule};
use helpers::PaxNumeric;
use pax_runtime_api::PaxValue;
use pest::iterators::Pair;
use serde::de::{self, Visitor};
use serde::{forward_to_deserialize_any, Deserialize};
pub mod error;
mod helpers;
mod tests;
use self::helpers::{PaxEnum, PaxObject, PaxSeq};
pub use error::{Error, Result};
use pax_runtime_api::constants::{
COLOR, DEGREES, INTEGER, NUMERIC, PERCENT, PIXELS, RADIANS, ROTATION, SIZE,
};
const STRING: &str = "String";
const BOOL: &str = "Bool";
const OPTION: &str = "Option";
const VEC: &str = "Vec";
const ENUM: &str = "Enum";
const OBJECT: &str = "Object";
pub fn from_pax(str: &str) -> Result<PaxValue> {
let ast = if let Ok(mut ast) = PaxParser::parse(Rule::literal_value, &str) {
ast.next().unwrap()
} else {
return Err(Error::Message(format!("Could not parse: {}", str)));
};
let deserializer: PaxDeserializer = PaxDeserializer::from(ast);
let t = PaxValue::deserialize(deserializer)?;
Ok(t)
}
pub fn from_pax_ast(ast: Pair<Rule>) -> Result<PaxValue> {
let deserializer: PaxDeserializer = PaxDeserializer::from(ast);
let t = PaxValue::deserialize(deserializer)?;
Ok(t)
}
pub struct PaxDeserializer<'de> {
pub ast: Pair<'de, Rule>,
}
impl<'de> PaxDeserializer<'de> {
pub fn from(ast: Pair<'de, Rule>) -> Self {
PaxDeserializer { ast }
}
fn deserialize_pax_value<V>(mut self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if let Rule::literal_value = self.ast.as_rule() {
self.ast = self.ast.into_inner().next().unwrap();
}
match self.ast.as_rule() {
Rule::literal_color => {
visitor.visit_enum(PaxEnum::new_pax_value(COLOR, Some(self.ast)))
}
Rule::literal_number => {
visitor.visit_enum(PaxEnum::new_pax_value(NUMERIC, Some(self.ast)))
}
Rule::literal_number_with_unit => {
let unit = self
.ast
.clone()
.into_inner()
.nth(1)
.unwrap()
.as_str()
.trim();
match unit {
"%" => visitor.visit_enum(PaxEnum::new_pax_value(PERCENT, Some(self.ast))),
"px" => visitor.visit_enum(PaxEnum::new_pax_value(SIZE, Some(self.ast))),
"rad" => visitor.visit_enum(PaxEnum::new_pax_value(ROTATION, Some(self.ast))),
"deg" => visitor.visit_enum(PaxEnum::new_pax_value(ROTATION, Some(self.ast))),
_ => {
unreachable!("Unsupported unit: {}", unit)
}
}
}
Rule::string => visitor.visit_enum(PaxEnum::new_pax_value(STRING, Some(self.ast))),
Rule::literal_list | Rule::literal_tuple => {
visitor.visit_enum(PaxEnum::new_pax_value(VEC, Some(self.ast)))
}
Rule::literal_enum_value => {
visitor.visit_enum(PaxEnum::new_pax_value(ENUM, Some(self.ast)))
}
Rule::literal_option => {
visitor.visit_enum(PaxEnum::new_pax_value(OPTION, Some(self.ast)))
}
Rule::literal_boolean => visitor.visit_enum(PaxEnum::new(BOOL, Some(self.ast))),
Rule::literal_object => {
visitor.visit_enum(PaxEnum::new_pax_value(OBJECT, Some(self.ast)))
}
_ => Err(Error::UnsupportedType(self.ast.to_string())),
}
}
fn deserialize_builtin<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
let ret = match self.ast.as_rule() {
Rule::literal_color => {
let what_kind_of_color = self.ast.into_inner().next().unwrap();
match what_kind_of_color.as_rule() {
Rule::literal_color_space_func => {
let func = what_kind_of_color
.as_str()
.trim()
.split("(")
.next()
.unwrap();
let color_args = Some(what_kind_of_color);
visitor.visit_enum(PaxEnum::new(func, color_args))
}
Rule::literal_color_const => {
let explicit_color =
visitor.visit_enum(PaxEnum::new(what_kind_of_color.as_str(), None));
explicit_color
}
_ => {
unreachable!()
}
}
}
Rule::literal_color_channel => {
let channel = self.ast.into_inner().next().unwrap();
match channel.as_rule() {
Rule::literal_number_integer => {
visitor.visit_enum(PaxEnum::new(INTEGER, Some(channel)))
}
Rule::literal_number_with_unit => {
let unit = channel.clone().into_inner().nth(1).unwrap().as_str().trim();
let number = channel.clone().into_inner().next().unwrap();
match unit {
"%" => visitor.visit_enum(PaxEnum::new(PERCENT, Some(number))),
"rad" => visitor.visit_enum(PaxEnum::new(ROTATION, Some(channel))),
"deg" => visitor.visit_enum(PaxEnum::new(ROTATION, Some(channel))),
_ => Err(Error::Message(format!(
"Unsupported unit: {} for ColorChannel",
unit
))),
}
}
_ => Err(Error::Message(format!(
"Unsupported type: {} for ColorChannel",
channel.as_str()
))),
}
}
Rule::literal_number => {
let number = self.ast.into_inner().next().unwrap();
visitor.visit_enum(PaxNumeric::new(number, true))
}
Rule::literal_number_integer | Rule::literal_number_float => {
visitor.visit_enum(PaxNumeric::new(self.ast, true))
}
Rule::literal_number_with_unit => {
let inner = self.ast.into_inner();
let number = inner.clone().next();
let unit = inner.clone().nth(1).unwrap().as_str().trim();
match unit {
"%" => visitor.visit_newtype_struct(PaxDeserializer::from(number.unwrap())),
"px" => visitor.visit_enum(PaxEnum::new(PIXELS, number)),
"rad" => visitor.visit_enum(PaxEnum::new(RADIANS, number)),
"deg" => visitor.visit_enum(PaxEnum::new(DEGREES, number)),
_ => Err(Error::Message(format!("Unsupported unit: {}", unit))),
}
}
Rule::string => {
let string_within_quotes =
self.ast.into_inner().next().unwrap().as_str().to_string();
visitor.visit_string(string_within_quotes)
}
Rule::literal_boolean => {
let bool_str = self.ast.as_str();
visitor.visit_bool(bool_str.parse::<bool>().unwrap())
}
Rule::identifier | Rule::pascal_identifier => visitor.visit_str(self.ast.as_str()),
_ => Err(Error::UnsupportedType(self.ast.to_string())),
}?;
Ok(ret)
}
}
impl<'de> de::Deserializer<'de> for PaxDeserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_builtin(visitor)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf unit unit_struct newtype_struct tuple identifier
tuple_struct struct ignored_any
}
fn deserialize_enum<V>(
self,
name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> std::result::Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
if name == "PaxValue" {
return self.deserialize_pax_value(visitor);
}
self.deserialize_any(visitor)
}
fn deserialize_seq<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_seq(PaxSeq::new(self.ast.into_inner()))
}
fn deserialize_option<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
let unwrapped_option = self.ast.into_inner().next().unwrap();
match unwrapped_option.as_rule() {
Rule::literal_none => visitor.visit_none(),
Rule::literal_some => visitor.visit_some(PaxDeserializer::from(
unwrapped_option.into_inner().next().unwrap(),
)),
_ => Err(Error::Message(format!(
"Unexpected format for Option: {}",
unwrapped_option.as_str()
))),
}
}
fn deserialize_map<V>(self, visitor: V) -> std::result::Result<V::Value, Self::Error>
where
V: Visitor<'de>,
{
visitor.visit_map(PaxObject::new(self.ast.into_inner()))
}
}