@@ -134,6 +134,29 @@ impl Method {
134134 }
135135 }
136136
137+ /// Convert static bytes into a `Method`.
138+ ///
139+ /// # Panics
140+ ///
141+ /// If the input bytes are not a valid method name or if the method name is over 15 bytes.
142+ pub const fn from_static ( src : & ' static [ u8 ] ) -> Method {
143+ match src {
144+ b"OPTIONS" => Method :: OPTIONS ,
145+ b"GET" => Method :: GET ,
146+ b"POST" => Method :: POST ,
147+ b"PUT" => Method :: PUT ,
148+ b"DELETE" => Method :: DELETE ,
149+ b"HEAD" => Method :: HEAD ,
150+ b"TRACE" => Method :: TRACE ,
151+ b"CONNECT" => Method :: CONNECT ,
152+ b"PATCH" => Method :: PATCH ,
153+ src => {
154+ let inline = InlineExtension :: from_static ( src) ;
155+ Method ( ExtensionInline ( inline) )
156+ }
157+ }
158+ }
159+
137160 fn extension_inline ( src : & [ u8 ] ) -> Result < Method , InvalidMethod > {
138161 let inline = InlineExtension :: new ( src) ?;
139162
@@ -316,6 +339,9 @@ mod extension {
316339 // Invariant: self.0 contains valid UTF-8.
317340 pub struct AllocatedExtension ( Box < [ u8 ] > ) ;
318341
342+ #[ derive( Clone , PartialEq , Eq , Hash ) ]
343+ pub struct StaticExtension ( & ' static [ u8 ] ) ;
344+
319345 impl InlineExtension {
320346 // Method::from_bytes() assumes this is at least 7
321347 pub const MAX : usize = 15 ;
@@ -330,6 +356,34 @@ mod extension {
330356 Ok ( InlineExtension ( data, src. len ( ) as u8 ) )
331357 }
332358
359+ /// Convert static bytes into an `InlineExtension`.
360+ ///
361+ /// # Panics
362+ ///
363+ /// If the input bytes are not a valid method name or if the method name is over 15 bytes.
364+ pub const fn from_static ( src : & ' static [ u8 ] ) -> InlineExtension {
365+ let mut i = 0 ;
366+ let mut dst = [ 0u8 ; 15 ] ;
367+ if src. len ( ) > 15 {
368+ // panicking in const requires Rust 1.57.0
369+ #[ allow( unconditional_panic) ]
370+ ( [ ] as [ u8 ; 0 ] ) [ 0 ] ;
371+ }
372+ while i < src. len ( ) {
373+ let byte = src[ i] ;
374+ let v = METHOD_CHARS [ byte as usize ] ;
375+ if v == 0 {
376+ // panicking in const requires Rust 1.57.0
377+ #[ allow( unconditional_panic) ]
378+ ( [ ] as [ u8 ; 0 ] ) [ 0 ] ;
379+ }
380+ dst[ i] = byte;
381+ i += 1 ;
382+ }
383+
384+ InlineExtension ( dst, i as u8 )
385+ }
386+
333387 pub fn as_str ( & self ) -> & str {
334388 let InlineExtension ( ref data, len) = self ;
335389 // Safety: the invariant of InlineExtension ensures that the first
@@ -436,6 +490,43 @@ mod test {
436490 assert_eq ! ( Method :: GET , & Method :: GET ) ;
437491 }
438492
493+ #[ test]
494+ fn test_from_static ( ) {
495+ // First class variant
496+ assert_eq ! (
497+ Method :: from_static( b"GET" ) ,
498+ Method :: from_bytes( b"GET" ) . unwrap( )
499+ ) ;
500+ // Inline, len < 15
501+ assert_eq ! (
502+ Method :: from_static( b"PROPFIND" ) ,
503+ Method :: from_bytes( b"PROPFIND" ) . unwrap( )
504+ ) ;
505+ // Inline, len == 15
506+ assert_eq ! ( Method :: from_static( b"GET" ) , Method :: GET ) ;
507+ assert_eq ! (
508+ Method :: from_static( b"123456789012345" ) . to_string( ) ,
509+ "123456789012345" . to_string( )
510+ ) ;
511+ }
512+
513+ #[ test]
514+ #[ should_panic]
515+ fn test_from_static_too_long ( ) {
516+ // Ref, len > 15
517+ Method :: from_static ( b"1234567890123456" ) ;
518+ assert_eq ! (
519+ Method :: from_static( b"1234567890123456" ) . to_string( ) ,
520+ "1234567890123456" . to_string( )
521+ ) ;
522+ }
523+
524+ #[ test]
525+ #[ should_panic]
526+ fn test_from_static_bad ( ) {
527+ Method :: from_static ( b"\0 " ) ;
528+ }
529+
439530 #[ test]
440531 fn test_invalid_method ( ) {
441532 assert ! ( Method :: from_str( "" ) . is_err( ) ) ;
@@ -497,4 +588,13 @@ mod test {
497588 ) ;
498589 }
499590 }
591+
592+ #[ test]
593+ fn test_extension_comparison ( ) {
594+ const REPORT : Method = Method :: from_static ( b"REPORT" ) ;
595+
596+ let m = Method :: from_bytes ( b"REPORT" ) . unwrap ( ) ;
597+
598+ assert ! ( matches!( m, REPORT ) ) ;
599+ }
500600}
0 commit comments