use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::default::Default;
use std::fmt;
use crate::ast::{BuiltinType, Literal, SpannedNode, Value};
use crate::errors::{DecodeError, ExpectedType};
use crate::traits::{Decode, ErrorSpan};
#[derive(Debug, Default)]
pub struct Context<S: ErrorSpan> {
errors: Vec<DecodeError<S>>,
extensions: HashMap<TypeId, Box<dyn Any>>,
}
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub enum Kind {
Int,
Decimal,
String,
Bool,
#[default]
Null,
Nan,
Inf,
NegInf,
}
pub fn bytes<S: ErrorSpan>(value: &Value<S>, ctx: &mut Context<S>) -> Vec<u8> {
if let Some(typ) = &value.type_name {
match typ.as_builtin() {
Some(&BuiltinType::Base64) => {
#[cfg(feature = "base64")]
{
use base64::{engine::general_purpose::STANDARD, Engine};
match &*value.literal {
Literal::String(s) => match STANDARD.decode(s.as_bytes()) {
Ok(vec) => vec,
Err(e) => {
ctx.emit_error(DecodeError::conversion(&value.literal, e));
Default::default()
}
},
_ => {
ctx.emit_error(DecodeError::scalar_kind(Kind::String, &value.literal));
Default::default()
}
}
}
#[cfg(not(feature = "base64"))]
{
ctx.emit_error(DecodeError::unsupported(
&value.literal,
"base64 support is not compiled in",
));
Default::default()
}
}
_ => {
ctx.emit_error(DecodeError::TypeName {
span: typ.span().clone(),
found: Some(typ.value.clone()),
expected: ExpectedType::optional(BuiltinType::Base64),
rust_type: "bytes",
});
Default::default()
}
}
} else {
match &*value.literal {
Literal::String(s) => s.as_bytes().to_vec(),
_ => {
ctx.emit_error(DecodeError::scalar_kind(Kind::String, &value.literal));
Default::default()
}
}
}
}
pub fn check_flag_node<S: ErrorSpan>(node: &SpannedNode<S>, ctx: &mut Context<S>) {
for arg in &node.arguments {
ctx.emit_error(DecodeError::unexpected(
&arg.literal,
"argument",
"unexpected argument",
));
}
for name in node.properties.keys() {
ctx.emit_error(DecodeError::unexpected(
name,
"property",
format!("unexpected property `{}`", name.escape_default()),
));
}
if let Some(children) = &node.children {
for child in children.iter() {
ctx.emit_error(DecodeError::unexpected(
child,
"node",
format!("unexpected node `{}`", child.node_name.escape_default()),
));
}
}
}
pub fn node<T, S>(ast: &SpannedNode<S>) -> Result<T, Vec<DecodeError<S>>>
where
T: Decode<S>,
S: ErrorSpan,
{
let mut ctx = Context::new();
match Decode::decode_node(ast, &mut ctx) {
Ok(_) if ctx.has_errors() => Err(ctx.into_errors()),
Err(e) => {
ctx.emit_error(e);
Err(ctx.into_errors())
}
Ok(v) => Ok(v),
}
}
impl<S: ErrorSpan> Context<S> {
pub(crate) fn new() -> Context<S> {
Context {
errors: Vec::new(),
extensions: HashMap::new(),
}
}
pub fn emit_error(&mut self, err: impl Into<DecodeError<S>>) {
self.errors.push(err.into());
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub(crate) fn into_errors(self) -> Vec<DecodeError<S>> {
self.errors
}
pub fn set<T: 'static>(&mut self, value: T) {
self.extensions.insert(TypeId::of::<T>(), Box::new(value));
}
pub fn get<T: 'static>(&self) -> Option<&T> {
self.extensions
.get(&TypeId::of::<T>())
.and_then(|b| b.downcast_ref())
}
}
impl fmt::Display for Kind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.as_str())
}
}
impl From<&'_ Literal> for Kind {
fn from(lit: &Literal) -> Kind {
use Kind as K;
use Literal as L;
match lit {
L::Int(_) => K::Int,
L::Decimal(_) => K::Decimal,
L::String(_) => K::String,
L::Bool(_) => K::Bool,
L::Null => K::Null,
L::Nan => K::Nan,
L::Inf => K::Inf,
L::NegInf => K::NegInf,
}
}
}
impl Kind {
pub const fn as_str(&self) -> &'static str {
use Kind::*;
match self {
Int => "integer",
Decimal => "decimal",
String => "string",
Bool => "boolean",
Null => "null",
Nan => "nan",
Inf => "inf",
NegInf => "-inf",
}
}
}