use nom::{
branch::alt,
bytes::complete::{tag, take_until},
character::complete::{char, u8},
combinator::{map, map_res, opt},
sequence::{delimited, pair, terminated, tuple},
IResult,
};
use crate::intermediate::*;
use super::{common::*, constraint::constraint};
pub fn character_string_value(input: &str) -> IResult<&str, ASN1Value> {
map(
skip_ws_and_comments(alt((
map(
delimited(tag("\""), take_until("\""), tag("\"")),
ToString::to_string,
),
map(quadruple, |c| c.to_string()),
))),
|m: String| ASN1Value::String(m),
)(input)
}
fn quadruple(input: &str) -> IResult<&str, char> {
map_res(
in_braces(tuple((
terminated(skip_ws(u8), skip_ws(char(COMMA))),
terminated(skip_ws(u8), skip_ws(char(COMMA))),
terminated(skip_ws(u8), skip_ws(char(COMMA))),
skip_ws(u8),
))),
|(group, plane, row, cell)| {
if group > 0 {
Err(nom::Err::Failure(nom::error::Error {
input,
code: nom::error::ErrorKind::Char,
}))
} else {
let code_point = (plane as u32) << 16 | (row as u32) << 8 | cell as u32;
char::from_u32(code_point).ok_or(nom::Err::Failure(nom::error::Error {
input,
code: nom::error::ErrorKind::Char,
}))
}
},
)(input)
}
pub fn character_string(input: &str) -> IResult<&str, ASN1Type> {
map(
pair(
skip_ws_and_comments(alt((
tag(IA5_STRING),
tag(UTF8_STRING),
tag(NUMERIC_STRING),
tag(VISIBLE_STRING),
tag(TELETEX_STRING),
tag(VIDEOTEX_STRING),
tag(GRAPHIC_STRING),
tag(GENERAL_STRING),
tag(UNIVERSAL_STRING),
tag(BMP_STRING),
tag(PRINTABLE_STRING),
))),
opt(constraint),
),
|m| ASN1Type::CharacterString(m.into()),
)(input)
}
#[cfg(test)]
mod tests {
use crate::intermediate::{constraints::*, types::*, *};
use crate::lexer::{
asn1_value,
character_string::{character_string_value, quadruple},
};
use super::character_string;
#[test]
fn parses_unconfined_characterstring() {
let sample = " IA5String";
assert_eq!(
character_string(sample).unwrap().1,
ASN1Type::CharacterString(CharacterString {
constraints: vec![],
ty: CharacterStringType::IA5String
})
)
}
#[test]
fn parses_strictly_constrained_characterstring() {
let sample = " IA5String(SIZE (8))";
assert_eq!(
character_string(sample).unwrap().1,
ASN1Type::CharacterString(CharacterString {
constraints: vec![Constraint::SubtypeConstraint(ElementSet {
set: ElementOrSetOperation::Element(SubtypeElement::SizeConstraint(Box::new(
ElementOrSetOperation::Element(SubtypeElement::SingleValue {
value: ASN1Value::Integer(8),
extensible: false
})
))),
extensible: false
})],
ty: CharacterStringType::IA5String
})
)
}
#[test]
fn parses_range_constrained_characterstring() {
let sample = " IA5String -- even here?!?!? -- (SIZE (8 ..18))";
assert_eq!(
character_string(sample).unwrap().1,
ASN1Type::CharacterString(CharacterString {
constraints: vec![Constraint::SubtypeConstraint(ElementSet {
set: ElementOrSetOperation::Element(SubtypeElement::SizeConstraint(Box::new(
ElementOrSetOperation::Element(SubtypeElement::ValueRange {
min: Some(ASN1Value::Integer(8)),
max: Some(ASN1Value::Integer(18)),
extensible: false
})
))),
extensible: false
})],
ty: CharacterStringType::IA5String
})
)
}
#[test]
fn parses_strictly_constrained_extended_characterstring() {
let sample = r#" IA5String
(SIZE (2, ...))"#;
assert_eq!(
character_string(sample).unwrap().1,
ASN1Type::CharacterString(CharacterString {
constraints: vec![Constraint::SubtypeConstraint(ElementSet {
set: ElementOrSetOperation::Element(SubtypeElement::SizeConstraint(Box::new(
ElementOrSetOperation::Element(SubtypeElement::SingleValue {
value: ASN1Value::Integer(2),
extensible: true
})
))),
extensible: false
})],
ty: CharacterStringType::IA5String
})
)
}
#[test]
fn parses_range_constrained_extended_characterstring() {
let sample = " IA5String (SIZE (8 -- comment -- .. 18, ...))";
assert_eq!(
character_string(sample).unwrap().1,
ASN1Type::CharacterString(CharacterString {
constraints: vec![Constraint::SubtypeConstraint(ElementSet {
set: ElementOrSetOperation::Element(SubtypeElement::SizeConstraint(Box::new(
ElementOrSetOperation::Element(SubtypeElement::ValueRange {
min: Some(ASN1Value::Integer(8)),
max: Some(ASN1Value::Integer(18)),
extensible: true
})
))),
extensible: false
})],
ty: CharacterStringType::IA5String
})
)
}
#[test]
fn parses_character_string_value() {
assert_eq!(
character_string_value("\"a\"").unwrap().1,
ASN1Value::String("a".to_owned())
)
}
#[test]
fn parses_character_string_asn1_value() {
assert_eq!(
asn1_value("\"a\"").unwrap().1,
ASN1Value::String("a".to_owned())
)
}
#[test]
fn parses_iso_10646_quadruple() {
assert_eq!(quadruple("{0,0,0,9}").unwrap().1, '\t');
assert_eq!(quadruple("{0,0,0,10}").unwrap().1, '\n');
assert_eq!(quadruple("{0,0,0,13}").unwrap().1, '\r');
assert_eq!(quadruple("{0,0,0,32}").unwrap().1, ' ');
assert_eq!(quadruple("{0,0,215,23}").unwrap().1, '휗');
assert_eq!(quadruple("{0,0,249,0}").unwrap().1, '豈');
assert_eq!(quadruple("{0,1,0,0}").unwrap().1, '𐀀');
}
}