1pub mod ast;
9pub mod build;
10mod expr;
11pub mod items;
12pub mod lower;
13pub mod style;
14mod ui;
15
16pub use ast::*;
17pub use items::{
18 CommandItem, CommandParam, CommandRef, CommandsItem, ComponentItem, EventItem, EventsItem,
19 FeatureItem, FeaturesItem, FieldItem, Param, ParamKind, PlayerField, PlayersItem, ProtocolArgs,
20 ProtocolItem, QueriesItem, QueryItem, SessionItem, SourceItems, StateItem, ValueItem,
21 ValueKind, VariantItem, doc_comment, is_bern_attr, parse_component, parse_component_args,
22 parse_component_with, parse_features, parse_players, parse_protocol, parse_protocol_args,
23 parse_session, parse_source_file, parse_value, parse_value_args, type_ref,
24};
25pub use style::{
26 CornerBlock, MotionDecl, StyleArg, StyleItem, StyleProp, StyleRuleItem, StyleValue, parse_style,
27};
28
29pub fn parse_ui(tokens: proc_macro2::TokenStream) -> syn::Result<UiBody> {
31 syn::parse2(tokens)
32}
33
34pub fn parse_tr(tokens: proc_macro2::TokenStream) -> syn::Result<Tr> {
36 syn::parse2::<expr::TrBody>(tokens).map(|body| body.0)
37}
38
39pub(crate) fn finite(value: f64, span: proc_macro2::Span) -> syn::Result<f64> {
42 if value.is_finite() {
43 Ok(value)
44 } else {
45 Err(syn::Error::new(
46 span,
47 "number literal is out of range for f64",
48 ))
49 }
50}
51
52pub fn valid_id(name: &str) -> bool {
54 bern_protocol::valid_name(name)
55}
56
57pub fn mentions(ty: &TypeRef, name: &str) -> bool {
59 match ty {
60 TypeRef::Named(path) => path.last().is_some_and(|last| last == name),
61 TypeRef::Optional(inner) | TypeRef::List(inner) => mentions(inner, name),
62 _ => false,
63 }
64}
65
66pub fn named(ty: &TypeRef, visit: &mut impl FnMut(&str)) {
68 match ty {
69 TypeRef::Named(path) => {
70 if let Some(last) = path.last() {
71 visit(last);
72 }
73 }
74 TypeRef::Optional(inner) | TypeRef::List(inner) => named(inner, visit),
75 _ => {}
76 }
77}
78
79pub fn pascal_case(name: &str) -> String {
81 name.split('_')
82 .filter(|part| !part.is_empty())
83 .map(|part| {
84 let mut chars = part.chars();
85 match chars.next() {
86 Some(first) => first.to_uppercase().chain(chars).collect::<String>(),
87 None => String::new(),
88 }
89 })
90 .collect()
91}
92
93pub fn camel_case(name: &str) -> String {
95 let pascal = pascal_case(name);
96 let mut chars = pascal.chars();
97 match chars.next() {
98 Some(first) => first.to_lowercase().chain(chars).collect(),
99 None => String::new(),
100 }
101}
102
103#[cfg(test)]
104mod tests;