1#![allow(dead_code)]
2#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum BladeMode {
7 Single,
9 AllTracks,
11 Linked,
13}
14
15impl BladeMode {
16 #[must_use]
18 pub fn cuts_all(&self) -> bool {
19 matches!(self, BladeMode::AllTracks)
20 }
21
22 #[must_use]
24 pub fn label(&self) -> &'static str {
25 match self {
26 BladeMode::Single => "Single",
27 BladeMode::AllTracks => "All Tracks",
28 BladeMode::Linked => "Linked",
29 }
30 }
31}
32
33#[derive(Debug, Clone)]
35pub struct BladeCut {
36 pub track_index: usize,
38 pub clip_id: u64,
40 pub cut_frame: i64,
42}
43
44impl BladeCut {
45 #[must_use]
47 pub fn new(track_index: usize, clip_id: u64, cut_frame: i64) -> Self {
48 Self {
49 track_index,
50 clip_id,
51 cut_frame,
52 }
53 }
54
55 #[must_use]
58 pub fn at_frame(mut self, frame: i64) -> Self {
59 self.cut_frame = frame;
60 self
61 }
62}
63
64#[derive(Debug, Clone)]
66pub struct BladeResult {
67 pub cuts: Vec<BladeCut>,
69 pub new_segments: usize,
71}
72
73impl BladeResult {
74 #[must_use]
76 pub fn new(cuts: Vec<BladeCut>) -> Self {
77 let new_segments = cuts.len();
78 Self { cuts, new_segments }
79 }
80
81 #[must_use]
83 pub fn cuts_applied(&self) -> usize {
84 self.cuts.len()
85 }
86}
87
88#[derive(Debug, Clone)]
90pub struct BladeTool {
91 pub mode: BladeMode,
93 pub snap_threshold: u32,
95}
96
97impl BladeTool {
98 #[must_use]
100 pub fn new(mode: BladeMode, snap_threshold: u32) -> Self {
101 Self {
102 mode,
103 snap_threshold,
104 }
105 }
106
107 #[must_use]
112 pub fn cut(&self, clips: &[(usize, u64, i64, i64)], frame: i64) -> BladeResult {
113 let cuts: Vec<BladeCut> = clips
114 .iter()
115 .filter(|(_, _, start, end)| frame > *start && frame < *end)
116 .map(|(track, id, _, _)| BladeCut::new(*track, *id, frame))
117 .collect();
118 BladeResult::new(cuts)
119 }
120
121 #[must_use]
123 pub fn preview_cut(&self, clips: &[(usize, u64, i64, i64)], frame: i64) -> Vec<BladeCut> {
124 self.cut(clips, frame).cuts
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::*;
131
132 #[test]
133 fn test_single_not_all() {
134 assert!(!BladeMode::Single.cuts_all());
135 }
136
137 #[test]
138 fn test_all_tracks_cuts_all() {
139 assert!(BladeMode::AllTracks.cuts_all());
140 }
141
142 #[test]
143 fn test_linked_not_all() {
144 assert!(!BladeMode::Linked.cuts_all());
145 }
146
147 #[test]
148 fn test_blade_mode_labels() {
149 assert_eq!(BladeMode::Single.label(), "Single");
150 assert_eq!(BladeMode::AllTracks.label(), "All Tracks");
151 assert_eq!(BladeMode::Linked.label(), "Linked");
152 }
153
154 #[test]
155 fn test_blade_cut_at_frame() {
156 let cut = BladeCut::new(0, 1, 50).at_frame(75);
157 assert_eq!(cut.cut_frame, 75);
158 }
159
160 #[test]
161 fn test_blade_result_cuts_applied() {
162 let cuts = vec![BladeCut::new(0, 1, 50), BladeCut::new(1, 2, 50)];
163 let result = BladeResult::new(cuts);
164 assert_eq!(result.cuts_applied(), 2);
165 assert_eq!(result.new_segments, 2);
166 }
167
168 #[test]
169 fn test_blade_tool_cut_single_clip() {
170 let tool = BladeTool::new(BladeMode::Single, 2);
171 let clips = vec![(0usize, 1u64, 0i64, 100i64)];
172 let result = tool.cut(&clips, 50);
173 assert_eq!(result.cuts_applied(), 1);
174 assert_eq!(result.cuts[0].cut_frame, 50);
175 }
176
177 #[test]
178 fn test_blade_tool_no_cut_outside_range() {
179 let tool = BladeTool::new(BladeMode::Single, 0);
180 let clips = vec![(0usize, 1u64, 0i64, 100i64)];
181 let result = tool.cut(&clips, 150);
183 assert_eq!(result.cuts_applied(), 0);
184 }
185
186 #[test]
187 fn test_blade_tool_no_cut_at_boundary() {
188 let tool = BladeTool::new(BladeMode::Single, 0);
189 let clips = vec![(0usize, 1u64, 0i64, 100i64)];
190 let result = tool.cut(&clips, 0);
192 assert_eq!(result.cuts_applied(), 0);
193 }
194
195 #[test]
196 fn test_blade_tool_all_tracks_cut_multiple() {
197 let tool = BladeTool::new(BladeMode::AllTracks, 0);
198 let clips = vec![
199 (0usize, 10u64, 0i64, 200i64),
200 (1usize, 20u64, 0i64, 200i64),
201 (2usize, 30u64, 50i64, 150i64),
202 ];
203 let result = tool.cut(&clips, 100);
204 assert_eq!(result.cuts_applied(), 3);
205 }
206
207 #[test]
208 fn test_preview_cut_matches_cut() {
209 let tool = BladeTool::new(BladeMode::Single, 0);
210 let clips = vec![(0usize, 5u64, 10i64, 90i64)];
211 let preview = tool.preview_cut(&clips, 40);
212 let actual = tool.cut(&clips, 40).cuts;
213 assert_eq!(preview.len(), actual.len());
214 }
215
216 #[test]
217 fn test_blade_result_empty() {
218 let result = BladeResult::new(vec![]);
219 assert_eq!(result.cuts_applied(), 0);
220 }
221
222 #[test]
223 fn test_blade_tool_default_fields() {
224 let tool = BladeTool::new(BladeMode::Linked, 5);
225 assert_eq!(tool.snap_threshold, 5);
226 assert_eq!(tool.mode, BladeMode::Linked);
227 }
228}