Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions rasn-compiler-tests/tests/edge_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,3 +126,56 @@ e2e_pdu!(
LDAPString ::= [UNIVERSAL 4] IMPLICIT UTF8String
"#
);

e2e_pdu!(
constrained_integer_newtype_default,
r#"
UInt8 ::= INTEGER (0..255)
MySeq ::= SEQUENCE {
value UInt8 DEFAULT 8
}
"#
);

e2e_pdu!(
sequence_of_newtype_default,
r#"
MyOctet ::= OCTET STRING
OctetList ::= SEQUENCE OF MyOctet
MySeq ::= SEQUENCE {
data OctetList DEFAULT { 'CAFE'H, 'BABE'H }
}
"#
);

#[test]
fn inline_constrained_integer_default() {
// INTEGER with inline constraint should NOT wrap in newtype
let generated = rasn_compiler::Compiler::<rasn_compiler::prelude::RasnBackend, _>::new()
.add_asn_literal(
r#"
TestModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN
TestSeq ::= SEQUENCE {
count INTEGER (0..255) DEFAULT 1,
bigCount INTEGER (0..4294967295) DEFAULT 100
}
END
"#,
)
.compile_to_string()
.unwrap()
.generated;

// Should NOT generate U8(1) or U32(100) - just bare literals
assert!(
!generated.contains("U8(1)") && !generated.contains("U32(100)"),
"Inline constrained integers should not be wrapped. Generated:\n{}",
generated
);
let normalized = generated.replace(' ', "");
assert!(
normalized.contains("fntest_seq_count_default()->u8") && normalized.contains("{1}"),
"Should generate bare integer literal. Generated:\n{}",
generated
);
}
224 changes: 224 additions & 0 deletions rasn-compiler-tests/tests/external_module_mapping.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
//! Tests for external module mapping feature.
//!
//! This feature allows mapping ASN.1 module names to external Rust crate paths,
//! so that IMPORTS from those modules generate `use crate_path::Type;` instead
//! of `use super::module_name::Type;`.

use std::collections::HashMap;

use rasn_compiler::prelude::*;

/// Test that external module mapping generates correct import statements.
#[test]
fn external_module_mapping_generates_correct_import() {
let mut config = RasnConfig::default();

// Map PKIX1Explicit88 to rasn_pkix crate
config.external_module_mappings.insert(
"PKIX1Explicit88".to_string(),
ExternalModuleMapping {
rust_path: "rasn_pkix".to_string(),
type_mappings: HashMap::new(),
},
);

let asn1 = r#"
TestModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN
IMPORTS Certificate FROM PKIX1Explicit88 { 1 2 3 };

MyCert ::= Certificate
END
"#;

let result = Compiler::<RasnBackend, _>::new_with_config(config)
.add_asn_literal(asn1)
.compile_to_string()
.unwrap();

// Verify the generated code uses external crate import (handle unformatted output)
let normalized = result.generated.replace(' ', "");
assert!(
normalized.contains("pubuserasn_pkix::{Certificate"),
"Expected external crate import, got:\n{}",
result.generated
);

// Verify it does NOT use sibling module import
assert!(
!normalized.contains("super::pkix1_explicit88"),
"Should not have sibling module import, got:\n{}",
result.generated
);
}

/// Test external module mapping with multiple types imported.
#[test]
fn external_module_mapping_multiple_types() {
let mut config = RasnConfig::default();

config.external_module_mappings.insert(
"PKIX1Explicit88".to_string(),
ExternalModuleMapping {
rust_path: "rasn_pkix".to_string(),
type_mappings: HashMap::new(),
},
);

let asn1 = r#"
TestModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN
IMPORTS Certificate, CertificateList, Time FROM PKIX1Explicit88 { 1 2 3 };

MyCert ::= Certificate
MyList ::= CertificateList
END
"#;

let result = Compiler::<RasnBackend, _>::new_with_config(config)
.add_asn_literal(asn1)
.compile_to_string()
.unwrap();

// Verify all types are imported from external crate (handle unformatted output)
let normalized = result.generated.replace(' ', "");
assert!(
normalized.contains("pubuserasn_pkix::{")
&& normalized.contains("Certificate")
&& normalized.contains("CertificateList")
&& normalized.contains("Time"),
"Expected all types from external crate, got:\n{}",
result.generated
);

// Verify no sibling imports for PKIX
assert!(
!normalized.contains("super::pkix1_explicit88"),
"Should not have sibling module import"
);
}

