@@ -15,7 +15,6 @@ use std::{
15
15
use directories_next:: ProjectDirs ;
16
16
use miette:: { IntoDiagnostic , Result , WrapErr } ;
17
17
use serde:: { Deserialize , Serialize } ;
18
- use serde_hex:: { Compact , SerHex } ;
19
18
use serialport:: UsbPortInfo ;
20
19
21
20
/// A configured, known serial connection
@@ -35,13 +34,38 @@ pub struct Connection {
35
34
#[ derive( Debug , Deserialize , Serialize , Default , Clone ) ]
36
35
pub struct UsbDevice {
37
36
/// USB Vendor ID
38
- #[ serde( with = "SerHex::<Compact> " ) ]
37
+ #[ serde( serialize_with = "parse_u16_hex" , deserialize_with = "parse_hex_u16 ") ]
39
38
pub vid : u16 ,
40
39
/// USB Product ID
41
- #[ serde( with = "SerHex::<Compact> " ) ]
40
+ #[ serde( serialize_with = "parse_u16_hex" , deserialize_with = "parse_hex_u16 ") ]
42
41
pub pid : u16 ,
43
42
}
44
43
44
+ fn parse_hex_u16 < ' de , D > ( deserializer : D ) -> Result < u16 , D :: Error >
45
+ where
46
+ D : serde:: Deserializer < ' de > ,
47
+ {
48
+ let s = String :: deserialize ( deserializer) ?;
49
+ let bytes = hex:: decode ( if s. len ( ) % 2 == 1 {
50
+ format ! ( "0{}" , s)
51
+ } else {
52
+ s
53
+ } )
54
+ . map_err ( serde:: de:: Error :: custom) ?;
55
+ let padding = vec ! [ 0 ; 2_usize . saturating_sub( bytes. len( ) ) ] ;
56
+ let vec = [ & padding[ ..] , & bytes[ ..] ] . concat ( ) ;
57
+ let decimal = u16:: from_be_bytes ( vec. try_into ( ) . unwrap ( ) ) ;
58
+ Ok ( decimal)
59
+ }
60
+
61
+ fn parse_u16_hex < S > ( decimal : & u16 , serializer : S ) -> Result < S :: Ok , S :: Error >
62
+ where
63
+ S : serde:: Serializer ,
64
+ {
65
+ let hex_string = format ! ( "{:04x}" , decimal) ;
66
+ serializer. serialize_str ( & hex_string)
67
+ }
68
+
45
69
impl UsbDevice {
46
70
/// Check if the given USB port matches this device
47
71
pub fn matches ( & self , port : & UsbPortInfo ) -> bool {
@@ -94,3 +118,69 @@ impl Config {
94
118
. wrap_err_with ( || format ! ( "Failed to write config to {}" , self . save_path. display( ) ) )
95
119
}
96
120
}
121
+
122
+ #[ cfg( test) ]
123
+ mod tests {
124
+ use super :: * ;
125
+ use serde:: Deserialize ;
126
+
127
+ #[ derive( Debug , Deserialize , Serialize ) ]
128
+ struct TestData {
129
+ #[ serde( serialize_with = "parse_u16_hex" , deserialize_with = "parse_hex_u16" ) ]
130
+ value : u16 ,
131
+ }
132
+
133
+ #[ test]
134
+ fn test_parse_hex_u16 ( ) {
135
+ // Test no padding
136
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "aaaa""# ) ;
137
+ assert_eq ! ( result. unwrap( ) . value, 0xaaaa ) ;
138
+
139
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "1234""# ) ;
140
+ assert_eq ! ( result. unwrap( ) . value, 0x1234 ) ;
141
+
142
+ // Test padding
143
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "a""# ) ;
144
+ assert_eq ! ( result. unwrap( ) . value, 0x0a ) ;
145
+
146
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "10""# ) ;
147
+ assert_eq ! ( result. unwrap( ) . value, 0x10 ) ;
148
+
149
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "100""# ) ;
150
+ assert_eq ! ( result. unwrap( ) . value, 0x0100 ) ;
151
+
152
+ // Test uppercase
153
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "A1B2""# ) ;
154
+ assert_eq ! ( result. unwrap( ) . value, 0xA1B2 ) ;
155
+
156
+ // Test invalid
157
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "gg""# ) ;
158
+ assert ! ( result. is_err( ) ) ;
159
+
160
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "10gg""# ) ;
161
+ assert ! ( result. is_err( ) ) ;
162
+ }
163
+
164
+ #[ test]
165
+ fn test_parse_u16_hex ( ) {
166
+ // Valid hexadecimal input with 1 digit
167
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "1""# ) ;
168
+ assert_eq ! ( result. unwrap( ) . value, 0x1 ) ;
169
+
170
+ // Valid hexadecimal input with 2 digits
171
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "ff""# ) ;
172
+ assert_eq ! ( result. unwrap( ) . value, 0xff ) ;
173
+
174
+ // Valid hexadecimal input with 3 digits
175
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "b1a""# ) ;
176
+ assert_eq ! ( result. unwrap( ) . value, 0xb1a ) ;
177
+
178
+ // Valid hexadecimal input with 4 digits
179
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "abc1""# ) ;
180
+ assert_eq ! ( result. unwrap( ) . value, 0xabc1 ) ;
181
+
182
+ // Invalid input (non-hexadecimal character)
183
+ let result: Result < TestData , _ > = toml:: from_str ( r#"value = "xyz""# ) ;
184
+ assert ! ( result. is_err( ) ) ;
185
+ }
186
+ }
0 commit comments