1use serde::Serialize;
2
3use crate::{execution::types::NumericType, pretty::NumericSuffix};
4
5pub fn human_display_number(value: f64, ty: NumericType) -> String {
8 match ty {
9 NumericType::Known(unit_type) => format!("{value}: number({unit_type})"),
10 NumericType::Default { len, angle } => format!("{value} (no units, defaulting to {len} or {angle})"),
11 NumericType::Unknown => format!("{value} (number with unknown units)"),
12 NumericType::Any => format!("{value} (number with any units)"),
13 }
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, thiserror::Error)]
17#[serde(tag = "type")]
18pub enum FormatNumericSuffixError {
19 #[error("Invalid numeric suffix: {0}")]
20 Invalid(NumericSuffix),
21}
22
23pub fn format_number_literal(value: f64, suffix: NumericSuffix) -> Result<String, FormatNumericSuffixError> {
28 match suffix {
29 NumericSuffix::Length | NumericSuffix::Angle | NumericSuffix::Unknown => {
33 Err(FormatNumericSuffixError::Invalid(suffix))
34 }
35 NumericSuffix::None
36 | NumericSuffix::Count
37 | NumericSuffix::Mm
38 | NumericSuffix::Cm
39 | NumericSuffix::M
40 | NumericSuffix::Inch
41 | NumericSuffix::Ft
42 | NumericSuffix::Yd
43 | NumericSuffix::Deg
44 | NumericSuffix::Rad => Ok(format!("{value}{suffix}")),
45 }
46}
47
48#[derive(Debug, Clone, PartialEq, Serialize, thiserror::Error)]
49#[serde(tag = "type")]
50pub enum FormatNumericTypeError {
51 #[error("Invalid numeric type: {0:?}")]
52 Invalid(NumericType),
53}
54
55pub fn format_number_value(value: f64, ty: NumericType) -> Result<String, FormatNumericTypeError> {
60 match ty {
61 NumericType::Default { .. } => Ok(value.to_string()),
62 NumericType::Unknown | NumericType::Any => Err(FormatNumericTypeError::Invalid(ty)),
66 NumericType::Known(unit_type) => unit_type
67 .to_suffix()
68 .map(|suffix| format!("{value}{suffix}"))
69 .ok_or(FormatNumericTypeError::Invalid(ty)),
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use kittycad_modeling_cmds::units::{UnitAngle, UnitLength};
76 use pretty_assertions::assert_eq;
77
78 use super::*;
79 use crate::execution::types::UnitType;
80
81 #[test]
82 fn test_human_display_number() {
83 assert_eq!(
84 human_display_number(1.0, NumericType::Known(UnitType::Count)),
85 "1: number(Count)"
86 );
87 assert_eq!(
88 human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLength::Meters))),
89 "1: number(m)"
90 );
91 assert_eq!(
92 human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLength::Millimeters))),
93 "1: number(mm)"
94 );
95 assert_eq!(
96 human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLength::Inches))),
97 "1: number(in)"
98 );
99 assert_eq!(
100 human_display_number(1.0, NumericType::Known(UnitType::Length(UnitLength::Feet))),
101 "1: number(ft)"
102 );
103 assert_eq!(
104 human_display_number(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Degrees))),
105 "1: number(deg)"
106 );
107 assert_eq!(
108 human_display_number(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Radians))),
109 "1: number(rad)"
110 );
111 assert_eq!(
112 human_display_number(
113 1.0,
114 NumericType::Default {
115 len: UnitLength::Millimeters,
116 angle: UnitAngle::Degrees,
117 }
118 ),
119 "1 (no units, defaulting to mm or deg)"
120 );
121 assert_eq!(
122 human_display_number(
123 1.0,
124 NumericType::Default {
125 len: UnitLength::Feet,
126 angle: UnitAngle::Radians,
127 }
128 ),
129 "1 (no units, defaulting to ft or rad)"
130 );
131 assert_eq!(
132 human_display_number(1.0, NumericType::Unknown),
133 "1 (number with unknown units)"
134 );
135 assert_eq!(human_display_number(1.0, NumericType::Any), "1 (number with any units)");
136 }
137
138 #[test]
139 fn test_format_number_literal() {
140 assert_eq!(
141 format_number_literal(1.0, NumericSuffix::Length),
142 Err(FormatNumericSuffixError::Invalid(NumericSuffix::Length))
143 );
144 assert_eq!(
145 format_number_literal(1.0, NumericSuffix::Angle),
146 Err(FormatNumericSuffixError::Invalid(NumericSuffix::Angle))
147 );
148 assert_eq!(format_number_literal(1.0, NumericSuffix::None), Ok("1".to_owned()));
149 assert_eq!(format_number_literal(1.0, NumericSuffix::Count), Ok("1_".to_owned()));
150 assert_eq!(format_number_literal(1.0, NumericSuffix::Mm), Ok("1mm".to_owned()));
151 assert_eq!(format_number_literal(1.0, NumericSuffix::Cm), Ok("1cm".to_owned()));
152 assert_eq!(format_number_literal(1.0, NumericSuffix::M), Ok("1m".to_owned()));
153 assert_eq!(format_number_literal(1.0, NumericSuffix::Inch), Ok("1in".to_owned()));
154 assert_eq!(format_number_literal(1.0, NumericSuffix::Ft), Ok("1ft".to_owned()));
155 assert_eq!(format_number_literal(1.0, NumericSuffix::Yd), Ok("1yd".to_owned()));
156 assert_eq!(format_number_literal(1.0, NumericSuffix::Deg), Ok("1deg".to_owned()));
157 assert_eq!(format_number_literal(1.0, NumericSuffix::Rad), Ok("1rad".to_owned()));
158 assert_eq!(
159 format_number_literal(1.0, NumericSuffix::Unknown),
160 Err(FormatNumericSuffixError::Invalid(NumericSuffix::Unknown))
161 );
162 }
163
164 #[test]
165 fn test_format_number_value() {
166 assert_eq!(
167 format_number_value(
168 1.0,
169 NumericType::Default {
170 len: UnitLength::Millimeters,
171 angle: UnitAngle::Degrees,
172 }
173 ),
174 Ok("1".to_owned())
175 );
176 assert_eq!(
177 format_number_value(1.0, NumericType::Known(UnitType::GenericLength)),
178 Err(FormatNumericTypeError::Invalid(NumericType::Known(
179 UnitType::GenericLength
180 )))
181 );
182 assert_eq!(
183 format_number_value(1.0, NumericType::Known(UnitType::GenericAngle)),
184 Err(FormatNumericTypeError::Invalid(NumericType::Known(
185 UnitType::GenericAngle
186 )))
187 );
188 assert_eq!(
189 format_number_value(1.0, NumericType::Known(UnitType::Count)),
190 Ok("1_".to_owned())
191 );
192 assert_eq!(
193 format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLength::Millimeters))),
194 Ok("1mm".to_owned())
195 );
196 assert_eq!(
197 format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLength::Centimeters))),
198 Ok("1cm".to_owned())
199 );
200 assert_eq!(
201 format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLength::Meters))),
202 Ok("1m".to_owned())
203 );
204 assert_eq!(
205 format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLength::Inches))),
206 Ok("1in".to_owned())
207 );
208 assert_eq!(
209 format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLength::Feet))),
210 Ok("1ft".to_owned())
211 );
212 assert_eq!(
213 format_number_value(1.0, NumericType::Known(UnitType::Length(UnitLength::Yards))),
214 Ok("1yd".to_owned())
215 );
216 assert_eq!(
217 format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Degrees))),
218 Ok("1deg".to_owned())
219 );
220 assert_eq!(
221 format_number_value(1.0, NumericType::Known(UnitType::Angle(UnitAngle::Radians))),
222 Ok("1rad".to_owned())
223 );
224 assert_eq!(
225 format_number_value(1.0, NumericType::Unknown),
226 Err(FormatNumericTypeError::Invalid(NumericType::Unknown))
227 );
228 assert_eq!(
229 format_number_value(1.0, NumericType::Any),
230 Err(FormatNumericTypeError::Invalid(NumericType::Any))
231 );
232 }
233}