rs_teststand/license/license_type.rs
1//! What license the engine is currently running on.
2
3/// The kind of license the engine is using (`Engine.LicenseType`).
4///
5/// Where several are activated on one machine, the engine picks the minimum
6/// that satisfies the request rather than the most capable, so this reports
7/// what is actually in use and not what is installed.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9#[non_exhaustive]
10pub enum LicenseType {
11 /// Full development system.
12 DevelopmentSystem,
13 /// Debug deployment environment.
14 DebugDeploymentEnv,
15 /// Base deployment engine.
16 BaseDeploymentEngine,
17 /// An OEM license.
18 Oem,
19 /// An evaluation license, which expires.
20 Evaluation,
21 /// No license at all.
22 ///
23 /// The state a host has to survive: the engine object may still exist, but
24 /// anything needing a license will refuse.
25 NoLicense,
26 /// A temporary license, taken when the volume license server is
27 /// unreachable. Worth logging: it means the station could not reach the
28 /// server, so it will need to later.
29 Temporary,
30 /// The engine could not determine the type.
31 Other,
32 /// Custom sequence editor deployment.
33 CustomEditorDeployment,
34}
35
36impl LicenseType {
37 /// Maps the engine's number onto a type.
38 ///
39 /// # Errors
40 /// The raw value, when it is one this build does not name. A newer engine
41 /// may define a license type this crate predates, and reporting the number
42 /// is more useful than guessing which existing one it resembles.
43 pub const fn from_bits(bits: i32) -> Result<Self, i32> {
44 match bits {
45 1 => Ok(Self::DevelopmentSystem),
46 2 => Ok(Self::DebugDeploymentEnv),
47 3 => Ok(Self::BaseDeploymentEngine),
48 4 => Ok(Self::Oem),
49 5 => Ok(Self::Evaluation),
50 6 => Ok(Self::NoLicense),
51 7 => Ok(Self::Temporary),
52 8 => Ok(Self::Other),
53 9 => Ok(Self::CustomEditorDeployment),
54 unknown => Err(unknown),
55 }
56 }
57
58 /// The engine's number for this type.
59 #[must_use]
60 pub const fn bits(self) -> i32 {
61 match self {
62 Self::DevelopmentSystem => 1,
63 Self::DebugDeploymentEnv => 2,
64 Self::BaseDeploymentEngine => 3,
65 Self::Oem => 4,
66 Self::Evaluation => 5,
67 Self::NoLicense => 6,
68 Self::Temporary => 7,
69 Self::Other => 8,
70 Self::CustomEditorDeployment => 9,
71 }
72 }
73
74 /// Whether this license lets a host expect to run sequences.
75 ///
76 /// False for [`NoLicense`](Self::NoLicense) only. Everything else grants
77 /// something, though not necessarily everything a caller wants, an
78 /// evaluation license expires and a base deployment engine cannot edit, so
79 /// this is a floor, not a guarantee.
80 #[must_use]
81 pub const fn is_usable(self) -> bool {
82 !matches!(self, Self::NoLicense)
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::LicenseType;
89
90 #[test]
91 fn every_documented_value_round_trips() {
92 for raw in 1..=9 {
93 let parsed = LicenseType::from_bits(raw);
94 assert_eq!(
95 parsed.map(LicenseType::bits),
96 Ok(raw),
97 "{raw} is documented but did not round-trip"
98 );
99 }
100 }
101
102 #[test]
103 fn an_unknown_value_is_returned_rather_than_guessed() {
104 // A newer engine may name a type this build predates. Reporting the
105 // number lets a caller say so; picking a near neighbour would lie.
106 assert_eq!(LicenseType::from_bits(99), Err(99));
107 assert_eq!(LicenseType::from_bits(0), Err(0));
108 }
109
110 #[test]
111 fn only_no_license_is_unusable() {
112 // The distinction a host branches on, so it is pinned.
113 assert!(!LicenseType::NoLicense.is_usable());
114 for usable in [
115 LicenseType::DevelopmentSystem,
116 LicenseType::DebugDeploymentEnv,
117 LicenseType::BaseDeploymentEngine,
118 LicenseType::Oem,
119 LicenseType::Evaluation,
120 LicenseType::Temporary,
121 LicenseType::Other,
122 LicenseType::CustomEditorDeployment,
123 ] {
124 assert!(usable.is_usable(), "{usable:?} should count as usable");
125 }
126 }
127}