@@ -20,10 +20,51 @@ use std::iter::once;
20
20
#[ derive( Clone , Debug , PartialEq ) ]
21
21
pub struct NonEmpty < T > ( pub Vec < T > ) ;
22
22
23
+ /// Error that might occur when creating a new [`Identifier`].
24
+ #[ derive( Debug ) ]
25
+ pub enum IdentifierError {
26
+ StartsWithDigit ,
27
+ ContainsNonASCIIAlphaNum
28
+ }
29
+
30
+ impl fmt:: Display for IdentifierError {
31
+ fn fmt ( & self , f : & mut fmt:: Formatter ) -> Result < ( ) , fmt:: Error > {
32
+ match * self {
33
+ IdentifierError :: StartsWithDigit =>
34
+ f. write_str ( "starts starts with a digit" ) ,
35
+
36
+ IdentifierError :: ContainsNonASCIIAlphaNum =>
37
+ f. write_str ( "contains at least one non-alphanumeric ASCII character" )
38
+ }
39
+ }
40
+ }
41
+
23
42
/// A generic identifier.
24
43
#[ derive( Clone , Debug , PartialEq ) ]
25
44
pub struct Identifier ( pub String ) ;
26
45
46
+ impl Identifier {
47
+ /// Create a new [`Identifier`].
48
+ ///
49
+ /// # Errors
50
+ ///
51
+ /// This function will fail if the identifier starts with a digit or contains non-alphanumeric
52
+ /// ASCII characters.
53
+ pub fn new < N > ( name : N ) -> Result < Self , IdentifierError > where N : Into < String > {
54
+ let name = name. into ( ) ;
55
+
56
+ if name. chars ( ) . next ( ) . map ( |c| c. is_ascii_alphabetic ( ) ) == Some ( false ) {
57
+ // check the first letter is not a digit
58
+ Err ( IdentifierError :: StartsWithDigit )
59
+ } else if name. contains ( |c : char | !( c. is_ascii_alphanumeric ( ) || c == '_' ) ) {
60
+ // check we only have ASCII alphanumeric characters
61
+ Err ( IdentifierError :: ContainsNonASCIIAlphaNum )
62
+ } else {
63
+ Ok ( Identifier ( name) )
64
+ }
65
+ }
66
+ }
67
+
27
68
impl < ' a > From < & ' a str > for Identifier {
28
69
fn from ( s : & str ) -> Self {
29
70
Identifier ( s. to_owned ( ) )
@@ -46,6 +87,20 @@ impl fmt::Display for Identifier {
46
87
#[ derive( Clone , Debug , PartialEq ) ]
47
88
pub struct TypeName ( pub String ) ;
48
89
90
+ impl TypeName {
91
+ /// Create a new [`TypeName`].
92
+ ///
93
+ /// # Errors
94
+ ///
95
+ /// This function will fail if the type name starts with a digit or contains non-alphanumeric
96
+ /// ASCII characters.
97
+ pub fn new < N > ( name : N ) -> Result < Self , IdentifierError > where N : Into < String > {
98
+ // build as identifier and unwrap into type name
99
+ let Identifier ( tn) = Identifier :: new ( name) ?;
100
+ Ok ( TypeName ( tn) )
101
+ }
102
+ }
103
+
49
104
impl < ' a > From < & ' a str > for TypeName {
50
105
fn from ( s : & str ) -> Self {
51
106
TypeName ( s. to_owned ( ) )
@@ -920,6 +975,29 @@ pub enum PreprocessorExtensionBehavior {
920
975
mod tests {
921
976
use super :: * ;
922
977
978
+ #[ test]
979
+ fn create_new_identifier ( ) {
980
+ assert ! ( Identifier :: new( "foo_bar" ) . is_ok( ) ) ;
981
+ assert ! ( Identifier :: new( "3foo_bar" ) . is_err( ) ) ;
982
+ assert ! ( Identifier :: new( "FooBar" ) . is_ok( ) ) ;
983
+ assert ! ( Identifier :: new( "_FooBar" ) . is_err( ) ) ;
984
+ assert ! ( Identifier :: new( "foo3" ) . is_ok( ) ) ;
985
+ assert ! ( Identifier :: new( "foo3_" ) . is_ok( ) ) ;
986
+ assert ! ( Identifier :: new( "fδo3_" ) . is_err( ) ) ;
987
+ }
988
+
989
+ #[ test]
990
+ fn create_new_type_name ( ) {
991
+ assert ! ( TypeName :: new( "foo_bar" ) . is_ok( ) ) ;
992
+ assert ! ( TypeName :: new( "FooBar" ) . is_ok( ) ) ;
993
+ assert ! ( TypeName :: new( "foo3" ) . is_ok( ) ) ;
994
+ assert ! ( TypeName :: new( "foo3_" ) . is_ok( ) ) ;
995
+
996
+ assert ! ( TypeName :: new( "_FooBar" ) . is_err( ) ) ;
997
+ assert ! ( TypeName :: new( "3foo_bar" ) . is_err( ) ) ;
998
+ assert ! ( TypeName :: new( "fδo3_" ) . is_err( ) ) ;
999
+ }
1000
+
923
1001
// bool predicate(float x) {
924
1002
// }
925
1003
#[ test]
0 commit comments