1
1
use std:: time:: Duration ;
2
2
3
+ use crate :: headers:: HeaderValue ;
4
+
3
5
/// An individual `ServerTiming` entry.
4
6
//
5
7
// # Implementation notes
@@ -21,37 +23,74 @@ pub struct Entry {
21
23
22
24
impl Entry {
23
25
/// Create a new instance of `Entry`.
24
- pub fn new ( name : String , dur : Option < Duration > , desc : Option < String > ) -> Self {
25
- Self { name, dur, desc }
26
+ ///
27
+ /// # Errors
28
+ ///
29
+ /// An error will be returned if the string values are invalid ASCII.
30
+ pub fn new ( name : String , dur : Option < Duration > , desc : Option < String > ) -> crate :: Result < Self > {
31
+ crate :: ensure!( name. is_ascii( ) , "Name should be valid ASCII" ) ;
32
+ if let Some ( desc) = desc. as_ref ( ) {
33
+ crate :: ensure!( desc. is_ascii( ) , "Description should be valid ASCII" ) ;
34
+ } ;
35
+
36
+ Ok ( Self { name, dur, desc } )
26
37
}
27
38
28
39
/// The timing name.
29
40
pub fn name ( & self ) -> & String {
30
41
& self . name
31
42
}
32
43
33
- /// Set the timing name.
34
- pub fn set_name ( & mut self , name : String ) {
35
- self . name = name;
36
- }
37
-
38
44
/// The timing duration.
39
45
pub fn duration ( & self ) -> Option < Duration > {
40
46
self . dur
41
47
}
42
48
43
- /// Set the timing name.
44
- pub fn set_duration ( & mut self , dur : Option < Duration > ) {
45
- self . dur = dur;
46
- }
47
-
48
49
/// The timing description.
49
50
pub fn description ( & self ) -> Option < & String > {
50
51
self . desc . as_ref ( )
51
52
}
53
+ }
54
+
55
+ impl From < Entry > for HeaderValue {
56
+ fn from ( entry : Entry ) -> HeaderValue {
57
+ let mut string = entry. name ;
58
+
59
+ // Format a `Duration` into the format that the spec expects.
60
+ let f = |d : Duration | d. as_secs_f64 ( ) * 1000.0 ;
61
+
62
+ match ( entry. dur , entry. desc ) {
63
+ ( Some ( dur) , Some ( desc) ) => {
64
+ string. push_str ( & format ! ( "; dur={}; desc=\" {}\" " , f( dur) , desc) )
65
+ }
66
+ ( Some ( dur) , None ) => string. push_str ( & format ! ( "; dur={}" , f( dur) ) ) ,
67
+ ( None , Some ( desc) ) => string. push_str ( & format ! ( "; desc=\" {}\" " , desc) ) ,
68
+ ( None , None ) => { }
69
+ } ;
70
+
71
+ // SAFETY: we validate that the values are valid ASCII on creation.
72
+ unsafe { HeaderValue :: from_bytes_unchecked ( string. into_bytes ( ) ) }
73
+ }
74
+ }
75
+
76
+ #[ cfg( test) ]
77
+ mod test {
78
+ use super :: * ;
79
+
80
+ #[ test]
81
+ fn create_header_value ( ) -> crate :: Result < ( ) > {
82
+ let name = String :: from ( "Server" ) ;
83
+ let dur = Duration :: from_secs ( 1 ) ;
84
+ let desc = String :: from ( "A server timing" ) ;
85
+
86
+ let val: HeaderValue = Entry :: new ( name. clone ( ) , None , None ) ?. into ( ) ;
87
+ assert_eq ! ( val, "Server" ) ;
88
+
89
+ let val: HeaderValue = Entry :: new ( name. clone ( ) , Some ( dur) , None ) ?. into ( ) ;
90
+ assert_eq ! ( val, "Server; dur=1000" ) ;
52
91
53
- /// Set the timing description.
54
- pub fn set_description ( & mut self , desc : Option < String > ) {
55
- self . desc = desc ;
92
+ let val : HeaderValue = Entry :: new ( name . clone ( ) , Some ( dur ) , Some ( desc . clone ( ) ) ) ? . into ( ) ;
93
+ assert_eq ! ( val , r#"Server; dur=1000; desc="A server timing""# ) ;
94
+ Ok ( ( ) )
56
95
}
57
96
}
0 commit comments