11use chrono:: { DateTime , Utc } ;
2+ use lazy_regex:: regex_switch;
23use serde:: de:: { self , Visitor } ;
34use serde:: { Deserialize , Deserializer , Serialize , Serializer } ;
5+
46/// Package identifies the code library or command that
57/// is potentially affected by a particular vulnerability.
68#[ derive( Debug , Serialize , Deserialize , Clone ) ]
@@ -67,6 +69,8 @@ pub enum Ecosystem {
6769 SwiftURL ,
6870 Ubuntu {
6971 version : String ,
72+ metadata : Option < String > ,
73+ fips : Option < String > ,
7074 pro : bool ,
7175 lts : bool ,
7276 } ,
@@ -145,25 +149,29 @@ impl Serialize for Ecosystem {
145149 }
146150 Ecosystem :: SwiftURL => serializer. serialize_str ( "SwiftURL" ) ,
147151 Ecosystem :: Ubuntu {
148- version : v,
149- pro : true ,
150- lts : true ,
151- } => serializer. serialize_str ( & format ! ( "Ubuntu:Pro:{}:LTS" , v) ) ,
152- Ecosystem :: Ubuntu {
153- version : v,
154- pro : true ,
155- lts : false ,
156- } => serializer. serialize_str ( & format ! ( "Ubuntu:Pro:{}" , v) ) ,
157- Ecosystem :: Ubuntu {
158- version : v,
159- pro : false ,
160- lts : true ,
161- } => serializer. serialize_str ( & format ! ( "Ubuntu:{}:LTS" , v) ) ,
162- Ecosystem :: Ubuntu {
163- version : v,
164- pro : false ,
165- lts : false ,
166- } => serializer. serialize_str ( & format ! ( "Ubuntu:{}" , v) ) ,
152+ version,
153+ pro,
154+ lts,
155+ metadata,
156+ fips,
157+ } => {
158+ let mut parts: Vec < String > = vec ! [ "Ubuntu" . to_string( ) ] ;
159+ if * pro {
160+ parts. push ( "Pro" . to_string ( ) ) ;
161+ }
162+ if let Some ( stream) = fips {
163+ parts. push ( stream. clone ( ) ) ;
164+ }
165+ parts. push ( version. clone ( ) ) ;
166+ if * lts {
167+ parts. push ( "LTS" . to_string ( ) ) ;
168+ }
169+ if let Some ( meta) = metadata {
170+ parts. push ( meta. clone ( ) ) ;
171+ }
172+ let serialized = parts. join ( ":" ) ;
173+ serializer. serialize_str ( & serialized)
174+ }
167175 Ecosystem :: UVI => serializer. serialize_str ( "UVI" ) ,
168176 }
169177 }
@@ -176,7 +184,7 @@ impl<'de> Deserialize<'de> for Ecosystem {
176184 {
177185 struct EcosystemVisitor ;
178186
179- impl < ' de > Visitor < ' de > for EcosystemVisitor {
187+ impl Visitor < ' _ > for EcosystemVisitor {
180188 type Value = Ecosystem ;
181189
182190 fn expecting ( & self , formatter : & mut std:: fmt:: Formatter ) -> std:: fmt:: Result {
@@ -257,67 +265,19 @@ impl<'de> Deserialize<'de> for Ecosystem {
257265 value. strip_prefix ( "SUSE:" ) . map ( |v| v. to_string ( ) ) ,
258266 ) ) ,
259267 "SwiftURL" => Ok ( Ecosystem :: SwiftURL ) ,
260- _ if value. starts_with ( "Ubuntu:Pro:" ) => {
261- value. strip_prefix ( "Ubuntu:Pro:" ) . map_or (
262- Err ( de:: Error :: unknown_variant ( value, & [ "Ecosystem" ] ) ) ,
263- |v| {
264- let parts: Vec < & str > = v. split ( ':' ) . collect ( ) ;
265- match parts. as_slice ( ) {
266- [ _, ver, "LTS" ] => Ok ( Ecosystem :: Ubuntu {
267- version : ver. to_string ( ) ,
268- pro : true ,
269- lts : true ,
270- } ) ,
271- [ ver, "LTS" ] => Ok ( Ecosystem :: Ubuntu {
272- version : ver. to_string ( ) ,
273- pro : true ,
274- lts : true ,
275- } ) ,
276- [ ver, "LTS" , ..] => Ok ( Ecosystem :: Ubuntu {
277- version : ver. to_string ( ) ,
278- pro : true ,
279- lts : true ,
280- } ) ,
281- [ ver] => Ok ( Ecosystem :: Ubuntu {
282- version : ver. to_string ( ) ,
283- pro : true ,
284- lts : false ,
285- } ) ,
286- _ => Err ( de:: Error :: unknown_variant (
287- value,
288- & [ "Ecosystem" , "Ubuntu:Pro:YY.MM:(LTS?)" ] ,
289- ) ) ,
268+ _ if value. starts_with ( "Ubuntu:" ) => {
269+ regex_switch ! ( value,
270+ r#"^Ubuntu(?::Pro)?(?::(?<fips>FIPS(?:-preview|-updates)?))?:(?<version>\d+\.\d+)(?::LTS)?(?::for:(?<specialized>.+))?$"# => {
271+ Ecosystem :: Ubuntu {
272+ version: version. to_string( ) ,
273+ metadata: ( !specialized. is_empty( ) ) . then_some( specialized. to_string( ) ) ,
274+ fips: ( !fips. is_empty( ) ) . then_some( fips. to_string( ) ) ,
275+ pro: value. contains( ":Pro" ) ,
276+ lts: value. contains( ":LTS" ) ,
290277 }
291- } ,
292- )
293- }
294- _ if value. starts_with ( "Ubuntu:" ) => value. strip_prefix ( "Ubuntu:" ) . map_or (
295- Err ( de:: Error :: unknown_variant ( value, & [ "Ecosystem" ] ) ) ,
296- |v| {
297- let parts: Vec < & str > = v. split ( ':' ) . collect ( ) ;
298- match parts. as_slice ( ) {
299- [ ver, "LTS" ] => Ok ( Ecosystem :: Ubuntu {
300- version : ver. to_string ( ) ,
301- pro : false ,
302- lts : true ,
303- } ) ,
304- [ ver, "LTS" , ..] => Ok ( Ecosystem :: Ubuntu {
305- version : ver. to_string ( ) ,
306- pro : true ,
307- lts : true ,
308- } ) ,
309- [ ver] => Ok ( Ecosystem :: Ubuntu {
310- version : ver. to_string ( ) ,
311- pro : false ,
312- lts : false ,
313- } ) ,
314- _ => Err ( de:: Error :: unknown_variant (
315- value,
316- & [ "Ecosystem" , "Ubuntu:YY.MM:(?LTS)" ] ,
317- ) ) ,
318278 }
319- } ,
320- ) ,
279+ ) . ok_or ( de :: Error :: unknown_variant ( value , & [ "Ecosystem" ] ) )
280+ }
321281 "UVI" => Ok ( Ecosystem :: UVI ) ,
322282 _ => Err ( de:: Error :: unknown_variant ( value, & [ "Ecosystem" ] ) ) ,
323283 }
@@ -734,6 +694,8 @@ mod tests {
734694 version : "20.04" . to_string ( ) ,
735695 pro : true ,
736696 lts : true ,
697+ fips : None ,
698+ metadata : None ,
737699 } ;
738700 let as_json = serde_json:: json!( ubuntu) ;
739701 assert_eq ! ( as_json, serde_json:: json!( "Ubuntu:Pro:20.04:LTS" ) ) ;
@@ -742,6 +704,8 @@ mod tests {
742704 version : "20.04" . to_string ( ) ,
743705 pro : true ,
744706 lts : false ,
707+ fips : None ,
708+ metadata : None ,
745709 } ;
746710 let as_json = serde_json:: json!( ubuntu) ;
747711 assert_eq ! ( as_json, serde_json:: json!( "Ubuntu:Pro:20.04" ) ) ;
@@ -750,6 +714,8 @@ mod tests {
750714 version : "20.04" . to_string ( ) ,
751715 pro : false ,
752716 lts : true ,
717+ fips : None ,
718+ metadata : None ,
753719 } ;
754720 let as_json = serde_json:: json!( ubuntu) ;
755721 assert_eq ! ( as_json, serde_json:: json!( "Ubuntu:20.04:LTS" ) ) ;
@@ -758,6 +724,8 @@ mod tests {
758724 version : "20.04" . to_string ( ) ,
759725 pro : false ,
760726 lts : false ,
727+ fips : None ,
728+ metadata : None ,
761729 } ;
762730 let as_json = serde_json:: json!( ubuntu) ;
763731 assert_eq ! ( as_json, serde_json:: json!( "Ubuntu:20.04" ) ) ;
@@ -769,7 +737,9 @@ mod tests {
769737 Ecosystem :: Ubuntu {
770738 version: "20.04" . to_string( ) ,
771739 pro: true ,
772- lts: true
740+ lts: true ,
741+ fips: None ,
742+ metadata: None ,
773743 }
774744 ) ;
775745
@@ -780,7 +750,9 @@ mod tests {
780750 Ecosystem :: Ubuntu {
781751 version: "20.04" . to_string( ) ,
782752 pro: true ,
783- lts: false
753+ lts: false ,
754+ fips: None ,
755+ metadata: None ,
784756 }
785757 ) ;
786758
@@ -791,7 +763,9 @@ mod tests {
791763 Ecosystem :: Ubuntu {
792764 version: "20.04" . to_string( ) ,
793765 pro: false ,
794- lts: true
766+ lts: true ,
767+ fips: None ,
768+ metadata: None ,
795769 }
796770 ) ;
797771
@@ -802,7 +776,62 @@ mod tests {
802776 Ecosystem :: Ubuntu {
803777 version: "20.04" . to_string( ) ,
804778 pro: false ,
805- lts: false
779+ lts: false ,
780+ fips: None ,
781+ metadata: None ,
782+ }
783+ ) ;
784+
785+ let json_str = r#""Ubuntu:22.04:LTS:for:NVIDIA:BlueField""# ;
786+ let ubuntu: Ecosystem = serde_json:: from_str ( json_str) . unwrap ( ) ;
787+ assert_eq ! (
788+ ubuntu,
789+ Ecosystem :: Ubuntu {
790+ version: "22.04" . to_string( ) ,
791+ pro: false ,
792+ lts: true ,
793+ fips: None ,
794+ metadata: Some ( "NVIDIA:BlueField" . to_string( ) ) ,
795+ }
796+ ) ;
797+
798+ let json_str = r#""Ubuntu:Pro:FIPS-preview:22.04:LTS""# ;
799+ let ubuntu: Ecosystem = serde_json:: from_str ( json_str) . unwrap ( ) ;
800+ assert_eq ! (
801+ ubuntu,
802+ Ecosystem :: Ubuntu {
803+ version: "22.04" . to_string( ) ,
804+ pro: true ,
805+ lts: true ,
806+ fips: Some ( "FIPS-preview" . to_string( ) ) ,
807+ metadata: None ,
808+ }
809+ ) ;
810+
811+ let json_str = r#""Ubuntu:Pro:FIPS-updates:18.04:LTS""# ;
812+ let ubuntu: Ecosystem = serde_json:: from_str ( json_str) . unwrap ( ) ;
813+ assert_eq ! (
814+ ubuntu,
815+ Ecosystem :: Ubuntu {
816+ version: "18.04" . to_string( ) ,
817+ pro: true ,
818+ lts: true ,
819+ fips: Some ( "FIPS-updates" . to_string( ) ) ,
820+ metadata: None ,
821+ }
822+ ) ;
823+
824+ let json_str = r#""Ubuntu:Pro:FIPS:16.04:LTS""# ;
825+ let ubuntu: Ecosystem = serde_json:: from_str ( json_str) . unwrap ( ) ;
826+
827+ assert_eq ! (
828+ ubuntu,
829+ Ecosystem :: Ubuntu {
830+ version: "16.04" . to_string( ) ,
831+ pro: true ,
832+ lts: true ,
833+ fips: Some ( "FIPS" . to_string( ) ) ,
834+ metadata: None ,
806835 }
807836 ) ;
808837 }
0 commit comments