1use kcl_api::UnitLength;
4
5use crate::SourceRange;
6use crate::errors::KclError;
7use crate::errors::KclErrorDetails;
8use crate::execution::Artifact;
9use crate::execution::ArtifactId;
10use crate::execution::CameraView;
11use crate::execution::CodeRef;
12use crate::execution::ExecState;
13use crate::execution::KclValue;
14use crate::execution::NamedViewValue;
15use crate::execution::Orientation;
16use crate::execution::Point3d;
17use crate::execution::Projection;
18use crate::execution::Visibility;
19use crate::execution::named_view_artifact;
20use crate::execution::types::NumericType;
21use crate::execution::types::NumericTypeExt;
22use crate::execution::types::RuntimeType;
23use crate::std::Args;
24use crate::std::args::TyF64;
25
26pub async fn oriented(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
28 let orientation: Orientation = args.get_unlabeled_kw_arg("orientation", &RuntimeType::any(), exec_state)?;
35 let target: Option<[TyF64; 3]> = args.get_kw_arg_opt("target", &RuntimeType::point3d(), exec_state)?;
36 let distance: Option<TyF64> = args.get_kw_arg_opt("distance", &RuntimeType::length(), exec_state)?;
37 let projection: Option<Projection> = args.get_kw_arg_opt("projection", &RuntimeType::any(), exec_state)?;
38
39 let view = CameraView::oriented(
40 orientation,
41 target.map(millimeter_point),
42 distance.map(millimeter_length),
43 projection,
44 vec![args.source_range.into()],
45 )
46 .map_err(|err| view_error(err, &args))?;
47 Ok(KclValue::CameraView { value: Box::new(view) })
48}
49
50pub async fn directed(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
52 let direction: [TyF64; 3] = args.get_unlabeled_kw_arg("direction", &RuntimeType::point3d(), exec_state)?;
59 let up: Option<[TyF64; 3]> = args.get_kw_arg_opt("up", &RuntimeType::point3d(), exec_state)?;
60 let target: Option<[TyF64; 3]> = args.get_kw_arg_opt("target", &RuntimeType::point3d(), exec_state)?;
61 let distance: Option<TyF64> = args.get_kw_arg_opt("distance", &RuntimeType::length(), exec_state)?;
62 let projection: Option<Projection> = args.get_kw_arg_opt("projection", &RuntimeType::any(), exec_state)?;
63
64 let view = CameraView::directed(
65 unitless_direction(direction),
66 up.map(unitless_direction),
67 target.map(millimeter_point),
68 distance.map(millimeter_length),
69 projection,
70 vec![args.source_range.into()],
71 )
72 .map_err(|err| view_error(err, &args))?;
73 Ok(KclValue::CameraView { value: Box::new(view) })
74}
75
76pub async fn named(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
78 let name: String = args.get_unlabeled_kw_arg("name", &RuntimeType::string(), exec_state)?;
81 let camera: CameraView = args.get_kw_arg("camera", &RuntimeType::any(), exec_state)?;
82 let baseline: Visibility = args.get_kw_arg("baseline", &RuntimeType::any(), exec_state)?;
83 let except: Option<Vec<KclValue>> = args.get_kw_arg_opt("except", &RuntimeType::any(), exec_state)?;
84
85 let except_ids = except
86 .as_ref()
87 .map(|objects| except_artifact_ids(objects, args.source_range))
88 .transpose()?;
89
90 let artifact_id = exec_state.next_artifact_id();
93 let view = NamedViewValue::new(
94 artifact_id,
95 name,
96 camera,
97 baseline,
98 except_ids,
99 args.source_range.module_id(),
100 exec_state.registered_named_views(),
101 vec![args.source_range.into()],
102 )
103 .map_err(|err| view_error(err, &args))?;
104 exec_state.add_artifact(Artifact::NamedView(named_view_artifact(
107 &view,
108 CodeRef::placeholder(args.source_range),
109 )));
110
111 Ok(KclValue::NamedView { value: Box::new(view) })
112}
113
114fn except_artifact_ids(objects: &[KclValue], source_range: SourceRange) -> Result<Vec<ArtifactId>, KclError> {
121 objects
122 .iter()
123 .map(|object| match object {
124 KclValue::Solid { value } => Ok(value.artifact_id),
125 KclValue::Sketch { value } => Ok(value.artifact_id),
126 KclValue::GdtAnnotation { value } => Ok(ArtifactId::new(value.id)),
127 KclValue::Helix { value } => Ok(value.artifact_id),
128 KclValue::Plane { value } => {
129 if value.is_standard() || value.is_uninitialized() {
130 Err(KclError::new_semantic(KclErrorDetails::new(
131 "Named views cannot control default planes or other uninitialized planes because their ids do not identify independent engine objects. Use a standalone plane returned by `offsetPlane()`."
132 .to_owned(),
133 vec![source_range],
134 )))
135 } else {
136 Ok(value.artifact_id)
137 }
138 }
139 KclValue::ImportedGeometry(value) => Ok(ArtifactId::new(value.id)),
140 other => Err(KclError::new_internal(KclErrorDetails::new(
141 format!(
142 "`except` cannot hold {}; the declared signature should have rejected it",
143 other.human_friendly_type()
144 ),
145 vec![source_range],
146 ))),
147 })
148 .collect()
149}
150
151fn view_error<E: std::fmt::Display>(err: E, args: &Args) -> KclError {
159 KclError::new_semantic(KclErrorDetails::new(err.to_string(), vec![args.source_range]))
160}
161
162fn millimeter_point([x, y, z]: [TyF64; 3]) -> Point3d {
180 Point3d {
181 x: x.to_mm(),
182 y: y.to_mm(),
183 z: z.to_mm(),
184 units: Some(UnitLength::Millimeters),
185 }
186}
187
188fn millimeter_length(length: TyF64) -> TyF64 {
190 TyF64::new(length.to_mm(), NumericType::mm())
191}
192
193fn unitless_direction([x, y, z]: [TyF64; 3]) -> Point3d {
200 Point3d {
201 x: x.to_mm(),
202 y: y.to_mm(),
203 z: z.to_mm(),
204 units: None,
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use crate::execution::ArtifactId;
211 use crate::execution::KclValue;
212 use crate::execution::Visibility;
213 use crate::execution::parse_execute;
214
215 const TWO_SOLIDS: &str = r#"@settings(kclVersion = "3.0-preview", experimentalFeatures = allow)
220
221plateSketch = sketch(on = XY) {
222 edge1 = line(start = [var 0mm, var 0mm], end = [var 40mm, var 0mm])
223 edge2 = line(start = [var 40mm, var 0mm], end = [var 40mm, var 20mm])
224 edge3 = line(start = [var 40mm, var 20mm], end = [var 0mm, var 20mm])
225 edge4 = line(start = [var 0mm, var 20mm], end = [var 0mm, var 0mm])
226 coincident([edge1.end, edge2.start])
227 coincident([edge2.end, edge3.start])
228 coincident([edge3.end, edge4.start])
229 coincident([edge4.end, edge1.start])
230}
231plateRegion = region(point = [20mm, 10mm], sketch = plateSketch)
232plate = extrude(plateRegion, length = 5mm)
233
234bossSketch = sketch(on = XY) {
235 edge1 = line(start = [var 50mm, var 0mm], end = [var 60mm, var 0mm])
236 edge2 = line(start = [var 60mm, var 0mm], end = [var 60mm, var 10mm])
237 edge3 = line(start = [var 60mm, var 10mm], end = [var 50mm, var 10mm])
238 edge4 = line(start = [var 50mm, var 10mm], end = [var 50mm, var 0mm])
239 coincident([edge1.end, edge2.start])
240 coincident([edge2.end, edge3.start])
241 coincident([edge3.end, edge4.start])
242 coincident([edge4.end, edge1.start])
243}
244bossRegion = region(point = [55mm, 5mm], sketch = bossSketch)
245boss = extrude(bossRegion, length = 8mm)
246"#;
247
248 #[tokio::test(flavor = "multi_thread")]
251 async fn named_shows_everything_under_a_show_baseline() {
252 let program = format!(
253 "{TWO_SOLIDS}\nv = view::named(\n \"Overview\",\n camera = view::oriented(view::Orientation::Isometric),\n baseline = view::Visibility::Show,\n)\n"
254 );
255 let result = parse_execute(&program).await.expect("the program executes");
256
257 let KclValue::NamedView { value } = result.variable("v") else {
258 panic!("`v` is not a named view");
259 };
260 assert_eq!(value.name(), "Overview");
261 assert_eq!(value.baseline(), Visibility::Show);
262 assert!(value.except_ids().is_empty());
263 }
264
265 #[tokio::test(flavor = "multi_thread")]
269 async fn named_excepts_the_objects_it_is_given() {
270 let program = format!(
271 "{TWO_SOLIDS}\nv = view::named(\n \"Plate only\",\n camera = view::oriented(view::Orientation::Front),\n baseline = view::Visibility::Hide,\n except = [plate, boss, plate],\n)\n"
272 );
273 let result = parse_execute(&program).await.expect("the program executes");
274
275 let KclValue::Solid { value: plate } = result.variable("plate") else {
276 panic!("`plate` is not a solid");
277 };
278 let KclValue::Solid { value: boss } = result.variable("boss") else {
279 panic!("`boss` is not a solid");
280 };
281 let KclValue::NamedView { value } = result.variable("v") else {
282 panic!("`v` is not a named view");
283 };
284
285 assert_eq!(value.baseline(), Visibility::Hide);
286 assert_eq!(value.except_ids().to_vec(), vec![plate.artifact_id, boss.artifact_id]);
288 }
289
290 #[tokio::test(flavor = "multi_thread")]
295 async fn named_excepts_more_than_one_kind() {
296 let program = format!(
297 "{TWO_SOLIDS}\nnote = gdt::note(note = \"Machine after welding\")\nv = view::named(\n \"Mixed\",\n camera = view::oriented(view::Orientation::Top),\n baseline = view::Visibility::Hide,\n except = [plate, bossRegion, note],\n)\n"
298 );
299 let result = parse_execute(&program).await.expect("the program executes");
300
301 let KclValue::Solid { value: plate } = result.variable("plate") else {
302 panic!("`plate` is not a solid");
303 };
304 let KclValue::Sketch { value: sketch } = result.variable("bossRegion") else {
308 panic!("`bossRegion` is not a sketch");
309 };
310 let KclValue::GdtAnnotation { value: note } = result.variable("note") else {
311 panic!("`note` is not an annotation");
312 };
313 let KclValue::NamedView { value } = result.variable("v") else {
314 panic!("`v` is not a named view");
315 };
316
317 assert_eq!(
318 value.except_ids().to_vec(),
319 vec![plate.artifact_id, sketch.artifact_id, ArtifactId::new(note.id)]
320 );
321 }
322
323 #[tokio::test(flavor = "multi_thread")]
327 async fn named_excepts_a_custom_plane_and_helix_by_their_artifact_ids() {
328 let program = r#"@settings(kclVersion = "3.0-preview", experimentalFeatures = allow)
329inspectionPlane = offsetPlane(XY, offset = 20mm)
330spring = helix(
331 axis = Z,
332 radius = 5mm,
333 length = 20mm,
334 revolutions = 4,
335 angleStart = 0deg,
336)
337v = view::named(
338 "Construction geometry",
339 camera = view::oriented(view::Orientation::Isometric),
340 baseline = view::Visibility::Hide,
341 except = [inspectionPlane, spring],
342)
343"#;
344 let result = parse_execute(program).await.expect("the program executes");
345
346 let KclValue::Plane { value: plane } = result.variable("inspectionPlane") else {
347 panic!("`inspectionPlane` is not a plane");
348 };
349 let KclValue::Helix { value: helix } = result.variable("spring") else {
350 panic!("`spring` is not a helix");
351 };
352 let KclValue::NamedView { value } = result.variable("v") else {
353 panic!("`v` is not a named view");
354 };
355
356 assert!(plane.is_initialized());
357 assert_eq!(value.except_ids().to_vec(), vec![plane.artifact_id, helix.artifact_id]);
358 }
359
360 #[test]
363 fn named_excepts_imported_geometry_by_its_artifact_id() {
364 let id = uuid::Uuid::from_u128(1);
365 let imported = KclValue::ImportedGeometry(crate::execution::ImportedGeometry::new(
366 id,
367 vec!["part.step".to_owned()],
368 Vec::new(),
369 ));
370
371 assert_eq!(
372 super::except_artifact_ids(&[imported], crate::SourceRange::default()).expect("the value is accepted"),
373 vec![ArtifactId::new(id)]
374 );
375 }
376
377 async fn execution_error(code: &str) -> String {
387 let program = format!("@settings(kclVersion = \"3.0-preview\")\n{code}");
388 match parse_execute(&program).await {
389 Ok(_) => panic!("expected `{code}` to be rejected, but it executed"),
390 Err(err) => err.message().to_owned(),
391 }
392 }
393
394 #[tokio::test(flavor = "multi_thread")]
397 async fn rejected_arguments_report_their_reason() {
398 assert_eq!(
400 execution_error("v = view::directed([1 / 0, 0, 0])").await,
401 "`direction` must have finite coordinates."
402 );
403 assert_eq!(
404 execution_error("v = view::directed([0, 1, 0], up = [0, 0, 1 / 0])").await,
405 "`up` must have finite coordinates."
406 );
407 assert_eq!(
408 execution_error("v = view::directed([0, 1, 0], target = [1 / 0, 0, 0])").await,
409 "`target` must have finite coordinates."
410 );
411 assert_eq!(
412 execution_error("v = view::oriented(view::Orientation::Front, target = [0, 1 / 0, 0])").await,
413 "`target` must have finite coordinates."
414 );
415 assert_eq!(
416 execution_error("v = view::oriented(view::Orientation::Front, distance = 1 / 0)").await,
417 "`distance` must be a finite number."
418 );
419 assert_eq!(
420 execution_error("v = view::directed([0, 1, 0], distance = 0)").await,
421 "`distance` must be greater than zero."
422 );
423 assert_eq!(
424 execution_error("v = view::oriented(view::Orientation::Front, distance = -50)").await,
425 "`distance` must be greater than zero."
426 );
427 assert_eq!(
428 execution_error("v = view::directed([0, 0, 0])").await,
429 "`direction` must be a non-zero vector."
430 );
431 assert_eq!(
432 execution_error("v = view::directed([0, 1, 0], up = [0, 0, 0])").await,
433 "`up` must be a non-zero vector."
434 );
435 assert_eq!(
436 execution_error("v = view::directed([0, 0, 1])").await,
437 "`direction` and `up` must not be parallel or nearly parallel."
438 );
439 }
440
441 #[tokio::test(flavor = "multi_thread")]
446 async fn named_rejects_names_it_cannot_identify_a_view_by() {
447 let showing = "camera = view::oriented(view::Orientation::Front), baseline = view::Visibility::Show";
449
450 assert_eq!(
451 execution_error(&format!(r#"v = view::named("", {showing})"#)).await,
452 "A view's name must not be empty."
453 );
454 assert_eq!(
455 execution_error(&format!(r#"v = view::named(" ", {showing})"#)).await,
456 "A view's name must not be only whitespace."
457 );
458 assert_eq!(
459 execution_error(&format!(r#"v = view::named("Front ", {showing})"#)).await,
460 "A view's name must not start or end with whitespace. Use `string::trim()` to remove it."
461 );
462 assert_eq!(
463 execution_error(&format!(r#"v = view::named(" Front", {showing})"#)).await,
464 "A view's name must not start or end with whitespace. Use `string::trim()` to remove it."
465 );
466 assert_eq!(
467 execution_error(&format!(r#"v = view::named("Default View", {showing})"#)).await,
468 "`Default View` is reserved for the view of the scene generated on successful execution of the program. Please give this view a different name."
469 );
470 assert_eq!(
471 execution_error(&format!(
472 "a = view::named(\"Front\", {showing})\nb = view::named(\"Front\", {showing})"
473 ))
474 .await,
475 "A view named `Front` already exists. Every view needs its own name, and names are compared exactly, so `Front` and `front` are different names."
476 );
477 }
478
479 #[tokio::test(flavor = "multi_thread")]
483 async fn named_accepts_names_that_differ_only_in_case() {
484 let showing = "camera = view::oriented(view::Orientation::Front), baseline = view::Visibility::Show";
485 let program = format!(
486 "@settings(kclVersion = \"3.0-preview\")\na = view::named(\"Front\", {showing})\nb = view::named(\"front\", {showing})\n"
487 );
488 let result = parse_execute(&program).await.expect("the program executes");
489
490 let KclValue::NamedView { value: upper } = result.variable("a") else {
491 panic!("`a` is not a named view");
492 };
493 let KclValue::NamedView { value: lower } = result.variable("b") else {
494 panic!("`b` is not a named view");
495 };
496 assert_eq!(upper.name(), "Front");
497 assert_eq!(lower.name(), "front");
498 }
499
500 #[tokio::test(flavor = "multi_thread")]
505 async fn named_requires_a_baseline() {
506 assert_eq!(
507 execution_error(r#"v = view::named("Front", camera = view::oriented(view::Orientation::Front))"#).await,
508 "The `view::named` function requires a keyword argument `baseline`"
509 );
510 }
511
512 #[tokio::test(flavor = "multi_thread")]
517 async fn named_rejects_wrongly_typed_arguments() {
518 assert_eq!(
520 execution_error(r#"v = view::named("Front", baseline = view::Visibility::Show)"#).await,
521 "The `view::named` function requires a keyword argument `camera`"
522 );
523 assert_eq!(
527 execution_error(r#"v = view::named("Front", camera = "nope", baseline = view::Visibility::Show)"#).await,
528 "camera requires a value with type `CameraView`, but found a value with type `string`."
529 );
530 assert_eq!(
533 execution_error(
534 r#"v = view::named("Front", camera = view::oriented(view::Orientation::Front), baseline = view::Orientation::Top)"#
535 )
536 .await,
537 "baseline requires a value with type `Visibility`, but found a value of enum `Orientation` (with type `Orientation`)."
538 );
539 assert_eq!(
541 execution_error(
542 r#"v = view::named(view::Visibility::Show, camera = view::oriented(view::Orientation::Front), baseline = view::Visibility::Show)"#
543 )
544 .await,
545 "The input argument of `view::named` requires a value with type `string`, but found a value of enum `Visibility` (with type `Visibility`)."
546 );
547 }
548
549 #[tokio::test(flavor = "multi_thread")]
550 async fn named_view_api_requires_v3_even_with_experimental_opt_in() {
551 for version in ["1.0", "2.0"] {
552 for opt_in in ["", ", experimentalFeatures = allow"] {
553 for (case, body) in [
554 ("module", "x = view"),
555 ("enum variant", "x = view::Orientation::Front"),
556 ("constructor", "x = view::directed([0, 1, -2])"),
557 ("type position", "fn pass(@x: view::Orientation) { return x }"),
558 ] {
559 let code = format!("@settings(kclVersion = \"{version}\"{opt_in})\n{body}\n");
560 let err = parse_execute(&code).await.unwrap_err();
561 assert!(
562 err.message()
563 .contains(&format!("added in KCL 3.0, but this program uses KCL {version}")),
564 "case: {case}; code: {code}; error: {}",
565 err.message()
566 );
567 }
568 }
569
570 let code =
571 format!("@settings(kclVersion = \"{version}\", experimentalFeatures = allow)\nimport \"std::view\"\n");
572 assert_eq!(
573 parse_execute(&code).await.unwrap_err().message(),
574 format!("The `std::view` module requires KCL 3.0-preview, but this program uses KCL {version}.")
575 );
576 }
577 }
578
579 #[tokio::test(flavor = "multi_thread")]
582 async fn named_views_are_stable_in_v3_without_opt_in() {
583 let code = r#"@settings(kclVersion = "3.0-preview")
584fn passCamera(@camera: view::CameraView): view::CameraView { return camera }
585fn passView(@named: view::NamedView): view::NamedView { return named }
586front = passCamera(view::oriented(view::Orientation::Front, projection = view::Projection::Perspective))
587custom = view::directed([0, 1, -2])
588named = passView(view::named("Front", camera = front, baseline = view::Visibility::Show))
589"#;
590 let result = parse_execute(code).await.unwrap();
591 assert!(result.issues().is_empty(), "issues: {:?}", result.issues());
592 }
593
594 #[tokio::test(flavor = "multi_thread")]
601 async fn named_excepts_a_sketch_block_by_its_own_id() {
602 let program = format!(
603 "{TWO_SOLIDS}\nv = view::named(\n \"Block\",\n camera = view::oriented(view::Orientation::Front),\n baseline = view::Visibility::Hide,\n except = [plateSketch],\n)\n"
604 );
605 let result = parse_execute(&program).await.expect("the program executes");
606
607 let KclValue::Sketch { value: region } = result.variable("plateRegion") else {
608 panic!("`plateRegion` is not a sketch");
609 };
610 let KclValue::NamedView { value } = result.variable("v") else {
611 panic!("`v` is not a named view");
612 };
613
614 assert_eq!(value.except_ids().len(), 1);
615 assert_ne!(
616 value.except_ids()[0],
617 region.artifact_id,
618 "the block and the region taken from it are different artifacts"
619 );
620 }
621
622 #[tokio::test(flavor = "multi_thread")]
626 async fn named_rejects_an_empty_except_list() {
627 assert_eq!(
628 execution_error(
629 r#"v = view::named("Front", camera = view::oriented(view::Orientation::Front), baseline = view::Visibility::Hide, except = [])"#
630 )
631 .await,
632 "except requires one or more `Solid`s or `Sketch`s or `GdtAnnotation`s or `Helix`s or `Plane`s or imported geometries (`[Solid | Sketch | GdtAnnotation | Helix | Plane | ImportedGeometry; 1+]`), but found an empty array (with type `[any; 0]`)."
633 );
634 }
635
636 #[tokio::test(flavor = "multi_thread")]
639 async fn named_rejects_every_default_plane() {
640 for plane in ["XY", "XZ", "YZ", "-XY", "-XZ", "-YZ"] {
641 assert_eq!(
642 execution_error(&format!(
643 "v = view::named(\"Front\", camera = view::oriented(view::Orientation::Front), baseline = view::Visibility::Hide, except = [{plane}])"
644 ))
645 .await,
646 "Named views cannot control default planes or other uninitialized planes because their ids do not identify independent engine objects. Use a standalone plane returned by `offsetPlane()`."
647 );
648 }
649 }
650
651 #[tokio::test(flavor = "multi_thread")]
654 async fn named_rejects_an_uninitialized_custom_plane() {
655 assert_eq!(
656 execution_error(
657 r#"customPlane = {
658 origin = { x = 0, y = 0, z = 0 },
659 xAxis = { x = 1, y = 0, z = 0 },
660 yAxis = { x = 0, y = 1, z = 0 },
661}
662v = view::named(
663 "Front",
664 camera = view::oriented(view::Orientation::Front),
665 baseline = view::Visibility::Hide,
666 except = [customPlane],
667)"#
668 )
669 .await,
670 "Named views cannot control default planes or other uninitialized planes because their ids do not identify independent engine objects. Use a standalone plane returned by `offsetPlane()`."
671 );
672 }
673
674 #[tokio::test(flavor = "multi_thread")]
686 async fn wrongly_typed_arguments_are_rejected() {
687 assert_eq!(
689 execution_error(r#"v = view::oriented("Front")"#).await,
690 "The input argument of `view::oriented` requires a value with type `Orientation`, but found a value with type `string`."
691 );
692 assert_eq!(
695 execution_error("v = view::oriented(view::Projection::Perspective)").await,
696 "The input argument of `view::oriented` requires a value with type `Orientation`, but found a value of enum `Projection` (with type `Projection`)."
697 );
698 assert_eq!(
701 parse_execute(
702 "@settings(kclVersion = \"3.0-preview\")\ntype MyOrientation { | Front }\nv = view::oriented(MyOrientation::Front)"
703 )
704 .await
705 .unwrap_err()
706 .message(),
707 "The input argument of `view::oriented` requires a value with type `Orientation`, but found a value of enum `MyOrientation` (with type `MyOrientation`)."
708 );
709 assert_eq!(
711 execution_error("v = view::oriented(view::Orientation::Front, projection = view::Orientation::Top)").await,
712 "projection requires a value with type `Projection`, but found a value of enum `Orientation` (with type `Orientation`)."
713 );
714 assert_eq!(
716 execution_error(r#"v = view::directed("nope")"#).await,
717 "The input argument of `view::directed` requires a value with type `Point3d`, but found a value with type `string`."
718 );
719 }
720
721 #[tokio::test(flavor = "multi_thread")]
725 async fn opaque_types_resolve_in_signatures() {
726 let code = r#"@settings(kclVersion = "3.0-preview", experimentalFeatures = allow)
727import CameraView, NamedView from "std::view"
728
729fn acceptsCamera(@camera: CameraView) {
730 return 0
731}
732
733fn passesNamed(@input: NamedView): NamedView {
734 return input
735}
736"#;
737 if let Err(err) = parse_execute(code).await {
738 panic!("expected the declarations to resolve, but got: {}", err.message());
739 }
740 }
741
742 #[tokio::test(flavor = "multi_thread")]
746 async fn qualified_type_path_resolves_in_signature() {
747 let code = r#"@settings(kclVersion = "3.0-preview")
748fn passesOrientation(@input: view::Orientation): view::Orientation {
749 return input
750}
751"#;
752 if let Err(err) = parse_execute(code).await {
753 panic!("expected the declarations to resolve, but got: {}", err.message());
754 }
755 }
756}