diff --git a/crates/polymodel-renderer-protocol/src/lib.rs b/crates/polymodel-renderer-protocol/src/lib.rs index e9ada70..de28b81 100644 --- a/crates/polymodel-renderer-protocol/src/lib.rs +++ b/crates/polymodel-renderer-protocol/src/lib.rs @@ -1985,19 +1985,6 @@ mod tests { assert_eq!(bundle.validate(), Err(ModelBundleError::SizeMismatch)); } - #[test] - fn model_bundle_roundtrips_and_rejects_duplicate_keys() { - let bundle = model_bundle(); - assert_eq!( - ModelLoadBundle::decode(&bundle.encode().unwrap()), - Ok(bundle.clone()) - ); - - let mut duplicate = bundle; - duplicate.resources.push(duplicate.resources[0].clone()); - assert_eq!(duplicate.validate(), Err(ModelBundleError::DuplicateKey)); - } - #[test] fn deserialize_garbage_returns_error() { assert!(deserialize_command_envelope(&[0xFF, 0xFE, 0xFD]).is_err()); diff --git a/src/appview/ldraw.rs b/src/appview/ldraw.rs index 8ba9eb4..62d36cc 100644 --- a/src/appview/ldraw.rs +++ b/src/appview/ldraw.rs @@ -414,6 +414,8 @@ async fn build_model_load_bundle(state: AppState, uri: AtUri) -> AppResult)> = Vec::new(); let mut target_keys_by_path: HashMap<(RootId, CanonicalPath), ProtocolCacheKey> = HashMap::new(); + let mut resolved_identities: HashSet<(ProtocolRootId, polymodel_ldraw_core::NormalizedPath)> = + HashSet::new(); let root_node = resolve_bundle_node(&resolver, &project, &path, libdir).await?; let root_bytes = match &root_node { BundleNode::Db(resolved) => { @@ -736,13 +738,18 @@ async fn build_model_load_bundle(state: AppState, uri: AtUri) -> AppResult AppResult AppResult binding_for(&project, resolved)?.sha256, BundleNode::Official(official) => official.sha256.clone(), }; + let node_path_normalized = + polymodel_renderer_protocol::NormalizedPath::new(node_path.as_str()) + .map_err(|_| invalid_request("invalid resolved resource path"))?; + let node_protocol_root = protocol_root(node_root); + if !resolved_identities.insert((node_protocol_root, node_path_normalized.clone())) { + resources.push((node, bytes)); + for (child_path, child_node, child_bytes, child_key) in children { + queue.push_back((child_path, child_node, child_bytes, child_key)); + } + continue; + } resolved_closure .resolved .push(polymodel_ldraw_core::ResolvedResource { - path: polymodel_renderer_protocol::NormalizedPath::new(node_path.as_str()) - .map_err(|_| invalid_request("invalid resolved resource path"))?, - root: protocol_root(node_root), + path: node_path_normalized, + root: node_protocol_root, sha256: node_sha256, byte_length: u64::try_from(bytes.len()) .map_err(|_| invalid_request("resolved resource byte length overflow"))?,