1use std::collections::HashMap;
4
5use anyhow::Result;
6use kcmc::{
7 each_cmd as mcmd,
8 ok_response::{output::EntityGetAllChildUuids, OkModelingCmdResponse},
9 websocket::OkWebSocketResponseData,
10 ModelingCmd,
11};
12use kittycad_modeling_cmds::{self as kcmc};
13
14use super::extrude::do_post_extrude;
15use crate::{
16 errors::{KclError, KclErrorDetails},
17 execution::{
18 types::{NumericType, PrimitiveType, RuntimeType},
19 ExecState, GeometryWithImportedGeometry, KclValue, Sketch, Solid,
20 },
21 parsing::ast::types::TagNode,
22 std::{extrude::NamedCapTags, Args},
23};
24
25pub async fn clone(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
29 let geometry = args.get_unlabeled_kw_arg_typed(
30 "geometry",
31 &RuntimeType::Union(vec![
32 RuntimeType::Primitive(PrimitiveType::Sketch),
33 RuntimeType::Primitive(PrimitiveType::Solid),
34 RuntimeType::imported(),
35 ]),
36 exec_state,
37 )?;
38
39 let cloned = inner_clone(geometry, exec_state, args).await?;
40 Ok(cloned.into())
41}
42
43async fn inner_clone(
44 geometry: GeometryWithImportedGeometry,
45 exec_state: &mut ExecState,
46 args: Args,
47) -> Result<GeometryWithImportedGeometry, KclError> {
48 let new_id = exec_state.next_uuid();
49 let mut geometry = geometry.clone();
50 let old_id = geometry.id(&args.ctx).await?;
51
52 let mut new_geometry = match &geometry {
53 GeometryWithImportedGeometry::ImportedGeometry(imported) => {
54 let mut new_imported = imported.clone();
55 new_imported.id = new_id;
56 GeometryWithImportedGeometry::ImportedGeometry(new_imported)
57 }
58 GeometryWithImportedGeometry::Sketch(sketch) => {
59 let mut new_sketch = sketch.clone();
60 new_sketch.id = new_id;
61 new_sketch.original_id = new_id;
62 #[cfg(feature = "artifact-graph")]
63 {
64 new_sketch.artifact_id = new_id.into();
65 }
66 GeometryWithImportedGeometry::Sketch(new_sketch)
67 }
68 GeometryWithImportedGeometry::Solid(solid) => {
69 args.flush_batch_for_solids(exec_state, &[solid.clone()]).await?;
71
72 let mut new_solid = solid.clone();
73 new_solid.id = new_id;
74 new_solid.sketch.original_id = new_id;
75 #[cfg(feature = "artifact-graph")]
76 {
77 new_solid.artifact_id = new_id.into();
78 }
79 GeometryWithImportedGeometry::Solid(new_solid)
80 }
81 };
82
83 if args.ctx.no_engine_commands().await {
84 return Ok(new_geometry);
85 }
86
87 args.batch_modeling_cmd(new_id, ModelingCmd::from(mcmd::EntityClone { entity_id: old_id }))
88 .await?;
89
90 fix_tags_and_references(&mut new_geometry, old_id, exec_state, &args)
91 .await
92 .map_err(|e| {
93 KclError::Internal(KclErrorDetails {
94 message: format!("failed to fix tags and references: {:?}", e),
95 source_ranges: vec![args.source_range],
96 })
97 })?;
98
99 Ok(new_geometry)
100}
101async fn fix_tags_and_references(
103 new_geometry: &mut GeometryWithImportedGeometry,
104 old_geometry_id: uuid::Uuid,
105 exec_state: &mut ExecState,
106 args: &Args,
107) -> Result<()> {
108 let new_geometry_id = new_geometry.id(&args.ctx).await?;
109 let entity_id_map = get_old_new_child_map(new_geometry_id, old_geometry_id, exec_state, args).await?;
110
111 match new_geometry {
113 GeometryWithImportedGeometry::ImportedGeometry(_) => {}
114 GeometryWithImportedGeometry::Sketch(sketch) => {
115 fix_sketch_tags_and_references(sketch, &entity_id_map, exec_state).await?;
116 }
117 GeometryWithImportedGeometry::Solid(solid) => {
118 solid.sketch.id = new_geometry_id;
120 solid.sketch.original_id = new_geometry_id;
121 #[cfg(feature = "artifact-graph")]
122 {
123 solid.sketch.artifact_id = new_geometry_id.into();
124 }
125
126 fix_sketch_tags_and_references(&mut solid.sketch, &entity_id_map, exec_state).await?;
127
128 let (start_tag, end_tag) = get_named_cap_tags(solid);
129
130 for edge_cut in solid.edge_cuts.iter_mut() {
132 if let Some(id) = entity_id_map.get(&edge_cut.id()) {
133 edge_cut.set_id(*id);
134 } else {
135 crate::log::logln!(
136 "Failed to find new edge cut id for old edge cut id: {:?}",
137 edge_cut.id()
138 );
139 }
140 if let Some(new_edge_id) = entity_id_map.get(&edge_cut.edge_id()) {
141 edge_cut.set_edge_id(*new_edge_id);
142 } else {
143 crate::log::logln!("Failed to find new edge id for old edge id: {:?}", edge_cut.edge_id());
144 }
145 }
146
147 let new_solid = do_post_extrude(
150 &solid.sketch,
151 #[cfg(feature = "artifact-graph")]
152 new_geometry_id.into(),
153 crate::std::args::TyF64::new(
154 solid.height,
155 NumericType::Known(crate::execution::types::UnitType::Length(solid.units)),
156 ),
157 solid.sectional,
158 &NamedCapTags {
159 start: start_tag.as_ref(),
160 end: end_tag.as_ref(),
161 },
162 exec_state,
163 args,
164 None,
165 )
166 .await?;
167
168 *solid = new_solid;
169 }
170 }
171
172 Ok(())
173}
174
175async fn get_old_new_child_map(
176 new_geometry_id: uuid::Uuid,
177 old_geometry_id: uuid::Uuid,
178 exec_state: &mut ExecState,
179 args: &Args,
180) -> Result<HashMap<uuid::Uuid, uuid::Uuid>> {
181 let response = args
183 .send_modeling_cmd(
184 exec_state.next_uuid(),
185 ModelingCmd::from(mcmd::EntityGetAllChildUuids {
186 entity_id: old_geometry_id,
187 }),
188 )
189 .await?;
190 let OkWebSocketResponseData::Modeling {
191 modeling_response:
192 OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids {
193 entity_ids: old_entity_ids,
194 }),
195 } = response
196 else {
197 anyhow::bail!("Expected EntityGetAllChildUuids response, got: {:?}", response);
198 };
199
200 let response = args
202 .send_modeling_cmd(
203 exec_state.next_uuid(),
204 ModelingCmd::from(mcmd::EntityGetAllChildUuids {
205 entity_id: new_geometry_id,
206 }),
207 )
208 .await?;
209 let OkWebSocketResponseData::Modeling {
210 modeling_response:
211 OkModelingCmdResponse::EntityGetAllChildUuids(EntityGetAllChildUuids {
212 entity_ids: new_entity_ids,
213 }),
214 } = response
215 else {
216 anyhow::bail!("Expected EntityGetAllChildUuids response, got: {:?}", response);
217 };
218
219 Ok(HashMap::from_iter(
221 old_entity_ids
222 .iter()
223 .zip(new_entity_ids.iter())
224 .map(|(old_id, new_id)| (*old_id, *new_id)),
225 ))
226}
227
228async fn fix_sketch_tags_and_references(
230 new_sketch: &mut Sketch,
231 entity_id_map: &HashMap<uuid::Uuid, uuid::Uuid>,
232 exec_state: &mut ExecState,
233) -> Result<()> {
234 for path in new_sketch.paths.as_mut_slice() {
236 if let Some(new_path_id) = entity_id_map.get(&path.get_id()) {
237 path.set_id(*new_path_id);
238 } else {
239 crate::log::logln!("Failed to find new path id for old path id: {:?}", path.get_id());
242 }
243 }
244
245 for path in new_sketch.paths.clone() {
249 if let Some(tag) = path.get_tag() {
251 new_sketch.add_tag(&tag, &path, exec_state);
252 }
253 }
254
255 if let Some(new_base_path) = entity_id_map.get(&new_sketch.start.geo_meta.id) {
257 new_sketch.start.geo_meta.id = *new_base_path;
258 } else {
259 crate::log::logln!(
260 "Failed to find new base path id for old base path id: {:?}",
261 new_sketch.start.geo_meta.id
262 );
263 }
264
265 Ok(())
266}
267
268fn get_named_cap_tags(solid: &Solid) -> (Option<TagNode>, Option<TagNode>) {
270 let mut start_tag = None;
271 let mut end_tag = None;
272 if let Some(start_cap_id) = solid.start_cap_id {
274 for value in &solid.value {
276 if value.get_id() == start_cap_id {
277 start_tag = value.get_tag().clone();
278 break;
279 }
280 }
281 }
282
283 if let Some(end_cap_id) = solid.end_cap_id {
285 for value in &solid.value {
287 if value.get_id() == end_cap_id {
288 end_tag = value.get_tag().clone();
289 break;
290 }
291 }
292 }
293
294 (start_tag, end_tag)
295}
296
297#[cfg(test)]
298mod tests {
299 use pretty_assertions::{assert_eq, assert_ne};
300
301 use crate::exec::KclValue;
302
303 #[tokio::test(flavor = "multi_thread")]
306 async fn kcl_test_clone_sketch() {
307 let code = r#"cube = startSketchOn(XY)
308 |> startProfile(at = [0,0])
309 |> line(end = [0, 10])
310 |> line(end = [10, 0])
311 |> line(end = [0, -10])
312 |> close()
313
314clonedCube = clone(cube)
315"#;
316 let ctx = crate::test_server::new_context(true, None).await.unwrap();
317 let program = crate::Program::parse_no_errs(code).unwrap();
318
319 let result = ctx.run_with_caching(program.clone()).await.unwrap();
321 let cube = result.variables.get("cube").unwrap();
322 let cloned_cube = result.variables.get("clonedCube").unwrap();
323
324 assert_ne!(cube, cloned_cube);
325
326 let KclValue::Sketch { value: cube } = cube else {
327 panic!("Expected a sketch, got: {:?}", cube);
328 };
329 let KclValue::Sketch { value: cloned_cube } = cloned_cube else {
330 panic!("Expected a sketch, got: {:?}", cloned_cube);
331 };
332
333 assert_ne!(cube.id, cloned_cube.id);
334 assert_ne!(cube.original_id, cloned_cube.original_id);
335 #[cfg(feature = "artifact-graph")]
336 assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
337
338 #[cfg(feature = "artifact-graph")]
339 assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
340 assert_eq!(cloned_cube.original_id, cloned_cube.id);
341
342 for (path, cloned_path) in cube.paths.iter().zip(cloned_cube.paths.iter()) {
343 assert_ne!(path.get_id(), cloned_path.get_id());
344 assert_eq!(path.get_tag(), cloned_path.get_tag());
345 }
346
347 assert_eq!(cube.tags.len(), 0);
348 assert_eq!(cloned_cube.tags.len(), 0);
349
350 ctx.close().await;
351 }
352
353 #[tokio::test(flavor = "multi_thread")]
356 async fn kcl_test_clone_solid() {
357 let code = r#"cube = startSketchOn(XY)
358 |> startProfile(at = [0,0])
359 |> line(end = [0, 10])
360 |> line(end = [10, 0])
361 |> line(end = [0, -10])
362 |> close()
363 |> extrude(length = 5)
364
365clonedCube = clone(cube)
366"#;
367 let ctx = crate::test_server::new_context(true, None).await.unwrap();
368 let program = crate::Program::parse_no_errs(code).unwrap();
369
370 let result = ctx.run_with_caching(program.clone()).await.unwrap();
372 let cube = result.variables.get("cube").unwrap();
373 let cloned_cube = result.variables.get("clonedCube").unwrap();
374
375 assert_ne!(cube, cloned_cube);
376
377 let KclValue::Solid { value: cube } = cube else {
378 panic!("Expected a solid, got: {:?}", cube);
379 };
380 let KclValue::Solid { value: cloned_cube } = cloned_cube else {
381 panic!("Expected a solid, got: {:?}", cloned_cube);
382 };
383
384 assert_ne!(cube.id, cloned_cube.id);
385 assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
386 assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
387 #[cfg(feature = "artifact-graph")]
388 assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
389 #[cfg(feature = "artifact-graph")]
390 assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
391
392 #[cfg(feature = "artifact-graph")]
393 assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
394
395 for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
396 assert_ne!(path.get_id(), cloned_path.get_id());
397 assert_eq!(path.get_tag(), cloned_path.get_tag());
398 }
399
400 for (value, cloned_value) in cube.value.iter().zip(cloned_cube.value.iter()) {
401 assert_ne!(value.get_id(), cloned_value.get_id());
402 assert_eq!(value.get_tag(), cloned_value.get_tag());
403 }
404
405 assert_eq!(cube.sketch.tags.len(), 0);
406 assert_eq!(cloned_cube.sketch.tags.len(), 0);
407
408 assert_eq!(cube.edge_cuts.len(), 0);
409 assert_eq!(cloned_cube.edge_cuts.len(), 0);
410
411 ctx.close().await;
412 }
413
414 #[tokio::test(flavor = "multi_thread")]
418 async fn kcl_test_clone_sketch_with_tags() {
419 let code = r#"cube = startSketchOn(XY)
420 |> startProfile(at = [0,0]) // tag this one
421 |> line(end = [0, 10], tag = $tag02)
422 |> line(end = [10, 0], tag = $tag03)
423 |> line(end = [0, -10], tag = $tag04)
424 |> close(tag = $tag05)
425
426clonedCube = clone(cube)
427"#;
428 let ctx = crate::test_server::new_context(true, None).await.unwrap();
429 let program = crate::Program::parse_no_errs(code).unwrap();
430
431 let result = ctx.run_with_caching(program.clone()).await.unwrap();
433 let cube = result.variables.get("cube").unwrap();
434 let cloned_cube = result.variables.get("clonedCube").unwrap();
435
436 assert_ne!(cube, cloned_cube);
437
438 let KclValue::Sketch { value: cube } = cube else {
439 panic!("Expected a sketch, got: {:?}", cube);
440 };
441 let KclValue::Sketch { value: cloned_cube } = cloned_cube else {
442 panic!("Expected a sketch, got: {:?}", cloned_cube);
443 };
444
445 assert_ne!(cube.id, cloned_cube.id);
446 assert_ne!(cube.original_id, cloned_cube.original_id);
447
448 for (path, cloned_path) in cube.paths.iter().zip(cloned_cube.paths.iter()) {
449 assert_ne!(path.get_id(), cloned_path.get_id());
450 assert_eq!(path.get_tag(), cloned_path.get_tag());
451 }
452
453 for (tag_name, tag) in &cube.tags {
454 let cloned_tag = cloned_cube.tags.get(tag_name).unwrap();
455
456 let tag_info = tag.get_cur_info().unwrap();
457 let cloned_tag_info = cloned_tag.get_cur_info().unwrap();
458
459 assert_ne!(tag_info.id, cloned_tag_info.id);
460 assert_ne!(tag_info.sketch, cloned_tag_info.sketch);
461 assert_ne!(tag_info.path, cloned_tag_info.path);
462 assert_eq!(tag_info.surface, None);
463 assert_eq!(cloned_tag_info.surface, None);
464 }
465
466 ctx.close().await;
467 }
468
469 #[tokio::test(flavor = "multi_thread")]
473 async fn kcl_test_clone_solid_with_tags() {
474 let code = r#"cube = startSketchOn(XY)
475 |> startProfile(at = [0,0]) // tag this one
476 |> line(end = [0, 10], tag = $tag02)
477 |> line(end = [10, 0], tag = $tag03)
478 |> line(end = [0, -10], tag = $tag04)
479 |> close(tag = $tag05)
480 |> extrude(length = 5) // TODO: Tag these
481
482clonedCube = clone(cube)
483"#;
484 let ctx = crate::test_server::new_context(true, None).await.unwrap();
485 let program = crate::Program::parse_no_errs(code).unwrap();
486
487 let result = ctx.run_with_caching(program.clone()).await.unwrap();
489 let cube = result.variables.get("cube").unwrap();
490 let cloned_cube = result.variables.get("clonedCube").unwrap();
491
492 assert_ne!(cube, cloned_cube);
493
494 let KclValue::Solid { value: cube } = cube else {
495 panic!("Expected a solid, got: {:?}", cube);
496 };
497 let KclValue::Solid { value: cloned_cube } = cloned_cube else {
498 panic!("Expected a solid, got: {:?}", cloned_cube);
499 };
500
501 assert_ne!(cube.id, cloned_cube.id);
502 assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
503 assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
504 #[cfg(feature = "artifact-graph")]
505 assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
506 #[cfg(feature = "artifact-graph")]
507 assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
508
509 #[cfg(feature = "artifact-graph")]
510 assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
511
512 for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
513 assert_ne!(path.get_id(), cloned_path.get_id());
514 assert_eq!(path.get_tag(), cloned_path.get_tag());
515 }
516
517 for (value, cloned_value) in cube.value.iter().zip(cloned_cube.value.iter()) {
518 assert_ne!(value.get_id(), cloned_value.get_id());
519 assert_eq!(value.get_tag(), cloned_value.get_tag());
520 }
521
522 for (tag_name, tag) in &cube.sketch.tags {
523 let cloned_tag = cloned_cube.sketch.tags.get(tag_name).unwrap();
524
525 let tag_info = tag.get_cur_info().unwrap();
526 let cloned_tag_info = cloned_tag.get_cur_info().unwrap();
527
528 assert_ne!(tag_info.id, cloned_tag_info.id);
529 assert_ne!(tag_info.sketch, cloned_tag_info.sketch);
530 assert_ne!(tag_info.path, cloned_tag_info.path);
531 assert_ne!(tag_info.surface, cloned_tag_info.surface);
532 }
533
534 assert_eq!(cube.edge_cuts.len(), 0);
535 assert_eq!(cloned_cube.edge_cuts.len(), 0);
536
537 ctx.close().await;
538 }
539
540 #[tokio::test(flavor = "multi_thread")]
542 #[ignore = "this test is not working yet, need to fix the getting of ids if sketch already closed"]
543 async fn kcl_test_clone_cube_already_closed_sketch() {
544 let code = r#"// Clone a basic solid and move it.
545
546exampleSketch = startSketchOn(XY)
547 |> startProfile(at = [0, 0])
548 |> line(end = [10, 0])
549 |> line(end = [0, 10])
550 |> line(end = [-10, 0])
551 |> line(end = [0, -10])
552 |> close()
553
554cube = extrude(exampleSketch, length = 5)
555clonedCube = clone(cube)
556 |> translate(
557 x = 25.0,
558 )"#;
559 let ctx = crate::test_server::new_context(true, None).await.unwrap();
560 let program = crate::Program::parse_no_errs(code).unwrap();
561
562 let result = ctx.run_with_caching(program.clone()).await.unwrap();
564 let cube = result.variables.get("cube").unwrap();
565 let cloned_cube = result.variables.get("clonedCube").unwrap();
566
567 assert_ne!(cube, cloned_cube);
568
569 let KclValue::Solid { value: cube } = cube else {
570 panic!("Expected a solid, got: {:?}", cube);
571 };
572 let KclValue::Solid { value: cloned_cube } = cloned_cube else {
573 panic!("Expected a solid, got: {:?}", cloned_cube);
574 };
575
576 assert_ne!(cube.id, cloned_cube.id);
577 assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
578 assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
579 #[cfg(feature = "artifact-graph")]
580 assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
581 #[cfg(feature = "artifact-graph")]
582 assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
583
584 #[cfg(feature = "artifact-graph")]
585 assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
586
587 for (path, cloned_path) in cube.sketch.paths.iter().zip(cloned_cube.sketch.paths.iter()) {
588 assert_ne!(path.get_id(), cloned_path.get_id());
589 assert_eq!(path.get_tag(), cloned_path.get_tag());
590 }
591
592 for (value, cloned_value) in cube.value.iter().zip(cloned_cube.value.iter()) {
593 assert_ne!(value.get_id(), cloned_value.get_id());
594 assert_eq!(value.get_tag(), cloned_value.get_tag());
595 }
596
597 for (tag_name, tag) in &cube.sketch.tags {
598 let cloned_tag = cloned_cube.sketch.tags.get(tag_name).unwrap();
599
600 let tag_info = tag.get_cur_info().unwrap();
601 let cloned_tag_info = cloned_tag.get_cur_info().unwrap();
602
603 assert_ne!(tag_info.id, cloned_tag_info.id);
604 assert_ne!(tag_info.sketch, cloned_tag_info.sketch);
605 assert_ne!(tag_info.path, cloned_tag_info.path);
606 assert_ne!(tag_info.surface, cloned_tag_info.surface);
607 }
608
609 for (edge_cut, cloned_edge_cut) in cube.edge_cuts.iter().zip(cloned_cube.edge_cuts.iter()) {
610 assert_ne!(edge_cut.id(), cloned_edge_cut.id());
611 assert_ne!(edge_cut.edge_id(), cloned_edge_cut.edge_id());
612 assert_eq!(edge_cut.tag(), cloned_edge_cut.tag());
613 }
614
615 ctx.close().await;
616 }
617
618 #[tokio::test(flavor = "multi_thread")]
622 #[ignore] async fn kcl_test_clone_solid_with_edge_cuts() {
624 let code = r#"cube = startSketchOn(XY)
625 |> startProfile(at = [0,0]) // tag this one
626 |> line(end = [0, 10], tag = $tag02)
627 |> line(end = [10, 0], tag = $tag03)
628 |> line(end = [0, -10], tag = $tag04)
629 |> close(tag = $tag05)
630 |> extrude(length = 5) // TODO: Tag these
631 |> fillet(
632 radius = 2,
633 tags = [
634 getNextAdjacentEdge(tag02),
635 ],
636 tag = $fillet01,
637 )
638 |> fillet(
639 radius = 2,
640 tags = [
641 getNextAdjacentEdge(tag04),
642 ],
643 tag = $fillet02,
644 )
645 |> chamfer(
646 length = 2,
647 tags = [
648 getNextAdjacentEdge(tag03),
649 ],
650 tag = $chamfer01,
651 )
652 |> chamfer(
653 length = 2,
654 tags = [
655 getNextAdjacentEdge(tag05),
656 ],
657 tag = $chamfer02,
658 )
659
660clonedCube = clone(cube)
661"#;
662 let ctx = crate::test_server::new_context(true, None).await.unwrap();
663 let program = crate::Program::parse_no_errs(code).unwrap();
664
665 let result = ctx.run_with_caching(program.clone()).await.unwrap();
667 let cube = result.variables.get("cube").unwrap();
668 let cloned_cube = result.variables.get("clonedCube").unwrap();
669
670 assert_ne!(cube, cloned_cube);
671
672 let KclValue::Solid { value: cube } = cube else {
673 panic!("Expected a solid, got: {:?}", cube);
674 };
675 let KclValue::Solid { value: cloned_cube } = cloned_cube else {
676 panic!("Expected a solid, got: {:?}", cloned_cube);
677 };
678
679 assert_ne!(cube.id, cloned_cube.id);
680 assert_ne!(cube.sketch.id, cloned_cube.sketch.id);
681 assert_ne!(cube.sketch.original_id, cloned_cube.sketch.original_id);
682 #[cfg(feature = "artifact-graph")]
683 assert_ne!(cube.artifact_id, cloned_cube.artifact_id);
684 #[cfg(feature = "artifact-graph")]
685 assert_ne!(cube.sketch.artifact_id, cloned_cube.sketch.artifact_id);
686
687 #[cfg(feature = "artifact-graph")]
688 assert_eq!(cloned_cube.artifact_id, cloned_cube.id.into());
689
690 for (value, cloned_value) in cube.value.iter().zip(cloned_cube.value.iter()) {
691 assert_ne!(value.get_id(), cloned_value.get_id());
692 assert_eq!(value.get_tag(), cloned_value.get_tag());
693 }
694
695 for (edge_cut, cloned_edge_cut) in cube.edge_cuts.iter().zip(cloned_cube.edge_cuts.iter()) {
696 assert_ne!(edge_cut.id(), cloned_edge_cut.id());
697 assert_ne!(edge_cut.edge_id(), cloned_edge_cut.edge_id());
698 assert_eq!(edge_cut.tag(), cloned_edge_cut.tag());
699 }
700
701 ctx.close().await;
702 }
703}