diff --git a/ksc-java/src/lib.rs b/ksc-java/src/lib.rs index f90aa93..79e4414 100644 --- a/ksc-java/src/lib.rs +++ b/ksc-java/src/lib.rs @@ -6,8 +6,8 @@ use heck::{ToLowerCamelCase, ToShoutySnakeCase, ToUpperCamelCase}; use indexmap::IndexMap; use ksc::model::expressions::{OwningAttr, OwningEnumRef, OwningNode}; use ksc::model::{ - Attribute, AttributeName, Chunk, EnumName, EnumVariantName, Enumerable, FieldName, OptionalName, - Repeat, Root, SeqName, Terminator, TypeName, TypeRef, UserType, Variant, + Attribute, AttributeName, Chunk, Enum, EnumName, EnumVariantName, Enumerable, FieldName, + OptionalName, Repeat, Root, SeqName, Terminator, TypeName, TypeRef, UserType, Variant, }; use ksc::parser::expressions::ContextVar; use num_traits::cast::ToPrimitive; @@ -170,6 +170,8 @@ impl<'a> TypeGenerator<'a> { let classes = ty.types.iter().map(|(n, t)| TypeGenerator::new(t).translate(n, t, true)); + let enums = ty.enums.iter().map(|(n, e)| self.translate_enum(n, e)); + quote! { #header public #static_ class #name implements PositionInfo { @@ -177,6 +179,8 @@ impl<'a> TypeGenerator<'a> { public final Map _spans = new HashMap<>(); + #(#enums)* + #(#classes)* @Override @@ -415,6 +419,53 @@ impl<'a> TypeGenerator<'a> { } } + fn translate_enum(&self, name: &EnumName, enum_: &Enum) -> TokenStream { + let java_name = self.translate_enum_name(name); + let elements = enum_.iter().map(|(id, variant)| { + let variant = self.translate_enum_value_name(&variant.name); + // SAFETY: Parsing are always successful because we generate a correct token + let id = Literal::from_str(&format!("{id}L")).unwrap(); + + quote!(#variant(#id)) + }); + quote! { + @Generated(id = "#name") + public interface #java_name extends KaitaiEnum { + public enum Known implements #java_name { + #(#elements),*; + + private final long value; + private Known(long value) { this.value = value; } + @Override + public long value() { return value; } + } + public static final class Unknown implements #java_name { + private final long value; + private Unknown(long value) { this.value = value; } + + @Override + public int ordinal() { return -1; } + @Override + public String name() { return null; } + @Override + public long value() { return value; } + @Override + public String toString() { return "#java_name("+value+")"; } + + private static final HashMap unknown = new HashMap<>(); + public static #java_name resolve(final long value) { + for (final Known e : Known.values()) { + if (e.value() == value) { + return e; + } + } + return unknown.computeIfAbsent(value, Unknown::new); + } + } + } + } + } + /// Converts kaitai's type name to Java name fn translate_type_name(&self, name: &TypeName) -> Ident { Ident::new(&name.to_upper_camel_case(), Span::call_site())