1use std::str::FromStr;
2
3use kcl_error::KclError;
4use kcl_error::KclErrorDetails;
5use serde::Deserialize;
6use serde::Serialize;
7
8#[derive(
10 Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq, Ord, PartialOrd, schemars::JsonSchema, ts_rs::TS,
11)]
12#[ts(export)]
13pub enum KclVersion {
14 #[default]
16 #[serde(rename = "1.0")]
17 V1,
18 #[serde(rename = "2.0")]
21 V2,
22 #[serde(rename = "3.0-preview")]
24 V3Preview,
25 #[serde(rename = "3.0")]
27 V3,
28 }
31
32impl KclVersion {
33 pub fn as_str(self) -> &'static str {
35 match self {
36 Self::V1 => "1.0",
37 Self::V2 => "2.0",
38 Self::V3Preview => "3.0-preview",
39 Self::V3 => "3.0",
40 }
41 }
42}
43
44#[derive(Debug, Eq, PartialEq, Clone, Copy)]
45pub struct KclVersionError;
46
47impl core::error::Error for KclVersionError {}
48
49impl std::fmt::Display for KclVersionError {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 write!(f, "Unrecognized version, expected 1.0, 2.0, or 3.0")
52 }
53}
54
55impl FromStr for KclVersion {
56 type Err = KclVersionError;
57
58 fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
59 match s {
60 "1" | "1.0" | "1.0.0" => Ok(Self::V1),
61 "2" | "2.0" | "2.0.0" => Ok(Self::V2),
62 "3-preview" | "3.0-preview" | "3.0.0-preview" => Ok(Self::V3Preview),
63 "3" | "3.0" | "3.0.0" => Ok(Self::V3),
64 _other => Err(KclVersionError),
65 }
66 }
67}
68
69impl From<KclVersionError> for KclError {
70 fn from(e: KclVersionError) -> Self {
71 Self::Semantic {
72 details: KclErrorDetails {
73 source_ranges: Default::default(),
74 backtrace: Default::default(),
75 message: e.to_string(),
76 },
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84
85 #[test]
86 fn roundtrip_str() {
87 for input in [KclVersion::V1, KclVersion::V2, KclVersion::V3Preview, KclVersion::V3] {
88 let serialized = input.as_str();
89 let deserialized: KclVersion = serialized.parse().unwrap();
90 assert_eq!(input, deserialized);
91 }
92 }
93
94 #[test]
95 fn kcl_version_parses_supported_spellings() {
96 assert_eq!(KclVersion::from_str("1"), Ok(KclVersion::V1));
97 assert_eq!(KclVersion::from_str("1.0.0"), Ok(KclVersion::V1));
98 assert_eq!(KclVersion::from_str("2"), Ok(KclVersion::V2));
99 assert_eq!(KclVersion::from_str("2.0.0"), Ok(KclVersion::V2));
100 assert_eq!(KclVersion::from_str("3.0-preview"), Ok(KclVersion::V3Preview));
101 assert_eq!(KclVersion::from_str("3"), Ok(KclVersion::V3));
102 assert_eq!(KclVersion::from_str("3.0"), Ok(KclVersion::V3));
103 assert_eq!(KclVersion::from_str("3.0.0"), Ok(KclVersion::V3));
104 KclVersion::from_str("99.123").unwrap_err();
106 }
107
108 #[test]
109 fn stable_v3_follows_preview() {
110 assert!(KclVersion::V3Preview < KclVersion::V3);
111 }
112}