1#![recursion_limit = "1024"]
6#![allow(clippy::boxed_local)]
7
8#[allow(unused_macros)]
9macro_rules! println {
10 ($($rest:tt)*) => {
11 #[cfg(all(feature = "disable-println", not(test)))]
12 {
13 let _ = format!($($rest)*);
14 }
15 #[cfg(any(not(feature = "disable-println"), test))]
16 std::println!($($rest)*)
17 }
18}
19
20#[allow(unused_macros)]
21macro_rules! eprintln {
22 ($($rest:tt)*) => {
23 #[cfg(all(feature = "disable-println", not(test)))]
24 {
25 let _ = format!($($rest)*);
26 }
27 #[cfg(any(not(feature = "disable-println"), test))]
28 std::eprintln!($($rest)*)
29 }
30}
31
32#[allow(unused_macros)]
33macro_rules! print {
34 ($($rest:tt)*) => {
35 #[cfg(all(feature = "disable-println", not(test)))]
36 {
37 let _ = format!($($rest)*);
38 }
39 #[cfg(any(not(feature = "disable-println"), test))]
40 std::print!($($rest)*)
41 }
42}
43
44#[allow(unused_macros)]
45macro_rules! eprint {
46 ($($rest:tt)*) => {
47 #[cfg(all(feature = "disable-println", not(test)))]
48 {
49 let _ = format!($($rest)*);
50 }
51 #[cfg(any(not(feature = "disable-println"), test))]
52 std::eprint!($($rest)*)
53 }
54}
55#[cfg(feature = "dhat-heap")]
56#[global_allocator]
57static ALLOC: dhat::Alloc = dhat::Alloc;
58
59pub mod collections;
60mod docs;
61mod engine;
62mod errors;
63mod execution;
64mod fmt;
65mod frontend;
66mod fs;
67pub(crate) mod id;
68mod import_format;
69pub mod lint;
70mod log;
71mod lsp_types;
72mod modules;
73pub mod named_views;
74mod parsing;
75mod project;
76mod runtime_flags;
77mod settings;
78#[cfg(test)]
79mod simulation_tests;
80pub mod std;
81#[cfg(not(target_arch = "wasm32"))]
82pub mod test_server;
83mod thread;
84#[doc(hidden)]
85pub mod tooling;
86pub mod unit_conversion;
87mod unparser;
88mod util;
89#[cfg(test)]
90mod variant_name;
91pub mod walk;
92#[cfg(target_arch = "wasm32")]
93mod wasm;
94
95#[doc(hidden)]
97pub mod lsp_support {
98 pub mod docs {
99 pub mod kcl_doc {
100 pub use crate::docs::kcl_doc::ArgData;
101 pub use crate::docs::kcl_doc::DocData;
102 pub use crate::docs::kcl_doc::ModData;
103 pub use crate::docs::kcl_doc::walk_stdlib;
104 }
105 }
106
107 pub mod engine {
108 #[cfg(not(target_arch = "wasm32"))]
109 pub use crate::engine::new_zoo_client;
110 }
111
112 pub mod errors {
113 pub use crate::errors::Severity;
114 pub use crate::errors::Suggestion;
115 }
116
117 pub mod execution {
118 pub use crate::execution::ExecutorContext;
119 pub use crate::execution::MockConfig;
120
121 pub mod cache {
122 pub use crate::execution::cache::read_old_memory_var;
123 }
124
125 pub mod typed_path {
126 pub use crate::execution::typed_path::TypedPath;
127 }
128 }
129
130 pub mod fs {
131 pub use crate::fs::FileManager;
132 pub use crate::fs::FileSystemHandle;
133 pub use crate::fs::new_file_system_handle;
134
135 #[cfg(all(target_arch = "wasm32", not(test)))]
136 pub mod wasm {
137 pub use crate::fs::wasm::FileSystemManager;
138 }
139 }
140
141 pub mod parsing {
142 pub use crate::parsing::PIPE_OPERATOR;
143 pub use crate::parsing::parse_str;
144 pub use crate::parsing::parse_tokens;
145
146 pub mod ast {
147 pub mod types {
148 pub use crate::parsing::ast::types::*;
149 }
150 }
151
152 pub mod token {
153 pub use crate::parsing::token::RESERVED_WORDS;
154 pub use crate::parsing::token::TokenStream;
155 pub use crate::parsing::token::lex;
156
157 pub mod adapter {
158 pub use crate::parsing::token::adapter::lex_with_diagnostics;
159 }
160 }
161 }
162}
163
164pub use engine::AsyncTasks;
165pub use engine::EngineBatchContext;
166pub use engine::EngineStats;
167pub use errors::BacktraceItem;
168pub use errors::BacktraceItemKind;
169pub use errors::CompilationIssue;
170pub use errors::CompilationIssueReport;
171pub use errors::ConnectionError;
172pub use errors::ExecError;
173pub use errors::IsRetryable;
174pub use errors::KclError;
175pub use errors::KclErrorWithOutputs;
176pub use errors::Report;
177pub use errors::ReportWithOutputs;
178pub use errors::render_compilation_issue_miette;
179pub use execution::ConstraintKind;
180pub use execution::EdgeRefactorMeta;
181pub use execution::EnvironmentRef;
182pub use execution::ExecOutcome;
183pub use execution::ExecState;
184pub use execution::ExecutionCallbacks;
185pub use execution::ExecutorContext;
186pub use execution::ExecutorSettings;
187pub use execution::KclValueView;
188pub use execution::KclVersion;
189pub use execution::LegacyAngleRefactorMeta;
190pub use execution::MetaSettings;
191pub use execution::MockConfig;
192pub use execution::OperationCallbackArgs;
193pub use execution::Point2d;
194pub use execution::RefactorMetadata;
195pub use execution::SegmentDragAnchor;
196pub use execution::SketchConstraintReport;
197pub use execution::SketchConstraintStatus;
198pub use execution::bust_cache;
199pub use execution::clear_mem_cache;
200pub use execution::kcl_value::is_kcl_version_available;
201pub use execution::typed_path::TypedPath;
202pub use fs::FileSystem;
203pub use fs::FileSystemHandle;
204pub use fs::in_memory::InMemoryFiles;
205pub use fs::new_file_system_handle;
206pub use kcl_error;
207pub use kcl_error::SourceRange;
208pub use lsp_types::IntoDiagnostic;
209pub use lsp_types::LspSuggestion;
210pub use lsp_types::ToLspRange;
211pub use modules::ModuleId;
212pub use parsing::ast::types::FormatOptions;
213pub use parsing::ast::types::NodePath;
214pub use parsing::ast::types::NodePathExt;
215pub use parsing::ast::types::Program as AstProgram;
216pub use parsing::ast::types::Step as NodePathStep;
217pub use project::ProjectManager;
218pub use runtime_flags::KclRuntimeFlags;
219pub use runtime_flags::RuntimeFlag;
220pub use runtime_flags::kcl_runtime_flags;
221pub use runtime_flags::set_kcl_runtime_flags;
222pub use settings::types::Configuration;
223pub use settings::types::project::ProjectConfiguration;
224#[cfg(not(target_arch = "wasm32"))]
225pub use unparser::recast_dir;
226#[cfg(not(target_arch = "wasm32"))]
227pub use unparser::walk_dir;
228
229pub mod engine_connection {
230 pub use crate::engine::engine_manager::EngineManager;
231 pub use crate::engine::engine_manager::EngineTransport;
232 pub use crate::engine::engine_manager::ResponseInformation;
233 pub use crate::engine::engine_manager::SocketHealth;
234 pub use crate::engine::engine_manager::TransportCloseError;
235}
236
237pub mod exec {
240 pub use kcl_api::NumericType;
241 pub use kcl_api::UnitAngle;
242 pub use kcl_api::UnitLength;
243 pub use kcl_api::UnitType;
244
245 pub use crate::execution::ArtifactCommand;
246 pub use crate::execution::DefaultPlanes;
247 pub use crate::execution::IdGenerator;
248 pub use crate::execution::KclObjectKind;
249 pub use crate::execution::KclValue;
250 pub use crate::execution::KclValueView;
251 pub use crate::execution::Operation;
252 pub use crate::execution::PlaneKind;
253 pub use crate::execution::Sketch;
254 pub use crate::execution::annotations::WarningLevel;
255 pub use crate::util::RetryConfig;
256 pub use crate::util::execute_with_retries;
257}
258
259#[cfg(target_arch = "wasm32")]
260pub mod wasm_engine {
261 pub use crate::engine::conn_wasm::EngineCommandManager;
262 pub use crate::engine::conn_wasm::EngineConnection;
263 pub use crate::engine::conn_wasm::ResponseContext;
264 pub use crate::fs::wasm::FileManager;
265 pub use crate::fs::wasm::FileSystemManager;
266}
267
268pub mod std_utils {
269 pub use crate::std::utils::TangentialArcInfoInput;
270 pub use crate::std::utils::get_tangential_arc_to_info;
271 pub use crate::std::utils::is_points_ccw_wasm;
272 pub use crate::std::utils::untyped_point_to_unit;
273}
274
275pub mod pretty {
276 pub use crate::fmt::format_number_literal;
277 pub use crate::fmt::format_number_value;
278 pub use crate::fmt::human_display_number;
279}
280
281pub mod front {
282 pub use crate::frontend::EditConstraintOptions;
283 pub use crate::frontend::EditDistanceConstraintLabelPositionOptions;
284 pub use crate::frontend::EditSegmentsOptions;
285 pub use crate::frontend::FrontendState;
286 pub use crate::frontend::MAX_SKETCH_CHECKPOINTS;
287 pub use crate::frontend::SetProgramOutcome;
288 pub use crate::frontend::api::EditSketchOutcome;
289 pub use crate::frontend::api::Error;
290 pub use crate::frontend::api::File;
291 pub use crate::frontend::api::FileId;
292 pub use crate::frontend::api::LifecycleApi;
293 pub use crate::frontend::api::NewSketchOutcome;
294 pub use crate::frontend::api::NumberExt;
295 pub use crate::frontend::api::ObjectKindExt;
296 pub use crate::frontend::api::ProjectId;
297 pub use crate::frontend::api::RestoreSketchCheckpointOutcome;
298 pub use crate::frontend::api::Result;
299 pub use crate::frontend::api::SceneGraph;
300 pub use crate::frontend::api::SceneGraphDelta;
301 pub use crate::frontend::api::Settings;
302 pub use crate::frontend::api::SketchCheckpointId;
303 pub use crate::frontend::api::SketchMutationOutcome;
304 pub use crate::frontend::api::SourceDelta;
305 pub use crate::frontend::api::Version;
306 pub(crate) use crate::frontend::modify::find_defined_names;
307 pub(crate) use crate::frontend::modify::next_free_name_using_max;
308 pub use crate::frontend::sketch::ArcExt;
309 pub use crate::frontend::sketch::CircleExt;
310 pub use crate::frontend::sketch::CoincidentExt;
311 pub use crate::frontend::sketch::ConstraintLabelPositionEdit;
312 pub use crate::frontend::sketch::ControlPointSplineExt;
313 pub use crate::frontend::sketch::DistanceExt;
314 pub use crate::frontend::sketch::ExecResult;
315 pub use crate::frontend::sketch::ExistingSegmentCtor;
316 pub use crate::frontend::sketch::LineExt;
317 pub use crate::frontend::sketch::NewSegmentInfo;
318 pub use crate::frontend::sketch::PointExt;
319 pub use crate::frontend::sketch::SegmentExt;
320 pub use crate::frontend::sketch::SketchApi;
321 pub use crate::frontend::sketch::StartOrEnd;
322 pub use crate::frontend::trim::ArcPoint;
323 pub use crate::frontend::trim::AttachToEndpoint;
324 pub use crate::frontend::trim::CoincidentData;
325 pub use crate::frontend::trim::ConstraintToMigrate;
326 pub use crate::frontend::trim::Coords2d;
327 pub use crate::frontend::trim::EndpointChanged;
328 pub use crate::frontend::trim::LineEndpoint;
329 pub use crate::frontend::trim::TrimDirection;
330 pub use crate::frontend::trim::TrimItem;
331 pub use crate::frontend::trim::TrimOperation;
332 pub use crate::frontend::trim::TrimTermination;
333 pub use crate::frontend::trim::TrimTerminations;
334 pub use crate::frontend::trim::execute_trim_loop_with_context;
335 pub use crate::frontend::trim::get_next_trim_spawn;
336 pub use crate::frontend::trim::get_position_coords_for_line;
337 pub use crate::frontend::trim::get_position_coords_from_arc;
338 pub use crate::frontend::trim::is_point_on_line_segment;
339 pub use crate::frontend::trim::line_segment_intersection;
340 pub use crate::frontend::trim::perpendicular_distance_to_segment;
341 pub use crate::frontend::trim::project_point_onto_arc;
342 pub use crate::frontend::trim::project_point_onto_segment;
343}
344
345use serde::Deserialize;
346use serde::Serialize;
347
348use crate::exec::WarningLevel;
349#[allow(unused_imports)]
350use crate::log::log;
351#[allow(unused_imports)]
352use crate::log::logln;
353
354lazy_static::lazy_static! {
355
356 pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
357 import_format::IMPORT_FILE_EXTENSION_FORMATS
358 .iter()
359 .map(|(extension, _)| (*extension).to_owned())
360 .collect()
361 };
362
363 pub static ref RELEVANT_FILE_EXTENSIONS: Vec<String> = {
364 let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
365 relevant_extensions.push("kcl".to_string());
366 relevant_extensions.push("md".to_string());
367 relevant_extensions
368 };
369}
370
371#[derive(Clone, Debug, PartialEq, Serialize)]
372pub struct Program {
373 pub kcl_version: KclVersion,
374 #[serde(flatten)]
375 pub ast: parsing::ast::types::Node<parsing::ast::types::Program>,
376 #[serde(skip)]
380 pub original_file_contents: String,
381}
382
383impl<'de> Deserialize<'de> for Program {
384 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
385 where
386 D: serde::Deserializer<'de>,
387 {
388 #[derive(Deserialize)]
389 struct ProgramHelper {
390 #[serde(flatten)]
391 ast: parsing::ast::types::Node<parsing::ast::types::Program>,
392 }
393
394 let program = ProgramHelper::deserialize(deserializer)?;
395 let kcl_version = execution::computed_kcl_version(&program.ast);
399
400 Ok(Self {
401 kcl_version,
402 ast: program.ast,
403 original_file_contents: String::new(),
404 })
405 }
406}
407
408impl Program {
409 pub fn parse(input: &str) -> Result<(Option<Program>, Vec<CompilationIssue>), KclError> {
410 let module_id = ModuleId::default();
411 let (program, errs) = parsing::parse_str(input, module_id).0?;
412
413 Ok((
414 program.map(|program| Program {
415 kcl_version: program.kcl_version,
416 ast: program.ast,
417 original_file_contents: input.to_string(),
418 }),
419 errs,
420 ))
421 }
422
423 pub fn parse_no_errs(input: &str) -> Result<Program, KclError> {
424 let module_id = ModuleId::default();
425 let program = parsing::parse_str(input, module_id).parse_errs_as_err()?;
426
427 Ok(Program {
428 kcl_version: program.kcl_version,
429 ast: program.ast,
430 original_file_contents: input.to_string(),
431 })
432 }
433
434 pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
435 self.ast.compute_digest()
436 }
437
438 pub fn meta_settings(&self) -> Result<Option<crate::MetaSettings>, KclError> {
440 self.ast.meta_settings()
441 }
442
443 pub fn language_version(&self) -> Result<KclVersion, KclError> {
446 Ok(self.meta_settings()?.unwrap_or_default().kcl_version)
447 }
448
449 pub fn change_default_units(
451 &self,
452 length_units: Option<kittycad_modeling_cmds::units::UnitLength>,
453 ) -> Result<Self, KclError> {
454 Ok(Self {
455 kcl_version: self.kcl_version,
456 ast: self.ast.change_default_units(length_units)?,
457 original_file_contents: self.original_file_contents.clone(),
458 })
459 }
460
461 pub fn change_kcl_version(&self, kcl_version: Option<KclVersion>) -> Result<Self, KclError> {
462 Ok(Self {
463 kcl_version: kcl_version.unwrap_or_default(),
464 ast: self.ast.change_kcl_version(kcl_version)?,
465 original_file_contents: self.original_file_contents.clone(),
466 })
467 }
468
469 pub fn change_experimental_features(&self, warning_level: Option<WarningLevel>) -> Result<Self, KclError> {
470 Ok(Self {
471 kcl_version: self.kcl_version,
472 ast: self.ast.change_experimental_features(warning_level)?,
473 original_file_contents: self.original_file_contents.clone(),
474 })
475 }
476
477 pub fn is_empty_or_only_settings(&self) -> bool {
478 self.ast.is_empty_or_only_settings()
479 }
480
481 pub fn lint_all(&self) -> Result<Vec<lint::Discovered>, anyhow::Error> {
482 self.ast.lint_all()
483 }
484
485 pub fn lint<'a>(&'a self, rule: impl lint::Rule<'a>) -> Result<Vec<lint::Discovered>, anyhow::Error> {
486 self.ast.lint(rule)
487 }
488
489 pub fn node_path_from_range(&self, cached_body_items: usize, range: SourceRange) -> Option<NodePath> {
490 let module_infos = indexmap::IndexMap::new();
491 let programs = crate::execution::ProgramLookup::new(self.ast.clone(), module_infos);
492 NodePath::from_range(&programs, cached_body_items, range)
493 }
494
495 pub fn fill_node_paths(mut self) -> Program {
501 parsing::ast::types::fill_node_paths(&mut self.ast);
502 self
503 }
504
505 pub fn recast(&self) -> String {
506 self.ast.recast_top(&Default::default(), 0)
508 }
509
510 pub fn recast_with_options(&self, options: &FormatOptions) -> String {
511 self.ast.recast_top(options, 0)
512 }
513
514 pub fn empty() -> Self {
516 Self {
517 kcl_version: KclVersion::default(),
518 ast: parsing::ast::types::Node::no_src(parsing::ast::types::Program::default()),
519 original_file_contents: String::new(),
520 }
521 }
522}
523
524#[inline]
525fn try_f64_to_usize(f: f64) -> Option<usize> {
526 let i = f as usize;
527 if i as f64 == f { Some(i) } else { None }
528}
529
530#[inline]
531fn try_f64_to_u32(f: f64) -> Option<u32> {
532 let i = f as u32;
533 if i as f64 == f { Some(i) } else { None }
534}
535
536#[inline]
537fn try_f64_to_u64(f: f64) -> Option<u64> {
538 let i = f as u64;
539 if i as f64 == f { Some(i) } else { None }
540}
541
542#[inline]
543fn try_f64_to_i64(f: f64) -> Option<i64> {
544 let i = f as i64;
545 if i as f64 == f { Some(i) } else { None }
546}
547
548pub fn version() -> &'static str {
550 env!("CARGO_PKG_VERSION")
551}
552
553#[cfg(test)]
554mod test {
555 use super::*;
556
557 #[test]
558 fn program_deserializes_ast_without_kcl_version() {
559 for code in [
560 "",
561 "x = 1\n",
562 "@settings(defaultLengthUnit = mm)\nx = 1\n",
563 "@settings(kclVersion = 1.0)\nx = 1\n",
564 "@settings(kclVersion = 2.0)\nx = 1\n",
565 "@settings(kclVersion = 3.0)\nx = 1\n",
566 "@settings(kclVersion = \"3.0\")\nx = 1\n",
567 "@settings(kclVersion = \"3.0-preview\")\nx = 1\n",
568 "@settings(kclVersion = \"3-preview\")\nx = 1\n",
569 "@settings(kclVersion = 2.0)\n@settings(kclVersion = \"3.0-preview\")\nx = 1\n",
570 "@settings(kclVersion = \"3.0-preview\")\n@settings(kclVersion = 2.0)\nx = 1\n",
571 "@settings(kclVersion = 2.0)\n@settings(defaultLengthUnit = in)\nx = 1\n",
572 "@settings(kclVersion = 99.0)\nx = 1\n",
573 ] {
574 let parsed = Program::parse_no_errs(code).unwrap();
575 let json = serde_json::to_value(&parsed.ast).unwrap();
577 let deserialized: Program = serde_json::from_str(&json.to_string()).unwrap();
578
579 assert_eq!(deserialized.kcl_version, parsed.kcl_version, "{code}");
580 assert_eq!(deserialized.recast(), parsed.recast(), "{code}");
581 }
582 }
583
584 #[test]
585 fn program_deserialization_recomputes_kcl_version() {
586 for (code, supplied_version, expected_version) in [
587 ("x = 1\n", KclVersion::V2, KclVersion::V1),
588 (
589 "@settings(kclVersion = 2.0)\nx = 1\n",
590 KclVersion::V3Preview,
591 KclVersion::V2,
592 ),
593 (
594 "@settings(kclVersion = \"3.0-preview\")\nx = 1\n",
595 KclVersion::V2,
596 KclVersion::V3Preview,
597 ),
598 (
599 "@settings(kclVersion = 3.0)\nx = 1\n",
600 KclVersion::V3Preview,
601 KclVersion::V3,
602 ),
603 (
604 "@settings(kclVersion = \"3.0-preview\")\nx = 1\n",
605 KclVersion::V3,
606 KclVersion::V3Preview,
607 ),
608 ] {
609 let mut program = Program::parse_no_errs(code).unwrap();
610 program.kcl_version = supplied_version;
611
612 let json = serde_json::to_value(&program).unwrap();
613 let deserialized: Program = serde_json::from_str(&json.to_string()).unwrap();
614
615 assert_eq!(deserialized.kcl_version, expected_version, "{code}");
616 assert_eq!(deserialized.recast(), program.recast(), "{code}");
617 assert!(deserialized.original_file_contents.is_empty());
618 }
619 }
620
621 #[test]
622 fn entry_point_language_version() {
623 for (code, expected) in [
624 ("", KclVersion::V1),
625 ("@settings(defaultLengthUnit = mm)", KclVersion::V1),
626 ("@settings(kclVersion = 2.0)", KclVersion::V2),
627 ("@settings(kclVersion = 3.0)", KclVersion::V3),
628 ("@settings(kclVersion = \"3.0\")", KclVersion::V3),
629 ("@settings(kclVersion = \"3.0-preview\")", KclVersion::V3Preview),
630 ] {
631 assert_eq!(
632 Program::parse_no_errs(code).unwrap().language_version().unwrap(),
633 expected
634 );
635 }
636 let invalid = Program::parse_no_errs("@settings(kclVersion = 99.0)").unwrap();
637 invalid.language_version().unwrap_err();
638 }
639
640 #[test]
641 fn proprietary_file_extensions_use_real_suffixes() {
642 for extension in ["sat", "sab", "catpart", "prt", "ipt", "x_t", "x_b", "sldprt"] {
643 assert!(IMPORT_FILE_EXTENSIONS.iter().any(|candidate| candidate == extension));
644 }
645 }
646
647 #[test]
648 fn convert_int() {
649 assert_eq!(try_f64_to_usize(0.0), Some(0));
650 assert_eq!(try_f64_to_usize(42.0), Some(42));
651 assert_eq!(try_f64_to_usize(0.00000000001), None);
652 assert_eq!(try_f64_to_usize(-1.0), None);
653 assert_eq!(try_f64_to_usize(f64::NAN), None);
654 assert_eq!(try_f64_to_usize(f64::INFINITY), None);
655 assert_eq!(try_f64_to_usize((0.1 + 0.2) * 10.0), None);
656
657 assert_eq!(try_f64_to_u32(0.0), Some(0));
658 assert_eq!(try_f64_to_u32(42.0), Some(42));
659 assert_eq!(try_f64_to_u32(0.00000000001), None);
660 assert_eq!(try_f64_to_u32(-1.0), None);
661 assert_eq!(try_f64_to_u32(f64::NAN), None);
662 assert_eq!(try_f64_to_u32(f64::INFINITY), None);
663 assert_eq!(try_f64_to_u32((0.1 + 0.2) * 10.0), None);
664
665 assert_eq!(try_f64_to_u64(0.0), Some(0));
666 assert_eq!(try_f64_to_u64(42.0), Some(42));
667 assert_eq!(try_f64_to_u64(0.00000000001), None);
668 assert_eq!(try_f64_to_u64(-1.0), None);
669 assert_eq!(try_f64_to_u64(f64::NAN), None);
670 assert_eq!(try_f64_to_u64(f64::INFINITY), None);
671 assert_eq!(try_f64_to_u64((0.1 + 0.2) * 10.0), None);
672
673 assert_eq!(try_f64_to_i64(0.0), Some(0));
674 assert_eq!(try_f64_to_i64(42.0), Some(42));
675 assert_eq!(try_f64_to_i64(0.00000000001), None);
676 assert_eq!(try_f64_to_i64(-1.0), Some(-1));
677 assert_eq!(try_f64_to_i64(f64::NAN), None);
678 assert_eq!(try_f64_to_i64(f64::INFINITY), None);
679 assert_eq!(try_f64_to_i64((0.1 + 0.2) * 10.0), None);
680 }
681}