KCL: Fix cryptic error when using duplicate edges in fillet call (#5755)
Fixes https://github.com/KittyCAD/modeling-app/issues/4307 Now if you try to fillet the same edge twice in a single fillet command, the error message is clearer, and the source range will highlight the specific edges in the array which are duplicated. Same goes for chamfer. Note: although the Rust KCL interpreter sends back an array of SourceRange for each KCL error, the frontend only puts the first one into CodeMirror diagnostics. We should fix that: https://github.com/KittyCAD/modeling-app/issues/5754
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
//! Standard library fillets.
|
||||
|
||||
use anyhow::Result;
|
||||
use indexmap::IndexMap;
|
||||
use kcl_derive_docs::stdlib;
|
||||
use kcmc::{
|
||||
each_cmd as mcmd, length_unit::LengthUnit, ok_response::OkModelingCmdResponse, shared::CutType,
|
||||
@ -11,13 +12,13 @@ use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::utils::unique_count;
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{EdgeCut, ExecState, ExtrudeSurface, FilletSurface, GeoMeta, KclValue, Solid, TagIdentifier},
|
||||
parsing::ast::types::TagNode,
|
||||
settings::types::UnitLength,
|
||||
std::Args,
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
/// A tag or a uuid of an edge.
|
||||
@ -40,13 +41,39 @@ impl EdgeReference {
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn validate_unique<T: Eq + std::hash::Hash>(tags: &[(T, SourceRange)]) -> Result<(), KclError> {
|
||||
// Check if tags contains any duplicate values.
|
||||
let mut tag_counts: IndexMap<&T, Vec<SourceRange>> = Default::default();
|
||||
for tag in tags {
|
||||
tag_counts.entry(&tag.0).or_insert(Vec::new()).push(tag.1);
|
||||
}
|
||||
let mut duplicate_tags_source = Vec::new();
|
||||
for (_tag, count) in tag_counts {
|
||||
if count.len() > 1 {
|
||||
duplicate_tags_source.extend(count)
|
||||
}
|
||||
}
|
||||
if !duplicate_tags_source.is_empty() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "The same edge ID is being referenced multiple times, which is not allowed. Please select a different edge".to_string(),
|
||||
source_ranges: duplicate_tags_source,
|
||||
}));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create fillets on tagged paths.
|
||||
pub async fn fillet(exec_state: &mut ExecState, args: Args) -> Result<KclValue, KclError> {
|
||||
// Get all args:
|
||||
let solid = args.get_unlabeled_kw_arg("solid")?;
|
||||
let radius = args.get_kw_arg("radius")?;
|
||||
let tolerance = args.get_kw_arg_opt("tolerance")?;
|
||||
let tags = args.get_kw_arg("tags")?;
|
||||
let tags = args.kw_arg_array_and_source::<EdgeReference>("tags")?;
|
||||
let tag = args.get_kw_arg_opt("tag")?;
|
||||
|
||||
// Run the function.
|
||||
validate_unique(&tags)?;
|
||||
let tags: Vec<EdgeReference> = tags.into_iter().map(|item| item.0).collect();
|
||||
let value = inner_fillet(solid, radius, tags, tolerance, tag, exec_state, args).await?;
|
||||
Ok(KclValue::Solid { value })
|
||||
}
|
||||
@ -129,15 +156,6 @@ async fn inner_fillet(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
// Check if tags contains any duplicate values.
|
||||
let unique_tags = unique_count(tags.clone());
|
||||
if unique_tags != tags.len() {
|
||||
return Err(KclError::Type(KclErrorDetails {
|
||||
message: "Duplicate tags are not allowed.".to_string(),
|
||||
source_ranges: vec![args.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
let mut solid = solid.clone();
|
||||
for edge_tag in tags {
|
||||
let edge_id = edge_tag.get_engine_id(exec_state, &args)?;
|
||||
@ -432,3 +450,22 @@ pub(crate) fn default_tolerance(units: &UnitLength) -> f64 {
|
||||
UnitLength::M => 0.001,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_validate_unique() {
|
||||
let dup_a = SourceRange::from([1, 3, 0]);
|
||||
let dup_b = SourceRange::from([10, 30, 0]);
|
||||
// Two entries are duplicates (abc) with different source ranges.
|
||||
let tags = vec![("abc", dup_a), ("abc", dup_b), ("def", SourceRange::from([2, 4, 0]))];
|
||||
let actual = validate_unique(&tags);
|
||||
// Both the duplicates should show up as errors, with both of the
|
||||
// source ranges they correspond to.
|
||||
// But the unique source range 'def' should not.
|
||||
let expected = vec![dup_a, dup_b];
|
||||
assert_eq!(actual.err().unwrap().source_ranges(), expected);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user