Skip to main content

kittycad_modeling_cmds/
kcl_version.rs

1use std::str::FromStr;
2
3use schemars::JsonSchema;
4use serde::Deserialize;
5use serde::Serialize;
6
7/// Which KCL versions does Zoo support?
8#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, PartialEq, Eq, Ord, PartialOrd, JsonSchema)]
9#[cfg_attr(feature = "ts-rs", derive(ts_rs::TS))]
10#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
11#[cfg_attr(feature = "ts-rs", ts(export))]
12pub enum KclVersion {
13    /// Original KCL released in 2025
14    #[default]
15    #[serde(rename = "1.0")]
16    V1,
17    /// KCL v2 is the same as KCL v1, except
18    /// that it supports the `region` function.
19    #[serde(rename = "2.0")]
20    V2,
21    /// KCL v3 preview -- used while developing version 3.
22    #[serde(rename = "3.0-preview")]
23    V3Preview,
24    /// KCL v3 releases 2026
25    #[serde(rename = "3.0")]
26    V3,
27    /// KCL v4 preview -- used while developing and testing version 4.
28    #[serde(rename = "4.0-preview")]
29    V4Preview,
30    // When you add a new version, please add it to the error string in KclVersionError's
31    // as_str() and FromStr impls.
32}
33
34impl KclVersion {
35    /// Get the canonical string representation for each version.
36    pub fn as_str(self) -> &'static str {
37        match self {
38            Self::V1 => "1.0",
39            Self::V2 => "2.0",
40            Self::V3Preview => "3.0-preview",
41            Self::V3 => "3.0",
42            Self::V4Preview => "4.0-preview",
43        }
44    }
45}
46
47/// Error returned when user tries to use an invalid KCL version.
48#[derive(Debug)]
49pub struct InvalidKclVersion;
50
51impl core::error::Error for InvalidKclVersion {}
52
53impl std::fmt::Display for InvalidKclVersion {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        write!(
56            f,
57            "Unrecognized version. Valid versions are 1.0, 2.0, 3.0 and (experimentally) 3.0-preview"
58        )
59    }
60}
61
62impl FromStr for KclVersion {
63    type Err = InvalidKclVersion;
64
65    fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
66        match s {
67            "1" | "1.0" | "1.0.0" => Ok(Self::V1),
68            "2" | "2.0" | "2.0.0" => Ok(Self::V2),
69            "3-preview" | "3.0-preview" | "3.0.0-preview" => Ok(Self::V3Preview),
70            "3" | "3.0" | "3.0.0" => Ok(Self::V3),
71            "4-preview" | "4.0-preview" | "4.0.0-preview" => Ok(Self::V4Preview),
72            _other => Err(InvalidKclVersion),
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn roundtrip_str() {
83        for input in [
84            KclVersion::V1,
85            KclVersion::V2,
86            KclVersion::V3Preview,
87            KclVersion::V3,
88            KclVersion::V4Preview,
89        ] {
90            let serialized = input.as_str();
91            let deserialized: KclVersion = serialized.parse().unwrap();
92            assert_eq!(input, deserialized);
93        }
94    }
95}