11//! Build script for ASN.1 schema compilation and code generation.
2+ //!
3+ //! # Why Custom ASN.1 Parser Instead of rasn-compiler?
4+ //!
5+ //! This build script implements a custom ASN.1 parser and code generator rather than using
6+ //! the official `rasn-compiler` crate. This architectural decision was made due to several
7+ //! compatibility and maintenance considerations:
8+ //!
9+ //! ## Version Compatibility Issues
10+ //!
11+ //! The primary reason for the custom implementation is version incompatibility between
12+ //! `rasn-compiler` and `rasn` 0.18.x:
13+ //!
14+ //! - **rasn-compiler dependency conflicts**: The rasn-compiler crate may depend on different
15+ //! versions of rasn than the 0.18.x version used in this project, causing dependency
16+ //! resolution conflicts during build.
17+ //!
18+ //! - **API surface changes**: Between rasn versions, there have been breaking changes in
19+ //! the generated code APIs, attribute syntax, and trait implementations that make
20+ //! rasn-compiler-generated code incompatible with rasn 0.18.x.
21+ //!
22+ //! - **Build-time constraints**: Using rasn-compiler would require careful version pinning
23+ //! and potentially upgrading rasn itself, which could introduce breaking changes throughout
24+ //! the RustyFix codebase.
25+ //!
26+ //! ## Benefits of Custom Implementation
27+ //!
28+ //! The custom ASN.1 parser implementation provides several advantages:
29+ //!
30+ //! - **Precise control**: Generate code that exactly matches the needs of the FIX protocol
31+ //! encoding requirements and integrates seamlessly with RustyFix's type system.
32+ //!
33+ //! - **Stability**: Immune to breaking changes in rasn-compiler updates, ensuring consistent
34+ //! builds across different environments and over time.
35+ //!
36+ //! - **FIX-specific optimizations**: Tailored for FIX protocol message structures, field
37+ //! types, and encoding patterns rather than generic ASN.1 use cases.
38+ //!
39+ //! - **Reduced dependencies**: Eliminates the need for rasn-compiler and its transitive
40+ //! dependencies, reducing build complexity and potential security surface.
41+ //!
42+ //! - **Incremental implementation**: Can be extended progressively to support additional
43+ //! ASN.1 features as needed by the FIX protocol without waiting for upstream changes.
44+ //!
45+ //! ## Migration Path
46+ //!
47+ //! Future migration to rasn-compiler should be considered when:
48+ //!
49+ //! - rasn-compiler achieves stable compatibility with rasn 0.18.x or later
50+ //! - The RustyFix project upgrades to a newer rasn version that's compatible with
51+ //! the latest rasn-compiler
52+ //! - The maintenance burden of the custom parser becomes significant
53+ //!
54+ //! ## Implementation Details
55+ //!
56+ //! The custom parser handles:
57+ //! - Basic ASN.1 constructs (SEQUENCE, CHOICE, ENUMERATED, INTEGER, STRING types)
58+ //! - FIX-specific message type generation from dictionary metadata
59+ //! - Field tag enumerations and value type mappings
60+ //! - Integration with rasn's derive macros for encoding/decoding
61+ //!
62+ //! For complex ASN.1 schemas that require advanced features not implemented in the
63+ //! custom parser, the build script falls back to copying the schema files directly
64+ //! and emitting warnings about unsupported constructs.
265
366use anyhow:: { Context , Result } ;
467use heck:: ToPascalCase ;
@@ -41,6 +104,8 @@ fn main() -> Result<()> {
41104 println ! ( "cargo:warning=Detected FIX features: {enabled_features:?}" ) ;
42105
43106 // Generate ASN.1 definitions from FIX dictionaries
107+ // This creates type-safe ASN.1 representations of FIX message structures
108+ // without requiring rasn-compiler, ensuring compatibility with rasn 0.18.x
44109 generate_fix_asn1_definitions ( & enabled_features)
45110 . context ( "Failed to generate FIX ASN.1 definitions" ) ?;
46111
@@ -98,7 +163,11 @@ fn probe_available_dictionaries() -> Vec<String> {
98163 let env_vars: Vec < _ > = env:: vars ( )
99164 . filter_map ( |( key, _) | {
100165 if key. starts_with ( "CARGO_FEATURE_FIX" ) {
101- let feature_name = key. strip_prefix ( "CARGO_FEATURE_" ) . unwrap ( ) . to_lowercase ( ) ;
166+ #[ allow( clippy:: expect_used) ]
167+ let feature_name = key
168+ . strip_prefix ( "CARGO_FEATURE_" )
169+ . expect ( "Environment variable must start with CARGO_FEATURE_ prefix" )
170+ . to_lowercase ( ) ;
102171 Some ( feature_name)
103172 } else {
104173 None
@@ -673,13 +742,21 @@ END
673742 ) ;
674743 }
675744
676- // Process any .asn1 files in the schemas directory using proper ASN.1 compilation
745+ // Process any .asn1 files in the schemas directory using our custom ASN.1 parser
746+ // Note: This uses a custom parser instead of rasn-compiler due to version compatibility issues
677747 compile_asn1_schemas ( & schemas_dir) . context ( "Failed to compile ASN.1 schemas" ) ?;
678748
679749 Ok ( ( ) )
680750}
681751
682- /// Compiles ASN.1 schema files using rasn-compiler for proper code generation.
752+ /// Compiles ASN.1 schema files using a custom ASN.1 parser implementation.
753+ ///
754+ /// **Note**: This function uses a custom ASN.1 parser instead of rasn-compiler due to
755+ /// version incompatibility issues between rasn-compiler and rasn 0.18.x. The custom
756+ /// implementation provides better control over the generated code and avoids dependency
757+ /// conflicts while maintaining compatibility with the rasn framework.
758+ ///
759+ /// See the module-level documentation for detailed reasoning behind this architectural choice.
683760fn compile_asn1_schemas ( schemas_dir : & Path ) -> Result < ( ) > {
684761 let schema_pattern = schemas_dir. join ( "*.asn1" ) ;
685762
@@ -707,7 +784,9 @@ fn compile_asn1_schemas(schemas_dir: &Path) -> Result<()> {
707784 let output_file = format ! ( "{file_stem}_asn1.rs" ) ;
708785 let output_path = out_path. join ( & output_file) ;
709786
710- // Attempt to compile the ASN.1 schema
787+ // Attempt to compile the ASN.1 schema using our custom parser
788+ // This avoids rasn-compiler version compatibility issues while providing
789+ // targeted support for FIX protocol ASN.1 extensions
711790 match compile_asn1_file ( & schema_file, & output_path) {
712791 Ok ( _) => {
713792 println ! (
@@ -717,12 +796,16 @@ fn compile_asn1_schemas(schemas_dir: &Path) -> Result<()> {
717796 ) ;
718797 }
719798 Err ( e) => {
720- // If compilation fails, fall back to copying the file and warn
799+ // If our custom parser fails, fall back to copying the file and warn
800+ // This provides a graceful degradation path for complex schemas
721801 println ! (
722- "cargo:warning=ASN.1 compilation failed for {}: {}. Copying file instead." ,
802+ "cargo:warning=Custom ASN.1 parser failed for {}: {}. Copying file instead." ,
723803 schema_file. display( ) ,
724804 e
725805 ) ;
806+ println ! (
807+ "cargo:warning=Consider simplifying the schema or extending the custom parser to support this construct."
808+ ) ;
726809 let filename = schema_file. file_name ( ) . with_context ( || {
727810 format ! (
728811 "Schema file should have a valid filename: {}" ,
@@ -745,8 +828,27 @@ fn compile_asn1_schemas(schemas_dir: &Path) -> Result<()> {
745828 Ok ( ( ) )
746829}
747830
748- /// Compiles a single ASN.1 schema file to Rust code.
749- /// Implements a basic ASN.1 parser that can handle common structures.
831+ /// Compiles a single ASN.1 schema file to Rust code using a custom ASN.1 parser.
832+ ///
833+ /// This function implements a custom ASN.1 parser that handles the subset of ASN.1
834+ /// constructs commonly used in FIX protocol extensions. The parser is designed to
835+ /// generate code compatible with rasn 0.18.x while avoiding the version compatibility
836+ /// issues that would arise from using rasn-compiler.
837+ ///
838+ /// **Supported ASN.1 Constructs:**
839+ /// - SEQUENCE types with optional fields and explicit tags
840+ /// - ENUMERATED types with explicit discriminant values
841+ /// - CHOICE types with context-specific tags
842+ /// - INTEGER types with constraint annotations
843+ /// - String types (UTF8String, PrintableString, VisibleString, etc.)
844+ ///
845+ /// **Limitations:**
846+ /// - Does not support complex constraints or extensibility markers
847+ /// - Limited support for advanced ASN.1 features like Information Object Classes
848+ /// - No support for parameterized types or macros
849+ ///
850+ /// For schemas requiring unsupported features, the function will return an error
851+ /// and the caller can fall back to copying the schema file directly.
750852fn compile_asn1_file ( schema_file : & Path , output_path : & Path ) -> Result < ( ) > {
751853 // Read the ASN.1 schema file
752854 let schema_content = fs:: read_to_string ( schema_file)
@@ -787,10 +889,12 @@ enum Asn1Type {
787889 } ,
788890 Integer {
789891 name : String ,
892+ #[ allow( dead_code) ]
790893 constraints : Option < String > ,
791894 } ,
792895 String {
793896 name : String ,
897+ #[ allow( dead_code) ]
794898 string_type : Asn1StringType ,
795899 } ,
796900}
@@ -811,19 +915,27 @@ struct Asn1EnumValue {
811915
812916#[ derive( Debug , Clone ) ]
813917enum Asn1StringType {
814- Utf8String ,
815- PrintableString ,
816- VisibleString ,
817- GeneralString ,
918+ Utf8 ,
919+ Printable ,
920+ Visible ,
921+ General ,
818922}
819923
820924#[ derive( Debug ) ]
821925struct Asn1Schema {
926+ #[ allow( dead_code) ]
822927 module_name : String ,
823928 types : Vec < Asn1Type > ,
824929}
825930
826- /// Basic ASN.1 schema parser
931+ /// Basic ASN.1 schema parser implementation.
932+ ///
933+ /// This parser handles a subset of ASN.1 sufficient for FIX protocol message
934+ /// extensions and common ASN.1 patterns. It's designed to be simple, reliable,
935+ /// and compatible with rasn 0.18.x generated code patterns.
936+ ///
937+ /// The parser uses a simple line-by-line approach with basic pattern matching
938+ /// rather than a full grammar parser, making it easier to maintain and debug.
827939fn parse_asn1_schema ( content : & str ) -> Result < Asn1Schema > {
828940 let mut types = Vec :: new ( ) ;
829941 let mut module_name = "UnknownModule" . to_string ( ) ;
@@ -1023,10 +1135,10 @@ fn parse_integer_type(name: String, type_def: &str) -> Result<Asn1Type> {
10231135/// Parse string type
10241136fn parse_string_type ( name : String , type_def : & str ) -> Result < Asn1Type > {
10251137 let string_type = match type_def {
1026- "UTF8String" => Asn1StringType :: Utf8String ,
1027- "PrintableString" => Asn1StringType :: PrintableString ,
1028- "VisibleString" => Asn1StringType :: VisibleString ,
1029- _ => Asn1StringType :: GeneralString ,
1138+ "UTF8String" => Asn1StringType :: Utf8 ,
1139+ "PrintableString" => Asn1StringType :: Printable ,
1140+ "VisibleString" => Asn1StringType :: Visible ,
1141+ _ => Asn1StringType :: General ,
10301142 } ;
10311143
10321144 Ok ( Asn1Type :: String { name, string_type } )
@@ -1190,3 +1302,39 @@ fn map_asn1_type_to_rust(asn1_type: &str) -> String {
11901302 _ => asn1_type. to_string ( ) , // Custom type, use as-is
11911303 }
11921304}
1305+
1306+ //
1307+ // FUTURE IMPROVEMENTS AND MIGRATION CONSIDERATIONS
1308+ //
1309+ // This custom ASN.1 parser implementation can be extended in the following ways:
1310+ //
1311+ // 1. **Enhanced ASN.1 Support**: Add support for advanced constructs like:
1312+ // - Information Object Classes (IOC)
1313+ // - Parameterized types and type parameters
1314+ // - Extensibility markers (...) and version brackets
1315+ // - Complex constraints (SIZE, range, character set)
1316+ // - Nested modules and imports
1317+ //
1318+ // 2. **Migration to rasn-compiler**: Consider migrating when:
1319+ // - rasn-compiler stabilizes compatibility with rasn 0.18.x+
1320+ // - The RustyFix project upgrades to a newer rasn version
1321+ // - The maintenance burden of custom parser becomes significant
1322+ //
1323+ // 3. **Performance Optimizations**:
1324+ // - Implement parallel parsing for multiple schema files
1325+ // - Cache parsed ASN.1 modules to avoid re-parsing
1326+ // - Optimize generated code for specific FIX protocol patterns
1327+ //
1328+ // 4. **Better Error Handling**:
1329+ // - Provide line number information in parser errors
1330+ // - Add syntax highlighting for error messages
1331+ // - Implement recovery mechanisms for malformed schemas
1332+ //
1333+ // 5. **Validation and Testing**:
1334+ // - Add comprehensive test suite for ASN.1 parser
1335+ // - Implement roundtrip testing (parse -> generate -> parse)
1336+ // - Add fuzzing support for parser robustness
1337+ //
1338+ // The current implementation prioritizes compatibility and stability over feature completeness.
1339+ // It successfully handles the ASN.1 constructs commonly used in FIX protocol extensions
1340+ // while maintaining seamless integration with rasn 0.18.x and the RustyFix ecosystem.
0 commit comments