use zerodds_idl::ast::types::{Annotation, AnnotationParams, ConstExpr, LiteralKind};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructExtensibility {
Final,
Appendable,
Mutable,
}
fn annotation_name(a: &Annotation) -> &str {
a.name.parts.last().map(|p| p.text.as_str()).unwrap_or("")
}
#[must_use]
pub fn struct_extensibility(annotations: &[Annotation]) -> StructExtensibility {
for a in annotations {
match annotation_name(a) {
"final" => return StructExtensibility::Final,
"appendable" => return StructExtensibility::Appendable,
"mutable" => return StructExtensibility::Mutable,
"extensibility" => {
if let Some(value) = annotation_first_param_text(a) {
return match value.as_str() {
"FINAL" | "Final" | "final" => StructExtensibility::Final,
"APPENDABLE" | "Appendable" | "appendable" => {
StructExtensibility::Appendable
}
"MUTABLE" | "Mutable" | "mutable" => StructExtensibility::Mutable,
_ => StructExtensibility::Final,
};
}
}
_ => {}
}
}
StructExtensibility::Final
}
#[must_use]
pub fn member_id(annotations: &[Annotation]) -> Option<u32> {
annotations
.iter()
.find(|a| annotation_name(a) == "id")
.and_then(annotation_first_param_integer)
.and_then(|v| u32::try_from(v).ok())
}
#[must_use]
pub fn member_must_understand(annotations: &[Annotation]) -> bool {
annotations
.iter()
.any(|a| annotation_name(a) == "must_understand")
}
#[must_use]
pub fn member_is_key(annotations: &[Annotation]) -> bool {
annotations.iter().any(|a| annotation_name(a) == "key")
}
#[must_use]
pub fn member_is_optional(annotations: &[Annotation]) -> bool {
annotations.iter().any(|a| annotation_name(a) == "optional")
}
#[must_use]
pub fn struct_is_nested(annotations: &[Annotation]) -> bool {
annotations.iter().any(|a| annotation_name(a) == "nested")
}
fn annotation_first_param_text(a: &Annotation) -> Option<String> {
let value = single_param(&a.params)?;
match value {
ConstExpr::Literal(lit) if lit.kind == LiteralKind::String => {
Some(lit.raw.trim_matches('"').to_string())
}
ConstExpr::Scoped(scoped) => scoped.parts.last().map(|p| p.text.clone()),
_ => None,
}
}
fn annotation_first_param_integer(a: &Annotation) -> Option<u64> {
let value = single_param(&a.params)?;
match value {
ConstExpr::Literal(lit) if lit.kind == LiteralKind::Integer => {
crate::type_map::const_expr_as_usize(value).map(|v| v as u64)
}
_ => None,
}
}
fn single_param(params: &AnnotationParams) -> Option<&ConstExpr> {
match params {
AnnotationParams::Single(expr) => Some(expr),
AnnotationParams::Named(named) => named.first().map(|np| &np.value),
AnnotationParams::None | AnnotationParams::Empty => None,
}
}