Skip to content

Commit 960c52e

Browse files
authored
chore: dedupe parse_duration_to_seconds across sync and traces (#181)
Re-applies the deduplication from d72671d that was inadvertently undone by the unicode-safety fix in 29191c0. Both `sync.rs` and `traces.rs` now import the canonical implementation from `utils::duration` instead of carrying their own copies, and `utils/mod.rs` re-exports the helper.
1 parent 232508e commit 960c52e

3 files changed

Lines changed: 3 additions & 80 deletions

File tree

src/sync.rs

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use crate::experiments::api::create_experiment;
2626
use crate::http::ApiClient;
2727
use crate::projects::api::{create_project, list_projects, Project};
2828
use crate::ui::{animations_enabled, fuzzy_select, is_quiet};
29+
use crate::utils::parse_duration_to_seconds;
2930

3031
const STATE_SCHEMA_VERSION: u32 = 1;
3132
const DEFAULT_PULL_LIMIT: usize = 100;
@@ -2606,34 +2607,6 @@ fn build_root_spans_query(
26062607
parts.join(" | ")
26072608
}
26082609

2609-
fn parse_duration_to_seconds(input: &str) -> Result<u64> {
2610-
let trimmed = input.trim();
2611-
if trimmed.is_empty() {
2612-
bail!("duration cannot be empty");
2613-
}
2614-
if let Ok(seconds) = trimmed.parse::<u64>() {
2615-
return Ok(seconds);
2616-
}
2617-
2618-
let suffix = trimmed.chars().last().filter(|ch| ch.is_ascii_alphabetic());
2619-
let (num_str, unit) = match suffix {
2620-
Some(unit) => (&trimmed[..trimmed.len() - unit.len_utf8()], unit),
2621-
None => (trimmed, 's'),
2622-
};
2623-
let value: u64 = num_str
2624-
.trim()
2625-
.parse()
2626-
.with_context(|| format!("invalid duration '{input}'"))?;
2627-
let multiplier = match unit.to_ascii_lowercase() {
2628-
's' => 1,
2629-
'm' => 60,
2630-
'h' => 60 * 60,
2631-
'd' => 60 * 60 * 24,
2632-
_ => bail!("invalid duration '{input}'. expected suffix s/m/h/d"),
2633-
};
2634-
Ok(value.saturating_mul(multiplier))
2635-
}
2636-
26372610
fn build_time_filter_clause(window: &str, extra_filter: Option<&str>) -> Result<String> {
26382611
let seconds = parse_duration_to_seconds(window)?;
26392612
let time_clause = format!("created >= NOW() - INTERVAL {seconds} SECOND");
@@ -4295,14 +4268,6 @@ fn spinner_bar(message: &str) -> ProgressBar {
42954268
mod tests {
42964269
use super::*;
42974270

4298-
#[test]
4299-
fn parse_duration_to_seconds_rejects_non_ascii_suffix_without_panicking() {
4300-
for input in ["1–", "1é", "1🙂"] {
4301-
let err = parse_duration_to_seconds(input).expect_err("invalid unicode suffix");
4302-
assert!(err.to_string().contains("invalid duration"));
4303-
}
4304-
}
4305-
43064271
#[test]
43074272
fn push_checkpoint_line_offset_advances_only_after_commit() {
43084273
let mut state =

src/traces.rs

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use crate::args::BaseArgs;
3636
use crate::auth::{self, login};
3737
use crate::http::ApiClient;
3838
use crate::ui::{fuzzy_select, is_interactive, with_spinner};
39+
use crate::utils::parse_duration_to_seconds;
3940

4041
const MAX_TRACE_SPANS: usize = 5000;
4142
const MAX_BTQL_PAGE_LIMIT: usize = 1000;
@@ -5030,34 +5031,6 @@ fn print_span_text(item: Option<&Map<String, Value>>) {
50305031
}
50315032
}
50325033

5033-
fn parse_duration_to_seconds(input: &str) -> Result<u64> {
5034-
let trimmed = input.trim();
5035-
if trimmed.is_empty() {
5036-
bail!("duration cannot be empty");
5037-
}
5038-
if let Ok(seconds) = trimmed.parse::<u64>() {
5039-
return Ok(seconds);
5040-
}
5041-
5042-
let suffix = trimmed.chars().last().filter(|ch| ch.is_ascii_alphabetic());
5043-
let (num_str, unit) = match suffix {
5044-
Some(unit) => (&trimmed[..trimmed.len() - unit.len_utf8()], unit),
5045-
None => (trimmed, 's'),
5046-
};
5047-
let value: u64 = num_str
5048-
.trim()
5049-
.parse()
5050-
.with_context(|| format!("invalid duration '{input}'"))?;
5051-
let multiplier = match unit.to_ascii_lowercase() {
5052-
's' => 1,
5053-
'm' => 60,
5054-
'h' => 60 * 60,
5055-
'd' => 60 * 60 * 24,
5056-
_ => bail!("invalid duration '{input}'. expected suffix s/m/h/d"),
5057-
};
5058-
Ok(value.saturating_mul(multiplier))
5059-
}
5060-
50615034
fn build_base_filter_clause(
50625035
since: Option<&str>,
50635036
window: &str,
@@ -6183,22 +6156,6 @@ mod tests {
61836156
}
61846157
}
61856158

6186-
#[test]
6187-
fn parse_duration_to_seconds_supports_units() {
6188-
assert_eq!(parse_duration_to_seconds("90").expect("seconds"), 90);
6189-
assert_eq!(parse_duration_to_seconds("15m").expect("minutes"), 900);
6190-
assert_eq!(parse_duration_to_seconds("2h").expect("hours"), 7_200);
6191-
assert_eq!(parse_duration_to_seconds("1d").expect("days"), 86_400);
6192-
}
6193-
6194-
#[test]
6195-
fn parse_duration_to_seconds_rejects_non_ascii_suffix_without_panicking() {
6196-
for input in ["1–", "1é", "1🙂"] {
6197-
let err = parse_duration_to_seconds(input).expect_err("invalid unicode suffix");
6198-
assert!(err.to_string().contains("invalid duration"));
6199-
}
6200-
}
6201-
62026159
#[test]
62036160
fn build_base_filter_clause_uses_window_or_since() {
62046161
let from_window = build_base_filter_clause(None, "1h", Some("metadata.model IS NOT NULL"))

src/utils/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ mod ids;
55
mod json_object;
66
mod plurals;
77

8+
pub use duration::parse_duration_to_seconds;
89
pub use fs_atomic::write_text_atomic;
910
pub use git::GitRepo;
1011
pub(crate) use ids::new_uuid_id;

0 commit comments

Comments
 (0)