kcl_lib/std/
units.rs

1//! Functions related to unitsematics.
2
3use anyhow::Result;
4use kcl_derive_docs::stdlib;
5
6use crate::{
7    errors::KclError,
8    execution::{types::UnitLen, ExecState, KclValue},
9    std::{args::TyF64, Args},
10};
11
12/// Millimeters conversion factor for current files units.
13pub async fn from_mm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
14    let input = args.get_number_with_type()?;
15    let result = inner_from_mm(input.n, exec_state)?;
16
17    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
18}
19
20/// Converts a number from mm to the current default unit.
21///
22/// No matter what units the current file uses, this function will always return a number equivalent
23/// to the input in millimeters.
24///
25/// For example, if the current file uses inches, `fromMm(1)` will return `1/25.4`.
26/// If the current file uses millimeters, `fromMm(1)` will return `1`.
27///
28/// **Caution**: This function is only intended to be used when you absolutely MUST
29/// have different units in your code than the file settings. Otherwise, it is
30/// a bad pattern to use this function.
31///
32/// We merely provide these functions for convenience and readability, as
33/// `fromMm(10)` is more readable that your intent is "I want 10 millimeters" than
34/// `10 * (1/25.4)`, if the file settings are in inches.
35///
36/// ```no_run
37/// totalWidth = fromMm(10)
38/// ```
39#[stdlib {
40    name = "fromMm",
41    tags = ["units"],
42}]
43fn inner_from_mm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
44    Ok(match exec_state.length_unit() {
45        UnitLen::Mm => input,
46        UnitLen::Inches => measurements::Length::from_millimeters(input).as_inches(),
47        UnitLen::Feet => measurements::Length::from_millimeters(input).as_feet(),
48        UnitLen::M => measurements::Length::from_millimeters(input).as_meters(),
49        UnitLen::Cm => measurements::Length::from_millimeters(input).as_centimeters(),
50        UnitLen::Yards => measurements::Length::from_millimeters(input).as_yards(),
51        UnitLen::Unknown => unreachable!(),
52    })
53}
54
55/// Inches conversion factor for current files units.
56pub async fn from_inches(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
57    let input = args.get_number_with_type()?;
58    let result = inner_from_inches(input.n, exec_state)?;
59
60    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
61}
62
63/// Converts a number from inches to the current default unit.
64///
65/// No matter what units the current file uses, this function will always return a number equivalent
66/// to the input in inches.
67///
68/// For example, if the current file uses inches, `fromInches(1)` will return `1`.
69/// If the current file uses millimeters, `fromInches(1)` will return `25.4`.
70///
71/// **Caution**: This function is only intended to be used when you absolutely MUST
72/// have different units in your code than the file settings. Otherwise, it is
73/// a bad pattern to use this function.
74///
75/// We merely provide these functions for convenience and readability, as
76/// `fromInches(10)` is more readable that your intent is "I want 10 inches" than
77/// `10 * 25.4`, if the file settings are in millimeters.
78///
79/// ```no_run
80/// totalWidth = fromInches(10)
81/// ```
82#[stdlib {
83    name = "fromInches",
84    tags = ["units"],
85}]
86fn inner_from_inches(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
87    match exec_state.length_unit() {
88        UnitLen::Mm => Ok(measurements::Length::from_inches(input).as_millimeters()),
89        UnitLen::Inches => Ok(input),
90        UnitLen::Feet => Ok(measurements::Length::from_inches(input).as_feet()),
91        UnitLen::M => Ok(measurements::Length::from_inches(input).as_meters()),
92        UnitLen::Cm => Ok(measurements::Length::from_inches(input).as_centimeters()),
93        UnitLen::Yards => Ok(measurements::Length::from_inches(input).as_yards()),
94        UnitLen::Unknown => unreachable!(),
95    }
96}
97
98/// Feet conversion factor for current files units.
99pub async fn from_ft(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
100    let input = args.get_number_with_type()?;
101    let result = inner_from_ft(input.n, exec_state)?;
102
103    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
104}
105
106/// Converts a number from feet to the current default unit.
107///
108/// No matter what units the current file uses, this function will always return a number equivalent
109/// to the input in feet.
110///
111/// For example, if the current file uses inches, `fromFt(1)` will return `12`.
112/// If the current file uses millimeters, `fromFt(1)` will return `304.8`.
113/// If the current file uses feet, `fromFt(1)` will return `1`.
114///
115/// **Caution**: This function is only intended to be used when you absolutely MUST
116/// have different units in your code than the file settings. Otherwise, it is
117/// a bad pattern to use this function.
118///
119/// We merely provide these functions for convenience and readability, as
120/// `fromFt(10)` is more readable that your intent is "I want 10 feet" than
121/// `10 * 304.8`, if the file settings are in millimeters.
122///
123/// ```no_run
124/// totalWidth = fromFt(10)
125/// ```
126#[stdlib {
127    name = "fromFt",
128    tags = ["units"],
129}]
130fn inner_from_ft(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
131    match exec_state.length_unit() {
132        UnitLen::Mm => Ok(measurements::Length::from_feet(input).as_millimeters()),
133        UnitLen::Inches => Ok(measurements::Length::from_feet(input).as_inches()),
134        UnitLen::Feet => Ok(input),
135        UnitLen::M => Ok(measurements::Length::from_feet(input).as_meters()),
136        UnitLen::Cm => Ok(measurements::Length::from_feet(input).as_centimeters()),
137        UnitLen::Yards => Ok(measurements::Length::from_feet(input).as_yards()),
138        UnitLen::Unknown => unreachable!(),
139    }
140}
141
142/// Meters conversion factor for current files units.
143pub async fn from_m(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
144    let input = args.get_number_with_type()?;
145    let result = inner_from_m(input.n, exec_state)?;
146
147    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
148}
149
150/// Converts a number from meters to the current default unit.
151///
152/// No matter what units the current file uses, this function will always return a number equivalent
153/// to the input in meters.
154///
155/// For example, if the current file uses inches, `fromM(1)` will return `39.3701`.
156/// If the current file uses millimeters, `fromM(1)` will return `1000`.
157/// If the current file uses meters, `fromM(1)` will return `1`.
158///
159/// **Caution**: This function is only intended to be used when you absolutely MUST
160/// have different units in your code than the file settings. Otherwise, it is
161/// a bad pattern to use this function.
162///
163/// We merely provide these functions for convenience and readability, as
164/// `fromM(10)` is more readable that your intent is "I want 10 meters" than
165/// `10 * 1000`, if the file settings are in millimeters.
166///
167/// ```no_run
168/// totalWidth = 10 * fromM(10)
169/// ```
170#[stdlib {
171    name = "fromM",
172    tags = ["units"],
173}]
174fn inner_from_m(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
175    match exec_state.length_unit() {
176        UnitLen::Mm => Ok(measurements::Length::from_meters(input).as_millimeters()),
177        UnitLen::Inches => Ok(measurements::Length::from_meters(input).as_inches()),
178        UnitLen::Feet => Ok(measurements::Length::from_meters(input).as_feet()),
179        UnitLen::M => Ok(input),
180        UnitLen::Cm => Ok(measurements::Length::from_meters(input).as_centimeters()),
181        UnitLen::Yards => Ok(measurements::Length::from_meters(input).as_yards()),
182        UnitLen::Unknown => unreachable!(),
183    }
184}
185
186/// Centimeters conversion factor for current files units.
187pub async fn from_cm(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
188    let input = args.get_number_with_type()?;
189    let result = inner_from_cm(input.n, exec_state)?;
190
191    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
192}
193
194/// Converts a number from centimeters to the current default unit.
195///
196/// No matter what units the current file uses, this function will always return a number equivalent
197/// to the input in centimeters.
198///
199/// For example, if the current file uses inches, `fromCm(1)` will return `0.393701`.
200/// If the current file uses millimeters, `fromCm(1)` will return `10`.
201/// If the current file uses centimeters, `fromCm(1)` will return `1`.
202///
203/// **Caution**: This function is only intended to be used when you absolutely MUST
204/// have different units in your code than the file settings. Otherwise, it is
205/// a bad pattern to use this function.
206///
207/// We merely provide these functions for convenience and readability, as
208/// `fromCm(10)` is more readable that your intent is "I want 10 centimeters" than
209/// `10 * 10`, if the file settings are in millimeters.
210///
211/// ```no_run
212/// totalWidth = fromCm(10)
213/// ```
214#[stdlib {
215    name = "fromCm",
216    tags = ["units"],
217}]
218fn inner_from_cm(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
219    match exec_state.length_unit() {
220        UnitLen::Mm => Ok(measurements::Length::from_centimeters(input).as_millimeters()),
221        UnitLen::Inches => Ok(measurements::Length::from_centimeters(input).as_inches()),
222        UnitLen::Feet => Ok(measurements::Length::from_centimeters(input).as_feet()),
223        UnitLen::M => Ok(measurements::Length::from_centimeters(input).as_meters()),
224        UnitLen::Cm => Ok(input),
225        UnitLen::Yards => Ok(measurements::Length::from_centimeters(input).as_yards()),
226        UnitLen::Unknown => unreachable!(),
227    }
228}
229
230/// Yards conversion factor for current files units.
231pub async fn from_yd(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
232    let input = args.get_number_with_type()?;
233    let result = inner_from_yd(input.n, exec_state)?;
234
235    Ok(args.make_user_val_from_f64_with_type(TyF64::new(result, exec_state.current_default_units())))
236}
237
238/// Converts a number from yards to the current default unit.
239///
240/// No matter what units the current file uses, this function will always return a number equivalent
241/// to the input in yards.
242///
243/// For example, if the current file uses inches, `fromYd(1)` will return `36`.
244/// If the current file uses millimeters, `fromYd(1)` will return `914.4`.
245/// If the current file uses yards, `fromYd(1)` will return `1`.
246///
247/// **Caution**: This function is only intended to be used when you absolutely MUST
248/// have different units in your code than the file settings. Otherwise, it is
249/// a bad pattern to use this function.
250///
251/// We merely provide these functions for convenience and readability, as
252/// `fromYd(10)` is more readable that your intent is "I want 10 yards" than
253/// `10 * 914.4`, if the file settings are in millimeters.
254///
255/// ```no_run
256/// totalWidth = fromYd(10)
257/// ```
258#[stdlib {
259    name = "fromYd",
260    tags = ["units"],
261}]
262fn inner_from_yd(input: f64, exec_state: &ExecState) -> Result<f64, KclError> {
263    match exec_state.length_unit() {
264        UnitLen::Mm => Ok(measurements::Length::from_yards(input).as_millimeters()),
265        UnitLen::Inches => Ok(measurements::Length::from_yards(input).as_inches()),
266        UnitLen::Feet => Ok(measurements::Length::from_yards(input).as_feet()),
267        UnitLen::M => Ok(measurements::Length::from_yards(input).as_meters()),
268        UnitLen::Cm => Ok(measurements::Length::from_yards(input).as_centimeters()),
269        UnitLen::Yards => Ok(input),
270        UnitLen::Unknown => unreachable!(),
271    }
272}