pub mod ast;
pub mod build;
mod expr;
pub mod items;
pub mod lower;
pub mod style;
mod ui;
pub use ast::*;
pub use items::{
CommandItem, CommandParam, CommandRef, CommandsItem, ComponentItem, EventItem, EventsItem,
FeatureItem, FeaturesItem, FieldItem, Param, ParamKind, PlayerField, PlayersItem, ProtocolArgs,
ProtocolItem, QueriesItem, QueryItem, SessionItem, SourceItems, StateItem, ValueItem,
ValueKind, VariantItem, doc_comment, is_bern_attr, parse_component, parse_component_args,
parse_component_with, parse_features, parse_players, parse_protocol, parse_protocol_args,
parse_session, parse_source_file, parse_value, parse_value_args, type_ref,
};
pub use style::{
CornerBlock, MotionDecl, StyleArg, StyleItem, StyleProp, StyleRuleItem, StyleValue, parse_style,
};
pub fn parse_ui(tokens: proc_macro2::TokenStream) -> syn::Result<UiBody> {
syn::parse2(tokens)
}
pub fn parse_tr(tokens: proc_macro2::TokenStream) -> syn::Result<Tr> {
syn::parse2::<expr::TrBody>(tokens).map(|body| body.0)
}
pub(crate) fn finite(value: f64, span: proc_macro2::Span) -> syn::Result<f64> {
if value.is_finite() {
Ok(value)
} else {
Err(syn::Error::new(
span,
"number literal is out of range for f64",
))
}
}
pub fn valid_id(name: &str) -> bool {
bern_protocol::valid_name(name)
}
pub fn mentions(ty: &TypeRef, name: &str) -> bool {
match ty {
TypeRef::Named(path) => path.last().is_some_and(|last| last == name),
TypeRef::Optional(inner) | TypeRef::List(inner) => mentions(inner, name),
_ => false,
}
}
pub fn named(ty: &TypeRef, visit: &mut impl FnMut(&str)) {
match ty {
TypeRef::Named(path) => {
if let Some(last) = path.last() {
visit(last);
}
}
TypeRef::Optional(inner) | TypeRef::List(inner) => named(inner, visit),
_ => {}
}
}
pub fn pascal_case(name: &str) -> String {
name.split('_')
.filter(|part| !part.is_empty())
.map(|part| {
let mut chars = part.chars();
match chars.next() {
Some(first) => first.to_uppercase().chain(chars).collect::<String>(),
None => String::new(),
}
})
.collect()
}
pub fn camel_case(name: &str) -> String {
let pascal = pascal_case(name);
let mut chars = pascal.chars();
match chars.next() {
Some(first) => first.to_lowercase().chain(chars).collect(),
None => String::new(),
}
}
#[cfg(test)]
mod tests;