1use uuid::Uuid;
4
5use super::sketch::FaceTag;
6use crate::errors::KclError;
7use crate::errors::KclErrorDetails;
8use crate::execution::ExecState;
9use crate::execution::Face;
10use crate::execution::FaceParentSolid;
11use crate::execution::KclValue;
12use crate::execution::Segment;
13use crate::execution::Solid;
14use crate::execution::types::RuntimeType;
15use crate::front::SourceRef;
16use crate::std::Args;
17
18const SEGMENT_MUST_HAVE_TAG_ERROR: &str =
19 "Face specifier must have a tag. For sketch block segments, assign the segment to a variable.";
20
21pub(crate) enum FaceSpecifier {
23 FaceTag(FaceTag),
24 Segment(Box<Segment>),
25}
26
27impl FaceSpecifier {
28 fn tag_name(&self) -> Option<String> {
32 match self {
33 FaceSpecifier::FaceTag(face_tag) => Some(face_tag.to_string()),
34 FaceSpecifier::Segment(segment) => segment.tag.as_ref().map(|tag| tag.to_string()),
35 }
36 }
37
38 pub(super) async fn face_id(
39 &self,
40 solid: &Solid,
41 exec_state: &mut ExecState,
42 args: &Args,
43 must_be_planar: bool,
44 ) -> Result<Uuid, KclError> {
45 match self {
46 FaceSpecifier::FaceTag(face_tag) => face_tag.get_face_id(solid, exec_state, args, must_be_planar).await,
47 FaceSpecifier::Segment(segment) => {
48 let tag_id = segment.tag.as_ref().ok_or_else(|| {
49 KclError::new_semantic(KclErrorDetails::new(
50 SEGMENT_MUST_HAVE_TAG_ERROR.to_owned(),
51 vec![args.source_range],
52 ))
53 })?;
54 args.get_adjacent_face_to_tag(exec_state, tag_id, must_be_planar).await
55 }
56 }
57 }
58}
59
60pub async fn face_of(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
62 let solid = args.get_unlabeled_kw_arg("solid", &RuntimeType::solid(), exec_state)?;
63 let face = args.get_kw_arg("face", &RuntimeType::tagged_face_or_segment(), exec_state)?;
64
65 let face = make_face(solid, face, exec_state, &args).await?;
66
67 Ok(KclValue::Face { value: face })
68}
69
70pub(super) async fn make_face(
71 solid: Box<Solid>,
72 face: FaceSpecifier,
73 exec_state: &mut ExecState,
74 args: &Args,
75) -> Result<Box<Face>, KclError> {
76 let Some(tag_name) = face.tag_name() else {
77 return Err(KclError::new_semantic(KclErrorDetails::new(
78 SEGMENT_MUST_HAVE_TAG_ERROR.to_owned(),
79 vec![args.source_range],
80 )));
81 };
82 let extrude_plane_id = face.face_id(&solid, exec_state, args, true).await?;
83 let sketch = solid.sketch().ok_or_else(|| {
84 KclError::new_type(crate::errors::KclErrorDetails::new(
85 "This solid was created without a sketch, so its face axes are unavailable.".to_owned(),
86 vec![args.source_range],
87 ))
88 })?;
89
90 let object_id = exec_state.next_object_id();
91 let face_object = crate::front::Object {
92 id: object_id,
93 kind: crate::front::ObjectKind::Face(crate::front::Face { id: object_id }),
94 label: Default::default(),
95 comments: Default::default(),
96 artifact_id: extrude_plane_id.into(),
97 source: SourceRef::new(args.source_range, args.node_path.clone()),
98 };
99 exec_state.add_scene_object(face_object, args.source_range);
100
101 Ok(Box::new(Face {
102 id: extrude_plane_id,
103 artifact_id: extrude_plane_id.into(),
104 object_id,
105 value: tag_name,
106 x_axis: sketch.on.x_axis(),
108 y_axis: sketch.on.y_axis(),
109 units: solid.units,
110 parent_solid: FaceParentSolid::from(&*solid),
111 meta: vec![args.source_range.into()],
112 }))
113}