1use std::fmt;
4
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
9#[serde(rename_all = "kebab-case")]
10pub enum CompilerKind {
11 Clang,
13 AppleClang,
17 ClangCl,
22 Gcc,
24 Msvc,
28 Unknown,
31}
32
33impl CompilerKind {
34 pub fn as_key(self) -> &'static str {
36 match self {
37 CompilerKind::Clang => "clang",
38 CompilerKind::AppleClang => "apple-clang",
39 CompilerKind::ClangCl => "clang-cl",
40 CompilerKind::Gcc => "gcc",
41 CompilerKind::Msvc => "msvc",
42 CompilerKind::Unknown => "unknown",
43 }
44 }
45
46 pub fn is_clang_like(self) -> bool {
51 matches!(
52 self,
53 CompilerKind::Clang | CompilerKind::AppleClang | CompilerKind::ClangCl
54 )
55 }
56
57 pub fn supports_gcc_style_command_line(self) -> bool {
62 matches!(
63 self,
64 CompilerKind::Clang | CompilerKind::AppleClang | CompilerKind::Gcc
65 )
66 }
67
68 pub fn speaks_msvc_dialect(self) -> bool {
72 matches!(self, CompilerKind::Msvc | CompilerKind::ClangCl)
73 }
74}
75
76impl fmt::Display for CompilerKind {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 f.write_str(self.as_key())
79 }
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
84#[serde(rename_all = "kebab-case")]
85pub enum ArchiverKind {
86 Ar,
89 LlvmAr,
91 Lib,
94 Unknown,
96}
97
98impl ArchiverKind {
99 pub fn as_key(self) -> &'static str {
100 match self {
101 ArchiverKind::Ar => "ar",
102 ArchiverKind::LlvmAr => "llvm-ar",
103 ArchiverKind::Lib => "lib",
104 ArchiverKind::Unknown => "unknown",
105 }
106 }
107
108 pub fn supports_ar_crs(self) -> bool {
111 matches!(self, ArchiverKind::Ar | ArchiverKind::LlvmAr)
112 }
113
114 pub fn produces_static_library(self) -> bool {
120 matches!(
121 self,
122 ArchiverKind::Ar | ArchiverKind::LlvmAr | ArchiverKind::Lib
123 )
124 }
125}
126
127impl fmt::Display for ArchiverKind {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 f.write_str(self.as_key())
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
139pub struct CompilerVersion {
140 pub major: u32,
141 #[serde(default, skip_serializing_if = "Option::is_none")]
142 pub minor: Option<u32>,
143 #[serde(default, skip_serializing_if = "Option::is_none")]
144 pub patch: Option<u32>,
145 pub raw: String,
146}
147
148impl CompilerVersion {
149 pub fn parse(raw: &str) -> Option<Self> {
153 let mut parts = raw.split('.');
154 let major: u32 = parts.next()?.parse().ok()?;
155 let minor = parts.next().and_then(|s| s.parse().ok());
156 let patch = parts.next().and_then(|s| s.parse().ok());
157 Some(Self {
158 major,
159 minor,
160 patch,
161 raw: raw.to_owned(),
162 })
163 }
164
165 pub fn to_display_string(&self) -> String {
168 match (self.minor, self.patch) {
169 (Some(min), Some(pat)) => format!("{}.{}.{}", self.major, min, pat),
170 (Some(min), None) => format!("{}.{}", self.major, min),
171 _ => self.major.to_string(),
172 }
173 }
174}
175
176impl fmt::Display for CompilerVersion {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 f.write_str(&self.to_display_string())
179 }
180}
181
182#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
184pub struct CompilerIdentity {
185 pub kind: CompilerKind,
186 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub version: Option<CompilerVersion>,
191 #[serde(default, skip_serializing_if = "Option::is_none")]
194 pub target: Option<String>,
195 pub raw_version_line: String,
198}
199
200impl CompilerIdentity {
201 pub fn unknown(raw_version_line: impl Into<String>) -> Self {
203 Self {
204 kind: CompilerKind::Unknown,
205 version: None,
206 target: None,
207 raw_version_line: raw_version_line.into(),
208 }
209 }
210
211 pub fn as_json(&self) -> serde_json::Value {
213 let mut obj = serde_json::Map::new();
214 obj.insert(
215 "kind".to_owned(),
216 serde_json::Value::String(self.kind.as_key().to_owned()),
217 );
218 if let Some(v) = &self.version {
219 obj.insert(
220 "version".to_owned(),
221 serde_json::Value::String(v.to_display_string()),
222 );
223 }
224 if let Some(t) = &self.target {
225 obj.insert("target".to_owned(), serde_json::Value::String(t.clone()));
226 }
227 obj.insert(
228 "raw_version_line".to_owned(),
229 serde_json::Value::String(self.raw_version_line.clone()),
230 );
231 serde_json::Value::Object(obj)
232 }
233}
234
235#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
237pub struct ArchiverIdentity {
238 pub kind: ArchiverKind,
239 #[serde(default, skip_serializing_if = "Option::is_none")]
240 pub version: Option<CompilerVersion>,
241 pub raw_version_line: String,
242}
243
244impl ArchiverIdentity {
245 pub fn unknown(raw_version_line: impl Into<String>) -> Self {
246 Self {
247 kind: ArchiverKind::Unknown,
248 version: None,
249 raw_version_line: raw_version_line.into(),
250 }
251 }
252
253 pub fn as_json(&self) -> serde_json::Value {
254 let mut obj = serde_json::Map::new();
255 obj.insert(
256 "kind".to_owned(),
257 serde_json::Value::String(self.kind.as_key().to_owned()),
258 );
259 if let Some(v) = &self.version {
260 obj.insert(
261 "version".to_owned(),
262 serde_json::Value::String(v.to_display_string()),
263 );
264 }
265 obj.insert(
266 "raw_version_line".to_owned(),
267 serde_json::Value::String(self.raw_version_line.clone()),
268 );
269 serde_json::Value::Object(obj)
270 }
271}