/// Test external module mapping with explicit type name mapping.
#[test]
fn external_module_mapping_with_type_mapping() {
let mut config = RasnConfig::default();

let mut type_mappings = HashMap::new();
// Map ASN.1 type name to different Rust type name
type_mappings.insert("MyAsn1Type".to_string(), "RustTypeName".to_string());

config.external_module_mappings.insert(
"ExternalModule".to_string(),
ExternalModuleMapping {
rust_path: "my_crate::submodule".to_string(),
type_mappings,
},
);

let asn1 = r#"
TestModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN
IMPORTS MyAsn1Type FROM ExternalModule { 1 2 3 };

MyType ::= MyAsn1Type
END
"#;

let result = Compiler::<RasnBackend, _>::new_with_config(config)
.add_asn_literal(asn1)
.compile_to_string()
.unwrap();

// Verify the mapped type name is used in the import (handle unformatted output)
let normalized = result.generated.replace(' ', "");
assert!(
normalized.contains("pubusemy_crate::submodule::{RustTypeName"),
"Expected mapped type name in import, got:\n{}",
result.generated
);
}

/// Test that unmapped modules still use default sibling import.
#[test]
fn unmapped_module_uses_sibling_import() {
let mut config = RasnConfig::default();

// Only map one module
config.external_module_mappings.insert(
"MappedModule".to_string(),
ExternalModuleMapping {
rust_path: "mapped_crate".to_string(),
type_mappings: HashMap::new(),
},
);

// Note: multiple IMPORTS statements need to be combined in ASN.1
let asn1 = r#"
TestModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN
IMPORTS
MappedType FROM MappedModule { 1 2 3 }
UnmappedType FROM UnmappedModule { 1 2 3 };

MyType1 ::= MappedType
MyType2 ::= UnmappedType
END
"#;

let result = Compiler::<RasnBackend, _>::new_with_config(config)
.add_asn_literal(asn1)
.compile_to_string()
.unwrap();

// Verify mapped module uses external import (handle unformatted output)
let normalized = result.generated.replace(' ', "");
assert!(
normalized.contains("pubusemapped_crate::{"),
"Expected external crate import for mapped module, got:\n{}",
result.generated
);

// Verify unmapped module uses sibling import
assert!(
normalized.contains("pubusesuper::unmapped_module::{"),
"Expected sibling import for unmapped module, got:\n{}",
result.generated
);
}

