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