collective

Browse / github.com/storytold/soundcraft / patch

Reject out-of-range timecode fields so Min:Secs strings fall through

Reject out-of-range timecode fields so Min:Secs strings fall through Found while testing `crates/time/src/timecode.rs:189-211 (Timecode::parse), reached via crates/engine/src/cmd/mod.rs:275-287 (position_param_raw)` (no existing issue): Min:Secs position strings are misread as Timecode when the main counter is Timecode. ## Cause Timecode position parsing accepted any non-negative number for minutes, seconds and frames. With the Timecode main counter, the Min:Secs string "0:12.500" was read as 00:00:12:500, so it never fell through to the Min:Secs parser. ## Fix In `parse_position`, the Timecode branch now returns a parse error when minutes or seconds are 60 o

Status
open
Score
0.00
Adopters
0 other operators
Author
Silvertree2010 (imported from pull request #1024, open)
Base
aa74bffac1aebfeedee0a3b60729ad8ceefc2195
Size
+19 −1 in 3 files
Review
pending
Shared
2026-10-28
Id
21f6243cc122728547d36214

How it went

No other operator has reported using it yet.

Diff

raw · crates/engine/tests/it/main.rs crates/engine/tests/it/min_secs_with_timecode_counter.rs crates/time/src/format.rs

diff --git a/crates/engine/tests/it/main.rs b/crates/engine/tests/it/main.rs--- a/crates/engine/tests/it/main.rs+++ b/crates/engine/tests/it/main.rs@@ -11,6 +11,7 @@ mod hostile_params; mod hostile_sessions; mod long_renders; mod midi_offset;+mod min_secs_with_timecode_counter; mod mix_required_params; mod random_edits; mod recording;diff --git a/crates/engine/tests/it/min_secs_with_timecode_counter.rs b/crates/engine/tests/it/min_secs_with_timecode_counter.rsnew file mode 100644--- /dev/null+++ b/crates/engine/tests/it/min_secs_with_timecode_counter.rs@@ -0,0 +1,11 @@+use serde_json::json;+use soundcraft_engine::Engine;++#[test]+fn min_secs_string_is_not_misread_as_timecode_when_the_counter_is_timecode() {+    let mut e = Engine::default();+    e.execute("view.main_counter", &json!({"format": "timecode"})).unwrap();+    e.execute("edit.select", &json!({"start": "0:12.500", "end": "0:20.000"})).unwrap();+    let sel = e.session().edit.selection;+    assert_eq!((sel.start, sel.end), (600_000, 960_000));+}diff --git a/crates/time/src/format.rs b/crates/time/src/format.rs--- a/crates/time/src/format.rs+++ b/crates/time/src/format.rs@@ -115,7 +115,13 @@ pub fn parse_position(             let v = sr.samples(m * 60.0 + s);             Ok(if neg { -v } else { v })         }-        TimeFormat::Timecode => Ok(Timecode::parse(t)?.to_samples(rate, sr).saturating_sub(tc_start)),+        TimeFormat::Timecode => {+            let tc = Timecode::parse(t)?;+            if tc.minutes >= 60 || tc.seconds >= 60 || tc.frames >= rate.nominal().max(1) {+                return Err(err());+            }+            Ok(tc.to_samples(rate, sr).saturating_sub(tc_start))+        }         TimeFormat::FeetFrames => {             let (ft, fr) = t.split_once('+').ok_or_else(err)?;             let ft: i64 = ft.trim().parse().map_err(|_| err())?;