use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub enum TreeShellDragEffect { Copy, Move, None, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct TreeShellDragPayload { pub row_ids: Vec, } #[derive(Debug, Clone, PartialEq, Eq, Default)] pub struct TreeShellDropTarget { pub row_id: Option, pub document_id: Option, pub asset_id: Option, } pub fn resolve_drag_effect(has_external_files: bool, alt_key: bool) -> TreeShellDragEffect { if has_external_files || alt_key { TreeShellDragEffect::Copy } else { TreeShellDragEffect::Move } } pub fn normalize_drag_payload(row_ids: &[String]) -> Option { let mut normalized = Vec::new(); for row_id in row_ids { let row_id = row_id.trim(); if row_id.is_empty() || normalized.iter().any(|existing| existing == row_id) { continue; } normalized.push(row_id.to_string()); } if normalized.is_empty() { None } else { Some(TreeShellDragPayload { row_ids: normalized, }) } } #[cfg(test)] mod tests { use super::{normalize_drag_payload, resolve_drag_effect, TreeShellDragEffect}; fn ids(values: &[&str]) -> Vec { values.iter().map(|value| (*value).to_string()).collect() } #[test] fn tree_shell_drag_drop_state_normalizes_payload_and_effect() { let payload = normalize_drag_payload(&ids(&[" doc:a ", "asset:b", "doc:a", ""])).unwrap(); assert_eq!(payload.row_ids, ids(&["doc:a", "asset:b"])); assert_eq!(resolve_drag_effect(true, false), TreeShellDragEffect::Copy); assert_eq!(resolve_drag_effect(false, true), TreeShellDragEffect::Copy); assert_eq!(resolve_drag_effect(false, false), TreeShellDragEffect::Move); assert!(normalize_drag_payload(&ids(&["", " "])).is_none()); } }