1#![deny(unsafe_code)]
38#![cfg_attr(not(any(test)), no_std)]
39#![cfg_attr(docsrs, feature(doc_cfg))]
40#![cfg_attr(docsrs, feature(doc_cfg_hide))]
41#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
42
43#[cfg(feature = "alloc")]
44extern crate alloc;
45
46#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
51pub struct CveId {
52 year: CveYear,
53 number: CveNumber,
55}
56
57pub type CveNumber = u64;
59
60const fn u8_slice_eq(left: &[u8], right: &[u8]) -> bool {
61 match (left, right) {
62 (left, right) => {
63 let mut returned = left.len() == right.len();
64
65 if returned {
66 let mut i = 0;
67
68 while i != left.len() {
69 if !(left[i] == right[i]) {
70 returned = false;
71
72 break;
73 }
74
75 i += 1;
76 }
77 }
78
79 returned
80 }
81 }
82}
83
84macro_rules! int_from_ascii {
85 ($func_name:ident, $int_ty:ty) => {
86 const fn $func_name(src: &[u8]) -> Result<$int_ty, ParseCveIdError> {
87 let mut digits = src;
88
89 let mut result = 0;
90
91 macro_rules! unwrap_or_PCE {
92 ($option:expr) => {
93 match $option {
94 Some(value) => value,
95 None => return Err(ParseCveIdError()),
96 }
97 };
98 }
99
100 #[inline(always)]
101 pub const fn can_not_overflow<T>(digits: &[u8]) -> bool {
102 digits.len() <= core::mem::size_of::<T>() * 2
103 }
104
105 if can_not_overflow::<$int_ty>(digits) {
106 while let [c, rest @ ..] = digits {
115 result *= 10 as $int_ty;
116 let x = unwrap_or_PCE!((*c as char).to_digit(10));
117 result += x as $int_ty;
118 digits = rest;
119 }
120 } else {
121 while let [c, rest @ ..] = digits {
122 let mul = result.checked_mul(10 as $int_ty);
133 let x = unwrap_or_PCE!((*c as char).to_digit(10)) as $int_ty;
134 result = unwrap_or_PCE!(mul);
135 result = unwrap_or_PCE!(<$int_ty>::checked_add(result, x));
136 digits = rest;
137 }
138 }
139 Ok(result)
140 }
141 };
142}
143
144int_from_ascii!(u16_from_ascii, u16);
145int_from_ascii!(u64_from_ascii, u64);
146
147impl CveId {
148 const CVE_PREFIX: &[u8] = b"CVE";
149 const SEPARATOR: u8 = b'-';
150
151 #[inline]
153 pub const fn new(year: CveYear, number: CveNumber) -> Self {
154 Self { year, number }
155 }
156
157 pub const fn from_str(src: &str) -> Result<Self, ParseCveIdError> {
159 let src = src.as_bytes();
160
161 if src.len() < 13 {
163 return Err(ParseCveIdError());
164 }
165
166 if src[3] != Self::SEPARATOR || src[8] != Self::SEPARATOR {
167 return Err(ParseCveIdError());
168 }
169
170 let (prefix, rest) = src.split_at(3);
172
173 if !u8_slice_eq(prefix, Self::CVE_PREFIX) {
174 return Err(ParseCveIdError());
175 }
176
177 let (_sep, rest) = rest.split_at(1);
179 let (year, rest) = rest.split_at(4);
181 let (_sep, number) = rest.split_at(1);
183
184 macro_rules! unwrap_or_PCE {
185 ($result:expr) => {
186 match $result {
187 Ok(value) => value,
188 Err(_err) => return Err(ParseCveIdError()),
189 }
190 };
191 }
192
193 let year = unwrap_or_PCE!(u16_from_ascii(year));
194 let number = unwrap_or_PCE!(u64_from_ascii(number));
195
196 let year = unwrap_or_PCE!(CveYear::new(year));
197
198 Ok(Self { year, number })
199 }
200
201 pub const fn year(&self) -> CveYear {
203 self.year
204 }
205
206 pub const fn number(&self) -> CveNumber {
208 self.number
209 }
210
211 pub const fn is_example_or_test(&self) -> bool {
223 1900 == self.year.0
224 }
225
226 }
230
231impl core::str::FromStr for CveId {
232 type Err = ParseCveIdError;
233
234 fn from_str(src: &str) -> Result<Self, Self::Err> {
235 Self::from_str(src)
236 }
237}
238
239impl core::fmt::Display for CveId {
240 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
241 write!(f, "CVE-{}-{:04}", self.year, self.number)
242 }
243}
244
245#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
251pub struct CveYear(u16);
252
253impl CveYear {
254 const YEAR_MIN: u16 = 0;
255 const YEAR_MAX: u16 = 9999;
256 pub const MIN: Self = CveYear(Self::YEAR_MIN);
257 pub const MAX: Self = CveYear(Self::YEAR_MAX);
258
259 pub const fn new(year: u16) -> Result<Self, InvalidCveYearError> {
261 if year > Self::YEAR_MAX {
262 return Err(InvalidCveYearError());
263 }
264
265 Ok(Self(year))
266 }
267
268 }
270
271impl core::convert::TryFrom<u16> for CveYear {
272 type Error = InvalidCveYearError;
273
274 fn try_from(value: u16) -> Result<Self, Self::Error> {
275 Self::new(value)
276 }
277}
278
279impl core::convert::From<CveYear> for u16 {
280 fn from(year: CveYear) -> Self {
281 year.0
282 }
283}
284
285impl PartialEq<u16> for CveYear {
286 fn eq(&self, other: &u16) -> bool {
287 self.0 == *other
288 }
289}
290
291impl core::fmt::Display for CveYear {
292 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
293 write!(f, "{:04}", self.0)
294 }
295}
296
297#[derive(Debug, Clone, PartialEq, Eq)]
299pub struct InvalidCveYearError();
300
301impl core::fmt::Display for InvalidCveYearError {
302 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
303 write!(f, "invalid CVE year")
304 }
305}
306
307impl core::error::Error for InvalidCveYearError {}
308
309#[derive(Debug, Clone, PartialEq, Eq)]
311pub struct ParseCveIdError();
312
313impl core::fmt::Display for ParseCveIdError {
314 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
315 write!(f, "can not parse CVE ID")
316 }
317}
318
319impl core::error::Error for ParseCveIdError {}
320
321#[cfg(feature = "serde")]
322mod serde {
323 use crate::{CveId, CveYear};
324 use ::serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
325 use ::serde::ser::{Serialize, SerializeStruct, Serializer};
326 use core::fmt::Write;
327
328 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
329 impl Serialize for CveId {
330 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
331 where
332 S: Serializer,
333 {
334 if serializer.is_human_readable() {
335 struct Wrapper<'a> {
336 buf: &'a mut [u8],
337 offset: usize,
338 }
339
340 impl<'a> Wrapper<'a> {
341 fn new(buf: &'a mut [u8]) -> Self {
342 Wrapper {
343 buf: buf,
344 offset: 0,
345 }
346 }
347 }
348
349 impl<'a> core::fmt::Write for Wrapper<'a> {
350 fn write_str(&mut self, s: &str) -> core::fmt::Result {
351 let bytes = s.as_bytes();
352
353 let remainder = &mut self.buf[self.offset..];
354 if remainder.len() < bytes.len() {
355 return Err(core::fmt::Error);
356 }
357 let remainder = &mut remainder[..bytes.len()];
358 remainder.copy_from_slice(bytes);
359
360 self.offset += bytes.len();
361
362 Ok(())
363 }
364 }
365
366 let mut buf = [0 as u8; 3 + 1 + 5 + 1 + 20];
368 let mut wrapper = Wrapper::new(&mut buf);
369 write!(wrapper, "{}", self).expect("can write to fixed buffer");
370 let chars = &wrapper.buf[0..wrapper.offset];
371 let s = str::from_utf8(chars).expect("valid ASCII");
372 serializer.serialize_str(s)
373 } else {
374 let mut state = serializer.serialize_struct("CveId", 2)?;
375 state.serialize_field("year", &self.year)?;
376 state.serialize_field("number", &self.number)?;
377 state.end()
378 }
379 }
380 }
381
382 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
383 impl<'de> Deserialize<'de> for CveId {
384 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
385 where
386 D: Deserializer<'de>,
387 {
388 if deserializer.is_human_readable() {
389 let s = <&str>::deserialize(deserializer)?;
390 CveId::from_str(s).map_err(de::Error::custom)
391 } else {
392 enum Field {
393 Year,
394 Number,
395 }
396
397 impl<'de> Deserialize<'de> for Field {
398 fn deserialize<D>(deserializer: D) -> Result<Field, D::Error>
399 where
400 D: Deserializer<'de>,
401 {
402 struct FieldVisitor;
403
404 impl<'de> Visitor<'de> for FieldVisitor {
405 type Value = Field;
406
407 fn expecting(
408 &self,
409 formatter: &mut core::fmt::Formatter,
410 ) -> core::fmt::Result {
411 formatter.write_str("`year` or `number`")
412 }
413
414 fn visit_str<E>(self, value: &str) -> Result<Field, E>
415 where
416 E: de::Error,
417 {
418 match value {
419 "year" => Ok(Field::Year),
420 "number" => Ok(Field::Number),
421 _ => Err(de::Error::unknown_field(value, FIELDS)),
422 }
423 }
424 }
425
426 deserializer.deserialize_identifier(FieldVisitor)
427 }
428 }
429
430 struct CveIdVisitor;
431
432 impl<'de> Visitor<'de> for CveIdVisitor {
433 type Value = CveId;
434
435 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
436 formatter.write_str("struct CveId")
437 }
438
439 fn visit_seq<V>(self, mut seq: V) -> Result<CveId, V::Error>
440 where
441 V: SeqAccess<'de>,
442 {
443 let year = seq
444 .next_element()?
445 .ok_or_else(|| de::Error::invalid_length(0, &self))?;
446 let number = seq
447 .next_element()?
448 .ok_or_else(|| de::Error::invalid_length(1, &self))?;
449 Ok(CveId::new(year, number))
450 }
451
452 fn visit_map<V>(self, mut map: V) -> Result<CveId, V::Error>
453 where
454 V: MapAccess<'de>,
455 {
456 let mut year = None;
457 let mut number = None;
458 while let Some(key) = map.next_key()? {
459 match key {
460 Field::Year => {
461 if year.is_some() {
462 return Err(de::Error::duplicate_field("year"));
463 }
464 year = Some(map.next_value()?);
465 }
466 Field::Number => {
467 if number.is_some() {
468 return Err(de::Error::duplicate_field("number"));
469 }
470 number = Some(map.next_value()?);
471 }
472 }
473 }
474 let year = year.ok_or_else(|| de::Error::missing_field("year"))?;
475 let number = number.ok_or_else(|| de::Error::missing_field("number"))?;
476 Ok(CveId::new(year, number))
477 }
478 }
479
480 const FIELDS: &[&str] = &["year", "number"];
481 deserializer.deserialize_struct("CveId", FIELDS, CveIdVisitor)
482 }
483 }
484 }
485
486 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
487 impl Serialize for CveYear {
488 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
489 where
490 S: Serializer,
491 {
492 serializer.serialize_newtype_struct("CveYear", &self.0)
493 }
494 }
495
496 #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
497 impl<'de> Deserialize<'de> for CveYear {
498 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
499 where
500 D: Deserializer<'de>,
501 {
502 struct CveYearVisitor;
503
504 impl<'de> Visitor<'de> for CveYearVisitor {
505 type Value = CveYear;
506
507 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
508 formatter.write_str("struct CveYear")
509 }
510
511 fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
512 where
513 D: Deserializer<'de>,
514 {
515 let year = deserializer.deserialize_u16(self)?;
516 Ok(year)
517 }
518
519 fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
520 where
521 E: de::Error,
522 {
523 CveYear::new(v).map_err(E::custom)
524 }
525 }
526
527 deserializer.deserialize_newtype_struct("CveYear", CveYearVisitor)
528 }
529 }
530
531 #[cfg(test)]
532 mod tests {
533 use crate::{CveId, CveNumber, CveYear};
534 use claims::{assert_ok, assert_ok_eq};
535 use serde::{Deserialize, Serialize};
536 use serde_assert::{Deserializer, Serializer, Token};
537
538 #[test]
539 fn test_serialize_binary() -> Result<(), Box<dyn std::error::Error>> {
540 let serializer = Serializer::builder().is_human_readable(false).build();
541
542 let cve_id = CveId::new(1999.try_into()?, 1);
543
544 assert_ok_eq!(
545 cve_id.serialize(&serializer),
546 [
547 Token::Struct {
548 name: "CveId",
549 len: 2
550 },
551 Token::Field("year"),
552 Token::NewtypeStruct { name: "CveYear" },
553 Token::U16(1999),
554 Token::Field("number"),
555 Token::U64(1),
556 Token::StructEnd
557 ]
558 );
559
560 Ok(())
561 }
562
563 #[test]
564 fn test_serialize_human() -> Result<(), Box<dyn std::error::Error>> {
565 let serializer = Serializer::builder().is_human_readable(true).build();
566
567 let cve_id = CveId::new(1999.try_into()?, 1);
568
569 assert_ok_eq!(
570 cve_id.serialize(&serializer),
571 [Token::Str("CVE-1999-0001".to_string())]
572 );
573
574 Ok(())
575 }
576
577 #[test]
578 fn test_deserialize_binary() -> Result<(), Box<dyn std::error::Error>> {
579 let mut deserializer = Deserializer::builder([
580 Token::Struct {
581 name: "CveId",
582 len: 2,
583 },
584 Token::Field("year"),
585 Token::NewtypeStruct { name: "CveYear" },
586 Token::U16(1999),
587 Token::Field("number"),
588 Token::U64(1),
589 Token::StructEnd,
590 ])
591 .is_human_readable(false)
592 .build();
593
594 let cve_id = CveId::new(1999.try_into()?, 1);
595
596 assert_ok_eq!(CveId::deserialize(&mut deserializer), cve_id);
597
598 Ok(())
599 }
600
601 #[test]
602 fn test_deserialize_human() -> Result<(), Box<dyn std::error::Error>> {
603 let mut deserializer = Deserializer::builder([Token::Str("CVE-1999-0001".to_string())])
604 .is_human_readable(true)
605 .build();
606
607 let cve_id = CveId::new(1999.try_into()?, 1);
608
609 assert_ok_eq!(CveId::deserialize(&mut deserializer), cve_id);
610
611 Ok(())
612 }
613
614 #[test]
615 fn test_roundtrip_binary() {
616 let cve_id = CveId::new(CveYear::MIN, CveNumber::MIN);
617
618 let serializer = Serializer::builder().is_human_readable(false).build();
619 let mut deserializer = Deserializer::builder(assert_ok!(cve_id.serialize(&serializer)))
620 .is_human_readable(false)
621 .build();
622
623 assert_ok_eq!(CveId::deserialize(&mut deserializer), cve_id);
624 }
625
626 #[test]
627 fn test_roundtrip_human() {
628 let cve_id = CveId::new(CveYear::MAX, CveNumber::MAX);
629
630 let serializer = Serializer::builder().is_human_readable(true).build();
631 let mut deserializer = Deserializer::builder(assert_ok!(cve_id.serialize(&serializer)))
632 .is_human_readable(true)
633 .build();
634
635 assert_ok_eq!(CveId::deserialize(&mut deserializer), cve_id);
636 }
637 }
638}
639
640#[cfg(feature = "schemars")]
641mod schemars {
642 use crate::CveId;
643 use alloc::borrow::Cow;
644 use schemars::{JsonSchema, Schema, SchemaGenerator, json_schema};
645
646 #[cfg_attr(docsrs, doc(cfg(feature = "schemars")))]
648 impl JsonSchema for CveId {
649 fn schema_name() -> Cow<'static, str> {
650 "CveId".into()
651 }
652
653 fn schema_id() -> Cow<'static, str> {
654 "cve_id::CveId".into()
655 }
656
657 fn json_schema(_: &mut SchemaGenerator) -> Schema {
658 json_schema!({
659 "type": "string",
660 "pattern": r"^CVE-[0-9]{4}-[0-9]{4,19}$"
661 })
662 }
663
664 fn inline_schema() -> bool {
665 true
666 }
667 }
668
669 #[cfg(test)]
670 mod tests {
671 use crate::CveId;
672 use schemars::JsonSchema;
673
674 #[test]
675 fn test_jsonschema() {
676 fn assert_jsonschema<T: JsonSchema>() {}
677 assert_jsonschema::<CveId>();
678 }
679 }
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 #[test]
687 fn test_send() {
688 fn assert_send<T: Send>() {}
689 assert_send::<CveId>();
690 }
691
692 #[test]
693 fn test_sync() {
694 fn assert_sync<T: Sync>() {}
695 assert_sync::<CveId>();
696 }
697
698 #[test]
699 fn test_debug() -> Result<(), Box<dyn std::error::Error>> {
700 assert_eq!(
701 format!("{:?}", CveId::new(1999.try_into()?, 1)),
702 "CveId { year: CveYear(1999), number: 1 }"
703 );
704
705 Ok(())
706 }
707
708 #[test]
709 fn test_display() -> Result<(), Box<dyn std::error::Error>> {
710 assert_eq!(
711 format!("{}", CveId::new(1999.try_into()?, 1)),
712 "CVE-1999-0001"
713 );
714 assert_eq!(
715 format!("{}", CveId::new(1900.try_into()?, 424242)),
716 "CVE-1900-424242"
717 );
718
719 Ok(())
720 }
721
722 #[test]
723 fn test_from_str() -> Result<(), Box<dyn std::error::Error>> {
724 assert_eq!(
725 CveId::from_str("CVE-1999-0001")?,
726 CveId::new(1999.try_into()?, 1)
727 );
728 assert_eq!(
729 CveId::from_str("CVE-1900-424242")?,
730 CveId::new(1900.try_into()?, 424242)
731 );
732
733 assert_eq!(CveId::from_str("hurz").unwrap_err(), ParseCveIdError());
734 assert_eq!(
735 CveId::from_str("cve-1999-0001").unwrap_err(),
736 ParseCveIdError()
737 );
738
739 Ok(())
740 }
741
742 #[test]
743 fn test_parsecveiderror_display() {
744 assert_eq!(
745 format!("{}", CveId::from_str("hurz").unwrap_err()),
746 "can not parse CVE ID"
747 );
748 }
749
750 #[test]
751 fn test_fromstr() -> Result<(), Box<dyn std::error::Error>> {
752 assert_eq!("CVE-1999-0001".parse(), Ok(CveId::new(1999.try_into()?, 1)));
753
754 Ok(())
755 }
756
757 #[test]
758 fn test_isexampleortest() -> Result<(), Box<dyn std::error::Error>> {
759 assert!(CveId::new(1900.try_into()?, 666).is_example_or_test());
760 assert!(!CveId::new(1999.try_into()?, 1).is_example_or_test());
761
762 Ok(())
763 }
764
765 #[test]
766 fn test_min() -> Result<(), Box<dyn std::error::Error>> {
767 assert_eq!(
768 CveId::new(CveYear::MIN, CveNumber::MIN),
769 "CVE-0000-0000".parse()?
770 );
771
772 Ok(())
773 }
774
775 #[test]
776 fn test_max() -> Result<(), Box<dyn std::error::Error>> {
777 assert_eq!(
778 CveId::new(CveYear::MAX, CveNumber::MAX),
779 "CVE-9999-18446744073709551615".parse()?
780 );
781
782 Ok(())
783 }
784}
785
786#[cfg(doctest)]
787#[doc=include_str!("../README-crate.md")]
788mod readme {}