rasn-compiler 0.16.0

An ASN.1 compiler producing bindings for the rasn framework
Documentation
use std::{cell::RefCell, io::Write, rc::Rc};

use nom::FindSubstring;

use crate::validator::Validator;

/// This function generates a stepwise-end-to-end test for a given ASN.1 module,
/// i.e. it generates test functions with corresponding inputs and expected outputs for
/// the `lexer`'s, `validator`'s, and `generator`'s `tests` modules.
/// The test generator is merely a helper to make writing unit tests less verbose,
/// and the generated tests and in particular the expected result of the test must
/// be diligently checked by the developer.
#[test]
#[ignore]
fn generate_stepwise_e2e_test() {
    // -----------------------------------------------------------------
    //                           CONFIGURATION
    // -----------------------------------------------------------------

    let generate_lexer_test = true;
    let generate_validator_test = true;
    let generate_system_test = true;
    let test_name = "stepwise_test";
    let input = r#"World-Schema DEFINITIONS AUTOMATIC TAGS ::= 
        BEGIN
          Rocket ::= SEQUENCE       
          {
             range     INTEGER, -- huge (see a special directive above)
             name      UTF8String (SIZE(1..16)),
             message   UTF8String DEFAULT "Hello World" , 
             fuel      ENUMERATED {solid, liquid, gas},
             speed     CHOICE     
             { 
                mph    INTEGER,  
                kmph   INTEGER  
             }  OPTIONAL, 
             payload   SEQUENCE OF UTF8String 
          }                                                     
        END
        "#;

    if test_name.is_empty() {
        panic!("Test name must not be an empty string!");
    }

    // -----------------------------------------------------------------
    //                           LEXER TEST
    // -----------------------------------------------------------------

    if generate_lexer_test {
        let mut lexer_tests = std::fs::OpenOptions::new()
            .append(true)
            .open("./src/lexer/tests/mod.rs")
            .unwrap();

        let expected = expected_lexer_result(input);

        lexer_tests
            .write_fmt(format_args!(
                r##"
        
        /// This test was auto-generated by `rasn_compiler::tests::generate_stepwise_e2e_test`
        #[test]
        fn {test_name}() {{
            assert_eq!(
                asn_spec(r#"{input}"#).unwrap(),
                {expected}
            )

            !!! PLEASE REVIEW THIS TEST BEFORE DELETING THIS LINE !!!
        }}
        "##
            ))
            .unwrap();
    }

    // -----------------------------------------------------------------
    //                           VALIDATOR TEST
    // -----------------------------------------------------------------

    if generate_validator_test {
        let mut validator_tests = std::fs::OpenOptions::new()
            .append(true)
            .open("./src/validator/tests/mod.rs")
            .unwrap();

        let (input, expected_output) = validator_io(input);

        validator_tests
            .write_fmt(format_args!(
                r##"
        
        /// This test was auto-generated by `rasn_compiler::tests::generate_stepwise_e2e_test`
        #[test]
        fn {test_name}() {{
            assert_eq!(
                Validator::new({input}).validate().unwrap().0,
                {expected_output}
            )

            !!! PLEASE REVIEW THIS TEST BEFORE DELETING THIS LINE !!!
        }}
        "##
            ))
            .unwrap()
    }

    // -----------------------------------------------------------------
    //                           SYSTEM TEST
    // -----------------------------------------------------------------

    if generate_system_test {
        let mut system_tests = std::fs::OpenOptions::new()
            .append(true)
            .open("../rasn-compiler-tests/tests/system_tests.rs")
            .unwrap();

        let (input, expected_output) = system_io(input);

        system_tests
            .write_fmt(format_args!(
                r##"
        
        /// This test was auto-generated by `rasn_compiler::tests::generate_stepwise_e2e_test`
        e2e_pdu! {{
            {test_name},
            r#"{input}"#,
            r#"{expected_output}"#
            !!! PLEASE REVIEW THIS TEST BEFORE DELETING THIS LINE !!!
        }}
        "##
            ))
            .unwrap()
    }
}

fn as_decl_string<I: std::fmt::Debug>(input: I) -> String {
    format!("{input:?}")
        .replace('[', "vec![")
        .replace("\")", "\".into())")
        .replace("\",", "\".into(),")
}

fn expected_lexer_result(literal: &str) -> String {
    as_decl_string(crate::lexer::asn_spec(literal.into()).unwrap())
}

fn validator_io(literal: &str) -> (String, String) {
    let input = crate::lexer::asn_spec(literal.into())
        .unwrap()
        .into_iter()
        .flat_map(|(header, tlds)| {
            let header_ref = Rc::new(RefCell::new(header));
            tlds.into_iter().map(move |mut tld| {
                tld.apply_tagging_environment(&header_ref.borrow().tagging_environment);
                tld.set_module_header(header_ref.clone());
                tld
            })
        })
        .collect::<Vec<_>>();
    let (expected_output, warnings) = Validator::new(input.clone()).validate().unwrap();
    assert!(warnings.is_empty());
    (as_decl_string(input), as_decl_string(expected_output))
}

fn system_io(literal: &str) -> (String, String) {
    let input = literal
        .find_substring("BEGIN")
        .zip(literal.find_substring("END"))
        .map_or(String::from(literal), |(s, e)| {
            String::from(&literal[(s + 5)..e])
        });
    let mut output = String::from(
        crate::Compiler::<crate::prelude::RasnBackend, _>::new()
            .add_asn_literal(format!(
                "TestModule DEFINITIONS AUTOMATIC TAGS::= BEGIN {input} END"
            ))
            .compile_to_string()
            .unwrap()
            .generated
            .split_once("prelude::*;")
            .unwrap()
            .1
            .trim_end(),
    );
    output.pop();
    (input, output)
}