wdl_core/concern/code.rs
1//! Codes for validation failures and lint warnings.
2
3use std::num::NonZeroUsize;
4
5use crate::Version;
6
7mod kind;
8
9pub use kind::Kind;
10use serde::Deserialize;
11use serde::Serialize;
12
13/// An error related to a [`Code`].
14#[derive(Debug)]
15pub enum Error {
16 /// Attempted to make a code with an invalid index.
17 InvalidIndex(usize),
18}
19
20impl std::fmt::Display for Error {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 match self {
23 Error::InvalidIndex(index) => write!(f, "invalid index: {index}"),
24 }
25 }
26}
27
28impl std::error::Error for Error {}
29
30/// A [`Result`](std::result::Result) with an [`Error`].
31type Result<T> = std::result::Result<T, Error>;
32
33/// A code.
34#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
35pub struct Code {
36 /// The kind of code.
37 kind: Kind,
38
39 /// The grammar for this code.
40 grammar: Version,
41
42 /// The index for this code.
43 index: NonZeroUsize,
44}
45
46impl Code {
47 /// Attempts to create a new [`Code`].
48 ///
49 /// # Examples
50 ///
51 /// ```
52 /// use wdl_core::concern::code::Kind;
53 /// use wdl_core::concern::Code;
54 /// use wdl_core::Version;
55 ///
56 /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
57 /// assert_eq!(code.kind(), &Kind::Warning);
58 /// assert_eq!(code.grammar(), &Version::V1);
59 /// assert_eq!(code.index().get(), 1);
60 ///
61 /// # Ok::<(), Box<dyn std::error::Error>>(())
62 /// ```
63 pub fn try_new(kind: Kind, grammar: Version, index: usize) -> Result<Self> {
64 let index = NonZeroUsize::try_from(index).map_err(|_| Error::InvalidIndex(index))?;
65
66 Ok(Self {
67 kind,
68 grammar,
69 index,
70 })
71 }
72
73 /// Gets the [`Kind`] of concern for this [`Code`] by reference.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use wdl_core::concern::code::Kind;
79 /// use wdl_core::concern::Code;
80 /// use wdl_core::Version;
81 ///
82 /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
83 /// assert_eq!(code.kind(), &Kind::Warning);
84 ///
85 /// # Ok::<(), Box<dyn std::error::Error>>(())
86 /// ```
87 pub fn kind(&self) -> &Kind {
88 &self.kind
89 }
90
91 /// Gets the grammar [`Version`] for this [`Code`] by reference.
92 ///
93 /// # Examples
94 ///
95 /// ```
96 /// use wdl_core::concern::code::Kind;
97 /// use wdl_core::concern::Code;
98 /// use wdl_core::Version;
99 ///
100 /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
101 /// assert_eq!(code.grammar(), &Version::V1);
102 ///
103 /// # Ok::<(), Box<dyn std::error::Error>>(())
104 /// ```
105 pub fn grammar(&self) -> &Version {
106 &self.grammar
107 }
108
109 /// Gets the index of this [`Code`] by reference.
110 ///
111 /// # Examples
112 ///
113 /// ```
114 /// use wdl_core::concern::code::Kind;
115 /// use wdl_core::concern::Code;
116 /// use wdl_core::Version;
117 ///
118 /// let code = Code::try_new(Kind::Warning, Version::V1, 1)?;
119 /// assert_eq!(code.index().get(), 1);
120 ///
121 /// # Ok::<(), Box<dyn std::error::Error>>(())
122 /// ```
123 pub fn index(&self) -> NonZeroUsize {
124 self.index
125 }
126}
127
128impl std::fmt::Display for Code {
129 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130 write!(
131 f,
132 "{}::{}{:03}",
133 self.grammar.short_name(),
134 self.kind.prefix(),
135 self.index
136 )
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143
144 #[test]
145 fn zero_index() {
146 let err = Code::try_new(Kind::Error, Version::V1, 0).unwrap_err();
147 assert!(matches!(err, Error::InvalidIndex(0)));
148 }
149
150 #[test]
151 fn display() {
152 let identity = Code::try_new(Kind::Error, Version::V1, 1).unwrap();
153 assert_eq!(identity.to_string(), String::from("v1::E001"));
154
155 let identity = Code::try_new(Kind::Warning, Version::V1, 1).unwrap();
156 assert_eq!(identity.to_string(), String::from("v1::W001"));
157 }
158}