/// Test external module mapping with lowercase constant imports.
#[test]
fn external_module_mapping_with_constant() {
let mut config = RasnConfig::default();

config.external_module_mappings.insert(
"ConstModule".to_string(),
ExternalModuleMapping {
rust_path: "const_crate".to_string(),
type_mappings: HashMap::new(),
},
);

let asn1 = r#"
TestModule DEFINITIONS AUTOMATIC TAGS ::= BEGIN
IMPORTS myConstant, MyType FROM ConstModule { 1 2 3 };

MyLocalType ::= MyType
END
"#;

let result = Compiler::<RasnBackend, _>::new_with_config(config)
.add_asn_literal(asn1)
.compile_to_string()
.unwrap();

// Verify constant is imported with correct case (SCREAMING_SNAKE_CASE)
assert!(
result.generated.contains("MY_CONSTANT"),
"Expected constant with SCREAMING_SNAKE_CASE, got:\n{}",
result.generated
);

// Verify type is also imported
assert!(
result.generated.contains("MyType"),
"Expected type to be imported, got:\n{}",
result.generated
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
source: rasn-compiler-tests/tests/edge_cases.rs
description: "\n UInt8 ::= INTEGER (0..255)\n MySeq ::= SEQUENCE {\n value UInt8 DEFAULT 8\n }\n "
---
Generated:
# [allow (non_camel_case_types , non_snake_case , non_upper_case_globals , unused , clippy :: too_many_arguments ,)] pub mod test_module { extern crate alloc ; use core :: borrow :: Borrow ; use std :: sync :: LazyLock ; use rasn :: prelude :: * ; # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (automatic_tags)] pub struct MySeq { # [rasn (default = "my_seq_value_default")] pub value : UInt8 , } impl MySeq { pub fn new (value : UInt8) -> Self { Self { value } } } impl std :: default :: Default for MySeq { fn default () -> Self { Self { value : my_seq_value_default () } } } fn my_seq_value_default () -> UInt8 { UInt8 (8) } # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (delegate , value ("0..=255"))] pub struct UInt8 (pub u8) ; }
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,4 @@ source: rasn-compiler-tests/tests/edge_cases.rs
description: " Restricted ::= Distinguished (second|fourth..sixth|eighth)\n Distinguished ::= INTEGER {\n first(1),\n second(2),\n third(3),\n fourth(4),\n fifth(5),\n sixth(6),\n seventh(7),\n eighth(8),\n ninth(9),\n tenth(10),\n } (1..10)"
---
Generated:
#[allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused,
clippy::too_many_arguments
)]
pub mod test_module {
extern crate alloc;
use core::borrow::Borrow;
use rasn::prelude::*;
use std::sync::LazyLock;
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, value("1..=10"))]
pub struct Distinguished(pub u8);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, value("2..=8"))]
pub struct Restricted(pub Distinguished);
}
# [allow (non_camel_case_types , non_snake_case , non_upper_case_globals , unused , clippy :: too_many_arguments ,)] pub mod test_module { extern crate alloc ; use core :: borrow :: Borrow ; use std :: sync :: LazyLock ; use rasn :: prelude :: * ; # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (delegate , value ("1..=10"))] pub struct Distinguished (pub u8) ; impl Distinguished { pub const FIRST : Self = Self (1) ; pub const SECOND : Self = Self (2) ; pub const THIRD : Self = Self (3) ; pub const FOURTH : Self = Self (4) ; pub const FIFTH : Self = Self (5) ; pub const SIXTH : Self = Self (6) ; pub const SEVENTH : Self = Self (7) ; pub const EIGHTH : Self = Self (8) ; pub const NINTH : Self = Self (9) ; pub const TENTH : Self = Self (10) ; } # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (delegate , value ("2..=8"))] pub struct Restricted (pub Distinguished) ; }
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,4 @@ source: rasn-compiler-tests/tests/edge_cases.rs
description: "\n TestChoice ::= CHOICE {\n restricted Distinguished (second|fourth..sixth|eighth),\n ...\n }\n Distinguished ::= INTEGER {\n first(1),\n second(2),\n third(3),\n fourth(4),\n fifth(5),\n sixth(6),\n seventh(7),\n eighth(8),\n ninth(9),\n tenth(10),\n } (1..10)"
---
Generated:
#[allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused,
clippy::too_many_arguments
)]
pub mod test_module {
extern crate alloc;
use core::borrow::Borrow;
use rasn::prelude::*;
use std::sync::LazyLock;
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, value("1..=10"))]
pub struct Distinguished(pub u8);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum TestChoice {
#[rasn(value("2..=8"))]
restricted(Distinguished),
}
}
# [allow (non_camel_case_types , non_snake_case , non_upper_case_globals , unused , clippy :: too_many_arguments ,)] pub mod test_module { extern crate alloc ; use core :: borrow :: Borrow ; use std :: sync :: LazyLock ; use rasn :: prelude :: * ; # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (delegate , value ("1..=10"))] pub struct Distinguished (pub u8) ; impl Distinguished { pub const FIRST : Self = Self (1) ; pub const SECOND : Self = Self (2) ; pub const THIRD : Self = Self (3) ; pub const FOURTH : Self = Self (4) ; pub const FIFTH : Self = Self (5) ; pub const SIXTH : Self = Self (6) ; pub const SEVENTH : Self = Self (7) ; pub const EIGHTH : Self = Self (8) ; pub const NINTH : Self = Self (9) ; pub const TENTH : Self = Self (10) ; } # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (choice)] # [non_exhaustive] pub enum TestChoice { # [rasn (value ("2..=8") , tag (context , 0))] restricted (Distinguished) , } }
Original file line number Diff line number Diff line change
Expand Up @@ -3,31 +3,4 @@ source: rasn-compiler-tests/tests/edge_cases.rs
description: "\n TestChoice ::= CHOICE {\n restricted Distinguished (second|fourth..sixth|eighth),\n ...\n }\n Distinguished ::= INTEGER {\n first(1),\n second(2),\n third(3),\n fourth(4),\n fifth(5),\n sixth(6),\n seventh(7),\n eighth(8),\n ninth(9),\n tenth(10),\n } (1..10)"
---
Generated:
#[allow(
non_camel_case_types,
non_snake_case,
non_upper_case_globals,
unused,
clippy::too_many_arguments
)]
pub mod test_module {
extern crate alloc;
use core::borrow::Borrow;
use rasn::prelude::*;
use std::sync::LazyLock;
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, value("1..=10"))]
pub struct Distinguished(pub u8);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum TestChoice {
#[rasn(value("2..=8"))]
restricted(Distinguished),
}
impl From<Distinguished> for TestChoice {
fn from(value: Distinguished) -> Self {
Self::restricted(value)
}
}
}
# [allow (non_camel_case_types , non_snake_case , non_upper_case_globals , unused , clippy :: too_many_arguments ,)] pub mod test_module { extern crate alloc ; use core :: borrow :: Borrow ; use std :: sync :: LazyLock ; use rasn :: prelude :: * ; # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (delegate , value ("1..=10"))] pub struct Distinguished (pub u8) ; impl Distinguished { pub const FIRST : Self = Self (1) ; pub const SECOND : Self = Self (2) ; pub const THIRD : Self = Self (3) ; pub const FOURTH : Self = Self (4) ; pub const FIFTH : Self = Self (5) ; pub const SIXTH : Self = Self (6) ; pub const SEVENTH : Self = Self (7) ; pub const EIGHTH : Self = Self (8) ; pub const NINTH : Self = Self (9) ; pub const TENTH : Self = Self (10) ; } # [derive (AsnType , Debug , Clone , Decode , Encode , PartialEq , Eq , Hash)] # [rasn (choice)] # [non_exhaustive] pub enum TestChoice { # [rasn (value ("2..=8") , tag (context , 0))] restricted (Distinguished) , } impl From < Distinguished > for TestChoice { fn from (value : Distinguished) -> Self { Self :: restricted (value) } } }
Loading
Loading