@@ -14,15 +14,18 @@ use unicode_normalization::UnicodeNormalization;
1414
1515pub mod tables;
1616
17+ /// Describes why a string failed stringprep normalization.
1718#[ derive( Debug ) ]
18- enum ErrorCause {
19+ pub enum ErrorCause {
20+ /// Contains stringprep prohibited characters.
1921 ProhibitedCharacter ( char ) ,
22+ /// Violates stringprep rules for bidirectional text.
2023 ProhibitedBidirectionalText ,
2124}
2225
2326/// An error performing the stringprep algorithm.
2427#[ derive( Debug ) ]
25- pub struct Error ( ErrorCause ) ;
28+ pub struct Error ( pub ErrorCause ) ;
2629
2730impl fmt:: Display for Error {
2831 fn fmt ( & self , fmt : & mut fmt:: Formatter ) -> fmt:: Result {
@@ -51,6 +54,7 @@ pub fn saslprep<'a>(s: &'a str) -> Result<Cow<'a, str>, Error> {
5154 return Ok ( Cow :: Borrowed ( s) ) ;
5255 }
5356
57+ // 2.1 Mapping
5458 let mapped = s. chars ( )
5559 . map ( |c| if tables:: non_ascii_space_character ( c) {
5660 ' '
@@ -59,50 +63,79 @@ pub fn saslprep<'a>(s: &'a str) -> Result<Cow<'a, str>, Error> {
5963 } )
6064 . filter ( |& c| !tables:: commonly_mapped_to_nothing ( c) ) ;
6165
66+ // 2.2 Normalization
6267 let normalized = mapped. nfkc ( ) . collect :: < String > ( ) ;
6368
69+ // 2.3 Prohibited Output
6470 let prohibited = normalized
6571 . chars ( )
6672 . filter ( |& c| {
67- tables:: non_ascii_space_character ( c) || tables:: ascii_control_character ( c) ||
68- tables:: non_ascii_control_character ( c) || tables:: private_use ( c) ||
69- tables:: non_character_code_point ( c) ||
70- tables:: surrogate_code ( c) || tables:: inappropriate_for_plain_text ( c) ||
71- tables:: inappropriate_for_canonical_representation ( c) ||
72- tables:: change_display_properties_or_deprecated ( c) ||
73- tables:: tagging_character ( c)
73+ tables:: non_ascii_space_character ( c) /* C.1.2 */ ||
74+ tables:: ascii_control_character ( c) /* C.2.1 */ ||
75+ tables:: non_ascii_control_character ( c) /* C.2.2 */ ||
76+ tables:: private_use ( c) /* C.3 */ ||
77+ tables:: non_character_code_point ( c) /* C.4 */ ||
78+ tables:: surrogate_code ( c) /* C.5 */ ||
79+ tables:: inappropriate_for_plain_text ( c) /* C.6 */ ||
80+ tables:: inappropriate_for_canonical_representation ( c) /* C.7 */ ||
81+ tables:: change_display_properties_or_deprecated ( c) /* C.8 */ ||
82+ tables:: tagging_character ( c) /* C.9 */
7483 } )
7584 . next ( ) ;
7685 if let Some ( c) = prohibited {
7786 return Err ( Error ( ErrorCause :: ProhibitedCharacter ( c) ) ) ;
7887 }
7988
89+ // RFC3454, 6. Bidirectional Characters
8090 if normalized. contains ( tables:: bidi_r_or_al) {
91+ // 2) If a string contains any RandALCat character, the string
92+ // MUST NOT contain any LCat character.
8193 if normalized. contains ( tables:: bidi_l) {
8294 return Err ( Error ( ErrorCause :: ProhibitedBidirectionalText ) ) ;
8395 }
8496
97+ // 3) If a string contains any RandALCat character, a RandALCat
98+ // character MUST be the first character of the string, and a
99+ // RandALCat character MUST be the last character of the string.
85100 if !tables:: bidi_r_or_al ( normalized. chars ( ) . next ( ) . unwrap ( ) ) ||
86101 !tables:: bidi_r_or_al ( normalized. chars ( ) . next_back ( ) . unwrap ( ) ) {
87102 return Err ( Error ( ErrorCause :: ProhibitedBidirectionalText ) ) ;
88103 }
89104 }
90105
106+ // 2.5 Unassigned Code Points
107+ // TODO: Reject unassigned code points.
108+
91109 Ok ( Cow :: Owned ( normalized) )
92110}
93111
94112#[ cfg( test) ]
95113mod test {
96114 use super :: * ;
97115
116+ // RFC4013, 3. Examples
98117 #[ test]
99118 fn saslprep_examples ( ) {
100119 assert_eq ! ( saslprep( "I\u{00AD} X" ) . unwrap( ) , "IX" ) ;
101120 assert_eq ! ( saslprep( "user" ) . unwrap( ) , "user" ) ;
102121 assert_eq ! ( saslprep( "USER" ) . unwrap( ) , "USER" ) ;
103122 assert_eq ! ( saslprep( "\u{00AA} " ) . unwrap( ) , "a" ) ;
104123 assert_eq ! ( saslprep( "\u{2168} " ) . unwrap( ) , "IX" ) ;
105- assert ! ( saslprep( "\u{0007} " ) . is_err( ) ) ;
106- assert ! ( saslprep( "\u{0627} \u{0031} " ) . is_err( ) ) ;
124+ assert_prohibited_character ( saslprep ( "\u{0007} " ) ) ;
125+ assert_prohibited_bidirectional_text ( saslprep ( "\u{0627} \u{0031} " ) ) ;
126+ }
127+
128+ fn assert_prohibited_character < T > ( result : Result < T , Error > ) {
129+ match result {
130+ Err ( Error ( ErrorCause :: ProhibitedCharacter ( _) ) ) => ( ) ,
131+ _ => assert ! ( false )
132+ }
133+ }
134+
135+ fn assert_prohibited_bidirectional_text < T > ( result : Result < T , Error > ) {
136+ match result {
137+ Err ( Error ( ErrorCause :: ProhibitedBidirectionalText ) ) => ( ) ,
138+ _ => assert ! ( false )
139+ }
107140 }
108141}
0 commit comments