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