blob: 0c480620428277aeaf4f5aa1152b23d679893f5f [file] [log] [blame]
use proc_macro2::TokenStream;
use quote::ToTokens;
use serde::{Deserialize, Serialize};
use std::fmt;
use crate::context::{self, LocalContext};
use crate::typekinds::{BaseType, BaseTypeKind, TypeKind};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MatchSizeValues<T> {
pub default: T,
pub byte: Option<T>,
pub halfword: Option<T>,
pub doubleword: Option<T>,
}
impl<T> MatchSizeValues<T> {
pub fn get(&mut self, ty: &TypeKind, ctx: &LocalContext) -> context::Result<&T> {
let base_ty = if let Some(w) = ty.wildcard() {
ctx.provide_type_wildcard(w)?
} else {
ty.clone()
};
if let BaseType::Sized(_, bitsize) = base_ty.base_type().unwrap() {
match (bitsize, &self.byte, &self.halfword, &self.doubleword) {
(64, _, _, Some(v)) | (16, _, Some(v), _) | (8, Some(v), _, _) => Ok(v),
_ => Ok(&self.default),
}
} else {
Err(format!("cannot match bitsize to unsized type {ty:?}!"))
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct MatchKindValues<T> {
pub default: T,
pub float: Option<T>,
pub unsigned: Option<T>,
}
impl<T> MatchKindValues<T> {
pub fn get(&mut self, ty: &TypeKind, ctx: &LocalContext) -> context::Result<&T> {
let base_ty = if let Some(w) = ty.wildcard() {
ctx.provide_type_wildcard(w)?
} else {
ty.clone()
};
match (
base_ty.base_type().unwrap().kind(),
&self.float,
&self.unsigned,
) {
(BaseTypeKind::Float, Some(v), _) | (BaseTypeKind::UInt, _, Some(v)) => Ok(v),
_ => Ok(&self.default),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged, deny_unknown_fields)]
pub enum SizeMatchable<T> {
Matched(T),
Unmatched {
match_size: Option<TypeKind>,
#[serde(flatten)]
values: MatchSizeValues<Box<T>>,
},
}
impl<T: Clone> SizeMatchable<T> {
pub fn perform_match(&mut self, ctx: &LocalContext) -> context::Result {
match self {
Self::Unmatched {
match_size: None,
values: MatchSizeValues { default, .. },
} => *self = Self::Matched(*default.to_owned()),
Self::Unmatched {
match_size: Some(ty),
values,
} => *self = Self::Matched(*values.get(ty, ctx)?.to_owned()),
_ => {}
}
Ok(())
}
}
impl<T: fmt::Debug> AsRef<T> for SizeMatchable<T> {
fn as_ref(&self) -> &T {
if let SizeMatchable::Matched(v) = self {
v
} else {
panic!("no match for {self:?} was performed");
}
}
}
impl<T: fmt::Debug> AsMut<T> for SizeMatchable<T> {
fn as_mut(&mut self) -> &mut T {
if let SizeMatchable::Matched(v) = self {
v
} else {
panic!("no match for {self:?} was performed");
}
}
}
impl<T: fmt::Debug + ToTokens> ToTokens for SizeMatchable<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.as_ref().to_tokens(tokens)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged, deny_unknown_fields)]
pub enum KindMatchable<T> {
Matched(T),
Unmatched {
match_kind: Option<TypeKind>,
#[serde(flatten)]
values: MatchKindValues<Box<T>>,
},
}
impl<T: Clone> KindMatchable<T> {
pub fn perform_match(&mut self, ctx: &LocalContext) -> context::Result {
match self {
Self::Unmatched {
match_kind: None,
values: MatchKindValues { default, .. },
} => *self = Self::Matched(*default.to_owned()),
Self::Unmatched {
match_kind: Some(ty),
values,
} => *self = Self::Matched(*values.get(ty, ctx)?.to_owned()),
_ => {}
}
Ok(())
}
}
impl<T: fmt::Debug> AsRef<T> for KindMatchable<T> {
fn as_ref(&self) -> &T {
if let KindMatchable::Matched(v) = self {
v
} else {
panic!("no match for {self:?} was performed");
}
}
}
impl<T: fmt::Debug> AsMut<T> for KindMatchable<T> {
fn as_mut(&mut self) -> &mut T {
if let KindMatchable::Matched(v) = self {
v
} else {
panic!("no match for {self:?} was performed");
}
}
}
impl<T: fmt::Debug + ToTokens> ToTokens for KindMatchable<T> {
fn to_tokens(&self, tokens: &mut TokenStream) {
self.as_ref().to_tokens(tokens)
}
}