use errors::Result;
use serde::Serialize;
use std::fmt;
use std::vec;
use {BigInt, Diagnostics, Flavor, Loc, RpCode, RpNumber, RpReg, RpValue, Span, Translate,
Translator};
decl_body!(pub struct RpEnumBody<F> {
pub enum_type: F::EnumType,
pub variants: RpVariants<F>,
pub codes: Vec<Loc<RpCode>>,
});
impl<F: 'static, T> Translate<T> for RpEnumBody<F>
where
F: Flavor,
T: Translator<Source = F>,
{
type Source = F;
type Out = RpEnumBody<T::Target>;
fn translate(self, diag: &mut Diagnostics, translator: &T) -> Result<RpEnumBody<T::Target>> {
translator.visit(diag, &self.name)?;
let name = translator.translate_local_name(diag, RpReg::Enum, self.name)?;
let enum_type = translator.translate_enum_type(diag, self.enum_type)?;
Ok(RpEnumBody {
name: name,
ident: self.ident,
comment: self.comment,
decls: self.decls.translate(diag, translator)?,
enum_type,
variants: self.variants.translate(diag, translator)?,
codes: self.codes,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RpVariantValue<'a> {
String(&'a str),
Number(&'a RpNumber),
}
impl<'a> From<&'a RpNumber> for RpVariantValue<'a> {
fn from(value: &'a RpNumber) -> Self {
RpVariantValue::Number(value)
}
}
impl<'a> From<&'a String> for RpVariantValue<'a> {
fn from(value: &'a String) -> Self {
RpVariantValue::String(value.as_str())
}
}
impl<'a> fmt::Display for RpVariantValue<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::RpVariantValue::*;
match *self {
String(string) => write!(fmt, "{:?}", string),
Number(value) => value.fmt(fmt),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct RpVariantRef<'a, F: 'static>
where
F: Flavor,
{
pub span: Span,
pub name: &'a F::Name,
pub ident: &'a Loc<String>,
pub comment: &'a Vec<String>,
pub value: RpVariantValue<'a>,
}
impl<'a, F: 'static> RpVariantRef<'a, F>
where
F: Flavor,
{
pub fn ident(&self) -> &'a str {
self.ident.as_str()
}
}
impl<'a, F: 'static> fmt::Display for RpVariantRef<'a, F>
where
F: Flavor,
{
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write!(fmt, "{} as {}", self.name, self.value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
#[serde(bound = "F::Package: Serialize, F::Name: Serialize, V: Serialize")]
pub struct RpVariant<F: 'static, V>
where
F: Flavor,
{
pub name: F::Name,
pub ident: Loc<String>,
pub comment: Vec<String>,
pub value: V,
}
impl<'a, F: 'static, V> RpVariant<F, V>
where
F: Flavor,
{
pub fn ident(&self) -> &str {
self.ident.as_str()
}
}
impl<'a, F: 'static, V: 'a> RpVariant<F, V>
where
F: Flavor,
RpVariantValue<'a>: From<&'a V>,
{
pub fn value(&'a self) -> RpVariantValue<'a> {
RpVariantValue::from(&self.value)
}
}
impl<F: 'static, T, V> Translate<T> for RpVariant<F, V>
where
F: Flavor,
T: Translator<Source = F>,
{
type Source = F;
type Out = RpVariant<T::Target, V>;
fn translate(self, diag: &mut Diagnostics, translator: &T) -> Result<RpVariant<T::Target, V>> {
translator.visit(diag, &self.name)?;
let name = translator.translate_local_name(diag, RpReg::EnumVariant, self.name)?;
Ok(RpVariant {
name: name,
ident: self.ident,
comment: self.comment,
value: self.value,
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum RpEnumType {
String,
U32,
U64,
I32,
I64,
}
impl RpEnumType {
pub fn is_assignable_from(&self, value: &RpValue) -> bool {
use self::RpEnumType::*;
match (self, value) {
(&String, &RpValue::String(_)) => true,
(&U32, &RpValue::Number(_)) => true,
(&U64, &RpValue::Number(_)) => true,
(&I32, &RpValue::Number(_)) => true,
(&I64, &RpValue::Number(_)) => true,
_ => false,
}
}
pub fn validate_number(&self, number: &RpNumber) -> Result<()> {
const MAX_SAFE_INTEGER: i64 = 9007199254740991i64;
const MIN_SAFE_INTEGER: i64 = -9007199254740991i64;
use self::RpEnumType::*;
let (mn, mx): (BigInt, BigInt) = match *self {
String => return Err("expected number, got `string`".into()),
U32 => (0u32.into(), i32::max_value().into()),
U64 => (0u64.into(), MAX_SAFE_INTEGER.into()),
I32 => (i32::min_value().into(), i32::max_value().into()),
I64 => (MIN_SAFE_INTEGER.into(), MAX_SAFE_INTEGER.into()),
};
let n = number.to_bigint().ok_or_else(|| "not a whole number")?;
if &mn <= n && n <= &mx {
return Ok(());
}
return Err(format!("number is not within {} to {} (inclusive)", mn, mx).into());
}
}
impl fmt::Display for RpEnumType {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use self::RpEnumType::*;
match *self {
String => "string".fmt(fmt),
U32 => "u32".fmt(fmt),
U64 => "u64".fmt(fmt),
I32 => "i32".fmt(fmt),
I64 => "i64".fmt(fmt),
}
}
}
#[derive(Debug, Clone, Serialize)]
#[serde(bound = "F: Serialize, F::Package: Serialize, F::Name: Serialize")]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RpVariants<F: 'static>
where
F: Flavor,
{
String {
variants: Vec<Loc<RpVariant<F, String>>>,
},
Number {
variants: Vec<Loc<RpVariant<F, RpNumber>>>,
},
}
pub struct RpVariantsIter<'a, F: 'static>
where
F: Flavor,
{
iter: vec::IntoIter<RpVariantRef<'a, F>>,
}
impl<'a, F: 'static> Iterator for RpVariantsIter<'a, F>
where
F: Flavor,
{
type Item = RpVariantRef<'a, F>;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next()
}
}
impl<F: 'static> RpVariants<F>
where
F: Flavor,
{
pub fn iter(&self) -> RpVariantsIter<F> {
use self::RpVariants::*;
macro_rules! variants {
($slf:ident, $($ty:ident),*) => {
match *$slf {
$(
$ty { ref variants } => {
let mut __o = Vec::new();
for v in variants {
let (value, span) = Loc::borrow_pair(v);
__o.push(RpVariantRef {
span: span,
name: &value.name,
ident: &value.ident,
comment: &value.comment,
value: RpVariantValue::from(&value.value),
})
}
__o
},
)*
}
};
}
let variants: Vec<_> = variants!(self, String, Number);
RpVariantsIter {
iter: variants.into_iter(),
}
}
}
impl<F: 'static, T> Translate<T> for RpVariants<F>
where
F: Flavor,
T: Translator<Source = F>,
{
type Source = F;
type Out = RpVariants<T::Target>;
fn translate(self, diag: &mut Diagnostics, translator: &T) -> Result<RpVariants<T::Target>> {
use self::RpVariants::*;
let out = match self {
String { variants } => String {
variants: variants.translate(diag, translator)?,
},
Number { variants } => Number {
variants: variants.translate(diag, translator)?,
},
};
Ok(out)
}
}
impl<'a, F: 'static> IntoIterator for &'a RpVariants<F>
where
F: Flavor,
{
type Item = RpVariantRef<'a, F>;
type IntoIter = RpVariantsIter<'a, F>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}