use crate::ast::types as ast;
use derive_more::{AsRef, From};
use std::{borrow::Cow, fmt::Display};
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, PartialEq)]
pub enum Node<'a> {
Namespace(Namespace<'a>),
Table(Table<'a>),
Struct(Struct<'a>),
Enum(Enum<'a>),
Union(Union<'a>),
Rpc(Rpc<'a>),
}
impl<'a> Node<'a> {
pub fn ident(&self) -> &QualifiedIdent<'a> {
match self {
Node::Namespace(ns) => &ns.ident,
Node::Table(t) => &t.ident,
Node::Struct(s) => &s.ident,
Node::Enum(e) => &e.ident,
Node::Union(u) => &u.ident,
Node::Rpc(r) => &r.ident,
}
}
pub fn namespace(&self) -> Option<QualifiedIdent<'a>> {
self.ident().namespace()
}
pub fn is_namespace(&self) -> bool {
self.as_namespace().is_some()
}
pub fn as_namespace(&self) -> Option<&Namespace<'a>> {
match self {
Node::Namespace(ns) => Some(ns),
_ => None,
}
}
pub fn as_namespace_mut(&mut self) -> Option<&mut Namespace<'a>> {
match self {
Node::Namespace(ns) => Some(ns),
_ => None,
}
}
pub fn into_namespace(self) -> Option<Namespace<'a>> {
match self {
Node::Namespace(ns) => Some(ns),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Root<'a> {
pub nodes: Vec<Node<'a>>,
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Namespace<'a> {
pub ident: QualifiedIdent<'a>,
#[builder(default)]
pub nodes: Vec<Node<'a>>,
}
impl Namespace<'_> {
pub fn depth(&self) -> usize {
self.nodes.len()
}
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Table<'a> {
pub ident: QualifiedIdent<'a>,
#[builder(default)]
pub fields: Vec<Field<'a>>,
#[builder(default)]
pub root_type: bool,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Struct<'a> {
pub ident: QualifiedIdent<'a>,
#[builder(default)]
pub fields: Vec<Field<'a>>,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Field<'a> {
pub ident: Ident<'a>,
pub ty: Type<'a>,
#[builder(default)]
pub default_value: Option<ast::DefaultValue<'a>>,
#[builder(default)]
pub metadata: FieldMetadata,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, TypedBuilder)]
pub struct FieldMetadata {
#[builder(default)]
pub required: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Type<'a> {
Bool,
Byte,
UByte,
Short,
UShort,
Int,
UInt,
Float,
Long,
ULong,
Double,
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
Float32,
Float64,
String,
Array(Box<Type<'a>>),
Custom(CustomTypeRef<'a>),
}
impl<'a> Type<'a> {
pub fn is_scalar(&self) -> bool {
match self {
Type::String | Type::Array(..) => false,
Type::Custom(CustomTypeRef { ty, .. }) => match ty {
CustomType::Table | CustomType::Union { .. } => false,
CustomType::Struct { .. } | CustomType::Enum { .. } => true,
},
_ => true,
}
}
pub fn is_union(&self) -> bool {
match self {
Type::Custom(CustomTypeRef { ty, .. }) => match ty {
CustomType::Union { .. } => true,
_ => false,
},
_ => false,
}
}
pub fn is_enum(&self) -> bool {
match self {
Type::Custom(CustomTypeRef { ty, .. }) => match ty {
CustomType::Enum { .. } => true,
_ => false,
},
_ => false,
}
}
pub fn is_array(&self) -> bool {
match self {
Type::Array(..) => true,
_ => false,
}
}
pub fn is_complex(&self) -> bool {
self.is_array()
}
pub fn make_union_enum_type_companion(&self) -> Option<&QualifiedIdent<'a>> {
match self {
Type::Custom(CustomTypeRef { ty, .. }) => match ty {
CustomType::Union { ref enum_ident, .. } => Some(enum_ident),
_ => None,
},
_ => None,
}
}
}
impl Display for Type<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Type::Bool => write!(f, "bool"),
Type::Byte => write!(f, "i8"),
Type::UByte => write!(f, "u8"),
Type::Short => write!(f, "i16"),
Type::UShort => write!(f, "u16"),
Type::Int => write!(f, "i32"),
Type::UInt => write!(f, "u32"),
Type::Float => write!(f, "f32"),
Type::Long => write!(f, "i64"),
Type::ULong => write!(f, "u64"),
Type::Double => write!(f, "f64"),
Type::Int8 => write!(f, "i8"),
Type::UInt8 => write!(f, "u8"),
Type::Int16 => write!(f, "i16"),
Type::UInt16 => write!(f, "u16"),
Type::Int32 => write!(f, "i32"),
Type::UInt32 => write!(f, "u32"),
Type::Int64 => write!(f, "i64"),
Type::UInt64 => write!(f, "u64"),
Type::Float32 => write!(f, "f32"),
Type::Float64 => write!(f, "f64"),
Type::String => write!(f, "&str"),
Type::Array(component) => write!(f, "[{}]", component),
Type::Custom(CustomTypeRef { ident, .. }) => write!(f, "{}", ident),
}
}
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct CustomTypeRef<'a> {
pub ident: QualifiedIdent<'a>,
pub ty: CustomType<'a>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum CustomType<'a> {
Table,
Struct {
fields: Vec<Field<'a>>,
},
Enum {
variants: Vec<EnumVariant<'a>>,
base_type: EnumBaseType,
},
Union {
enum_ident: QualifiedIdent<'a>,
variants: Vec<UnionVariant<'a>>,
},
}
impl<'a> CustomType<'a> {
pub fn is_scalar(&self) -> bool {
match self {
CustomType::Struct { .. } | CustomType::Enum { .. } => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Enum<'a> {
pub ident: QualifiedIdent<'a>,
pub variants: Vec<EnumVariant<'a>>,
pub base_type: EnumBaseType,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum EnumBaseType {
Byte,
UByte,
Short,
UShort,
Int,
UInt,
Long,
ULong,
Int8,
UInt8,
Int16,
UInt16,
Int32,
UInt32,
Int64,
UInt64,
}
impl<'a> std::convert::TryFrom<&ast::Type<'a>> for EnumBaseType {
type Error = ();
fn try_from(ty: &ast::Type<'a>) -> Result<Self, Self::Error> {
Ok(match ty {
ast::Type::Byte => EnumBaseType::Byte,
ast::Type::UByte => EnumBaseType::UByte,
ast::Type::Short => EnumBaseType::Short,
ast::Type::UShort => EnumBaseType::UShort,
ast::Type::Int => EnumBaseType::Int,
ast::Type::UInt => EnumBaseType::UInt,
ast::Type::Long => EnumBaseType::Long,
ast::Type::ULong => EnumBaseType::ULong,
ast::Type::Int8 => EnumBaseType::Int8,
ast::Type::UInt8 => EnumBaseType::UInt8,
ast::Type::Int16 => EnumBaseType::Int16,
ast::Type::UInt16 => EnumBaseType::UInt16,
ast::Type::Int32 => EnumBaseType::Int32,
ast::Type::UInt32 => EnumBaseType::UInt32,
ast::Type::Int64 => EnumBaseType::Int64,
ast::Type::UInt64 => EnumBaseType::UInt64,
_ => return Err(()),
})
}
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Union<'a> {
pub ident: QualifiedIdent<'a>,
pub enum_ident: QualifiedIdent<'a>,
pub variants: Vec<UnionVariant<'a>>,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct Rpc<'a> {
pub ident: QualifiedIdent<'a>,
pub methods: Vec<RpcMethod<'a>>,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Clone, PartialEq, TypedBuilder)]
pub struct RpcMethod<'a> {
pub ident: Ident<'a>,
pub snake_ident: Ident<'a>,
pub request_type: QualifiedIdent<'a>,
pub response_type: QualifiedIdent<'a>,
#[builder(default)]
pub metadata: RpcMethodMetadata,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, TypedBuilder)]
pub struct RpcMethodMetadata {
pub streaming: RpcStreaming,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum RpcStreaming {
None,
Client,
Server,
Bidi,
}
impl Default for RpcStreaming {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct RootType<'a> {
_phantom: &'a (),
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, From, TypedBuilder)]
pub struct EnumVariant<'a> {
pub ident: Ident<'a>,
#[builder(default)]
pub value: Option<ast::IntegerConstant>,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Clone, PartialEq, From, TypedBuilder)]
pub struct UnionVariant<'a> {
pub ty: Type<'a>,
pub ident: Ident<'a>,
#[builder(default)]
pub doc: ast::Comment<'a>,
}
#[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord, AsRef)]
pub struct Ident<'a> {
pub raw: Cow<'a, str>,
}
impl<'a> Ident<'a> {
pub fn fix_identifier(ident: Cow<'_, str>) -> Cow<'_, str> {
if let Some(substitute) = super::keywords::substitute_keyword(ident.as_ref()) {
Cow::Borrowed(substitute)
} else {
ident
}
}
fn keyword(keyword: &str) -> Ident<'_> {
Ident {
raw: Cow::Borrowed(keyword),
}
}
}
impl<'a> From<ast::Ident<'a>> for Ident<'a> {
fn from(ident: ast::Ident<'a>) -> Self {
Ident::from(Cow::Borrowed(ident.raw))
}
}
impl<'a> From<&ast::Ident<'a>> for Ident<'a> {
fn from(ident: &ast::Ident<'a>) -> Self {
Ident::from(Cow::Borrowed(ident.raw))
}
}
impl<'a> From<&'a str> for Ident<'a> {
fn from(s: &'a str) -> Self {
Ident::from(Cow::Borrowed(s))
}
}
impl<'a> From<String> for Ident<'a> {
fn from(s: String) -> Self {
Ident::from(Cow::Owned(s))
}
}
impl<'a> From<Cow<'a, str>> for Ident<'a> {
fn from(raw: Cow<'a, str>) -> Self {
let raw = Ident::fix_identifier(raw);
Self { raw }
}
}
impl<'a> Display for Ident<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.raw.as_ref())
}
}
#[derive(Default, Debug, Clone, PartialEq, Hash, Eq, PartialOrd, Ord, From)]
pub struct QualifiedIdent<'a> {
pub parts: Vec<Ident<'a>>,
}
#[allow(clippy::len_without_is_empty)]
impl<'a> QualifiedIdent<'a> {
pub fn len(&self) -> usize {
self.parts.len()
}
pub fn namespace(&self) -> Option<QualifiedIdent<'a>> {
if self.parts.len() > 1 {
Some(Self {
parts: self.parts[..self.parts.len() - 1].to_vec(),
})
} else {
None
}
}
pub fn relative(&self, namespace: Option<&QualifiedIdent<'a>>) -> QualifiedIdent<'a> {
if let Some(ns) = namespace {
let common_prefix_len = self
.parts
.iter()
.zip(ns.parts.iter())
.take_while(|(a, b)| a == b)
.count();
let parts = std::iter::repeat(Ident::keyword("super"))
.take(ns.len() - common_prefix_len)
.chain(self.parts[common_prefix_len..].to_vec())
.collect();
QualifiedIdent { parts }
} else {
self.clone()
}
}
pub fn simple(&self) -> &Ident<'a> {
&self.parts[self.parts.len() - 1]
}
#[cfg(test)]
pub fn parse_str(s: &'a str) -> Self {
QualifiedIdent {
parts: s
.split('.')
.map(|p| Ident {
raw: Cow::Borrowed(p),
})
.collect(),
}
}
}
impl<'a> From<&'a str> for QualifiedIdent<'a> {
fn from(s: &'a str) -> Self {
Self {
parts: vec![s.into()],
}
}
}
impl<'a> From<ast::QualifiedIdent<'a>> for QualifiedIdent<'a> {
fn from(ident: ast::QualifiedIdent<'a>) -> Self {
Self {
parts: ident.parts.into_iter().map(Into::into).collect(),
}
}
}
impl<'a> From<&ast::QualifiedIdent<'a>> for QualifiedIdent<'a> {
fn from(ident: &ast::QualifiedIdent<'a>) -> Self {
Self {
parts: ident.parts.iter().map(Into::into).collect(),
}
}
}
impl<'a> Display for QualifiedIdent<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use itertools::Itertools;
write!(f, "{}", self.parts.iter().join("."))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
pub fn test_relative_ident_from_same_namespace() {
let id = QualifiedIdent::parse_str("foo.bar.Baz");
let ns = id.namespace(); let expected = QualifiedIdent::parse_str("Baz");
let actual = id.relative(ns.as_ref());
assert_eq!(expected, actual);
}
#[test]
pub fn test_relative_ident_from_root_namespace() {
let id = QualifiedIdent::parse_str("foo.bar.Baz");
let ns = None; let expected = id.clone(); let actual = id.relative(ns.as_ref());
assert_eq!(expected, actual);
}
#[test]
pub fn test_relative_ident_from_parent_namespace() {
let id = QualifiedIdent::parse_str("foo.bar.Baz");
let ns = Some(QualifiedIdent::parse_str("foo"));
let expected = QualifiedIdent::parse_str("bar.Baz");
let actual = id.relative(ns.as_ref());
assert_eq!(expected, actual);
}
#[test]
pub fn test_relative_ident_from_child_namespace() {
let id = QualifiedIdent::parse_str("foo.bar.Baz");
let ns = Some(QualifiedIdent::parse_str("foo.bar.bim"));
let expected = QualifiedIdent::parse_str("super.Baz");
let actual = id.relative(ns.as_ref());
assert_eq!(expected, actual);
}
#[test]
pub fn test_relative_ident_from_sibling_namespace() {
let id = QualifiedIdent::parse_str("foo.bar.bam.Baz");
let ns = Some(QualifiedIdent::parse_str("foo.bar.bim"));
let expected = QualifiedIdent::parse_str("super.bam.Baz");
let actual = id.relative(ns.as_ref());
assert_eq!(expected, actual);
}
}