Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
281 changes: 145 additions & 136 deletions crates/timestamp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,187 +1,196 @@
use std::time::{Duration, Instant, SystemTime};
#[cfg(target_os = "macos")]
mod macos;

#[cfg(windows)]
#[cfg(target_os = "windows")]
mod win;
#[cfg(windows)]
pub use win::*;

#[cfg(target_os = "macos")]
mod macos;
mod master_clock;

use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};

use serde::{Deserialize, Serialize};
use specta::Type;
Comment on lines +11 to +12

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 New dependencies are missing

serde and specta are imported without being declared in crates/timestamp/Cargo.toml. Neither is available as a direct dependency, so cap-timestamp fails to compile on every platform. Add both dependencies or remove the new imports and derives.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/timestamp/src/lib.rs
Line: 11-12

Comment:
**New dependencies are missing**

`serde` and `specta` are imported without being declared in `crates/timestamp/Cargo.toml`. Neither is available as a direct dependency, so `cap-timestamp` fails to compile on every platform. Add both dependencies or remove the new imports and derives.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.


#[cfg(target_os = "macos")]
pub use macos::*;

mod master_clock;
pub use master_clock::*;
#[cfg(target_os = "windows")]
pub use win::*;

/// Captures anchor timestamps at the same instant, allowing later
/// conversions from any timestamp type.
#[derive(Debug, Clone, Copy)]
pub struct Timestamps {
instant: Instant,
system_time: SystemTime,
#[cfg(target_os = "macos")]
mach_absolute_time: MachAbsoluteTimestamp,
#[cfg(target_os = "windows")]
performance_counter: PerformanceCounterTimestamp,
}

#[derive(Clone, Copy, Debug)]
impl Timestamps {
pub fn now() -> Self {
Self {
instant: Instant::now(),
system_time: SystemTime::now(),
#[cfg(target_os = "macos")]
mach_absolute_time: MachAbsoluteTimestamp::now(),
#[cfg(target_os = "windows")]
performance_counter: PerformanceCounterTimestamp::now(),
}
}

pub fn from_wall_and_instant(system_time: SystemTime, instant: Instant) -> Self {
Self {
instant,
system_time,
#[cfg(target_os = "macos")]
mach_absolute_time: MachAbsoluteTimestamp::now(),
#[cfg(target_os = "windows")]
performance_counter: PerformanceCounterTimestamp::now(),
}
}

pub fn instant(&self) -> Instant {
self.instant
}

pub fn system_time(&self) -> SystemTime {
self.system_time
}

#[cfg(target_os = "macos")]
pub fn mach_absolute_time(&self) -> MachAbsoluteTimestamp {
self.mach_absolute_time
}

#[cfg(target_os = "windows")]
pub fn performance_counter(&self) -> PerformanceCounterTimestamp {
self.performance_counter
}
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy, Type)]
pub struct SystemTimeStamp {
value: Duration,
}

#[derive(Debug, Clone, Copy)]
pub enum Timestamp {
Instant(Instant),
SystemTime(SystemTime),
#[cfg(windows)]
#[cfg(target_os = "windows")]
PerformanceCounter(PerformanceCounterTimestamp),
#[cfg(target_os = "macos")]
MachAbsoluteTime(MachAbsoluteTimestamp),
}

impl Timestamp {
pub fn duration_since(&self, start: Timestamps) -> Duration {
match self {
Self::Instant(instant) => instant.duration_since(start.instant),
Self::SystemTime(time) => time.duration_since(start.system_time).unwrap(),
#[cfg(windows)]
Self::PerformanceCounter(counter) => counter.duration_since(start.performance_counter),
#[cfg(target_os = "macos")]
Self::MachAbsoluteTime(time) => time.duration_since(start.mach_absolute_time),
}
}

pub fn checked_duration_since(&self, start: Timestamps) -> Option<Duration> {
match self {
Self::Instant(instant) => instant.checked_duration_since(start.instant),
Self::SystemTime(time) => time.duration_since(start.system_time).ok(),
#[cfg(windows)]
Self::PerformanceCounter(counter) => {
counter.checked_duration_since(start.performance_counter)
pub fn duration_since(&self, start: Timestamps) -> Option<Duration> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Capture timestamp calls break

Timestamp::duration_since now returns Option<Duration>, but existing callers still use its result as a Duration. For example, AudioMixer::detect_laggard_source calls .checked_sub(...) on it. The rewrite also removes Timestamp::from_cpal, checked_duration_since, timestamp arithmetic, and PerformanceCounterTimestamp::from_100ns while capture code still calls them.

Preserve these APIs while adding nanosecond conversion, or update their callers together. Otherwise, the recording crate cannot compile.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/timestamp/src/lib.rs
Line: 90

Comment:
**Capture timestamp calls break**

`Timestamp::duration_since` now returns `Option<Duration>`, but existing callers still use its result as a `Duration`. For example, `AudioMixer::detect_laggard_source` calls `.checked_sub(...)` on it. The rewrite also removes `Timestamp::from_cpal`, `checked_duration_since`, timestamp arithmetic, and `PerformanceCounterTimestamp::from_100ns` while capture code still calls them.

Preserve these APIs while adding nanosecond conversion, or update their callers together. Otherwise, the recording crate cannot compile.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Some(match self {
Timestamp::Instant(t) => t.checked_duration_since(start.instant())?,
Timestamp::SystemTime(t) => t.duration_since(start.system_time()).ok()?,
#[cfg(target_os = "windows")]
Timestamp::PerformanceCounter(t) => {
t.checked_duration_since(start.performance_counter())?
}
#[cfg(target_os = "macos")]
Self::MachAbsoluteTime(time) => time.checked_duration_since(start.mach_absolute_time),
}
Timestamp::MachAbsoluteTime(t) => {
t.checked_duration_since(start.mach_absolute_time())?
}
})
}

pub fn signed_duration_since_secs(&self, start: Timestamps) -> f64 {
/// Returns the signed elapsed duration in nanoseconds since `start`.
/// Negative if `self` is earlier than `start`.
Comment on lines +105 to +106

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Comments repeat the code

The new documentation comments only restate the method name and return value. AGENTS.md requires comments to capture non-obvious context and explicitly bans comments that paraphrase parameter names or narrate the code. Remove these comments or explain a non-obvious timing constraint instead. The same pattern appears above signed_duration_since_secs and elsewhere in the rewrite. This repository requirement must be satisfied before merging.

Context Used: AGENTS.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/timestamp/src/lib.rs
Line: 105-106

Comment:
**Comments repeat the code**

The new documentation comments only restate the method name and return value. `AGENTS.md` requires comments to capture non-obvious context and explicitly bans comments that paraphrase parameter names or narrate the code. Remove these comments or explain a non-obvious timing constraint instead. The same pattern appears above `signed_duration_since_secs` and elsewhere in the rewrite. This repository requirement must be satisfied before merging.

**Context Used:** AGENTS.md ([source](https://github.com/capsoftware/cap/blob/main/AGENTS.md))

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

pub fn signed_duration_since_nanos(&self, start: Timestamps) -> i128 {
match self {
Self::Instant(instant) => {
if let Some(duration) = instant.checked_duration_since(start.instant) {
duration.as_secs_f64()
Timestamp::Instant(t) => {
if let Some(d) = t.checked_duration_since(start.instant()) {
d.as_nanos() as i128
} else if let Some(d) = start.instant().checked_duration_since(*t) {
-(d.as_nanos() as i128)
} else {
let reverse = start.instant.duration_since(*instant);
-(reverse.as_secs_f64())
0
}
}
Self::SystemTime(time) => match time.duration_since(start.system_time) {
Ok(duration) => duration.as_secs_f64(),
Err(e) => -(e.duration().as_secs_f64()),
Timestamp::SystemTime(t) => match t.duration_since(start.system_time()) {
Ok(d) => d.as_nanos() as i128,
Err(e) => -(e.duration().as_nanos() as i128),
},
#[cfg(windows)]
Self::PerformanceCounter(counter) => {
counter.signed_duration_since_secs(start.performance_counter)
#[cfg(target_os = "windows")]
Timestamp::PerformanceCounter(t) => {
t.signed_duration_since_nanos(start.performance_counter())
}
#[cfg(target_os = "macos")]
Self::MachAbsoluteTime(time) => {
time.signed_duration_since_secs(start.mach_absolute_time)
Timestamp::MachAbsoluteTime(t) => {
t.signed_duration_since_nanos(start.mach_absolute_time())
}
}
}

pub fn from_cpal(instant: cpal::StreamInstant) -> Self {
#[cfg(windows)]
{
Self::PerformanceCounter(PerformanceCounterTimestamp::from_cpal(instant))
}
#[cfg(target_os = "macos")]
{
Self::MachAbsoluteTime(MachAbsoluteTimestamp::from_cpal(instant))
}
#[cfg(not(any(windows, target_os = "macos")))]
{
let _ = instant;
Self::Instant(Instant::now())
}
}
}

impl std::ops::Add<Duration> for &Timestamp {
type Output = Timestamp;

fn add(self, rhs: Duration) -> Self::Output {
match *self {
Timestamp::Instant(i) => Timestamp::Instant(i + rhs),
Timestamp::SystemTime(t) => Timestamp::SystemTime(t + rhs),
#[cfg(windows)]
Timestamp::PerformanceCounter(c) => Timestamp::PerformanceCounter(c + rhs),
#[cfg(target_os = "macos")]
Timestamp::MachAbsoluteTime(c) => Timestamp::MachAbsoluteTime(c + rhs),
}
}
}

impl std::ops::Add<Duration> for Timestamp {
type Output = Timestamp;

fn add(self, rhs: Duration) -> Self::Output {
/// Returns the signed elapsed duration in seconds since `start`.
/// Negative if `self` is earlier than `start`.
pub fn signed_duration_since_secs(&self, start: Timestamps) -> f64 {
match self {
Timestamp::Instant(i) => Timestamp::Instant(i + rhs),
Timestamp::SystemTime(t) => Timestamp::SystemTime(t + rhs),
#[cfg(windows)]
Timestamp::PerformanceCounter(c) => Timestamp::PerformanceCounter(c + rhs),
Timestamp::Instant(_) | Timestamp::SystemTime(_) => {
self.signed_duration_since_nanos(start) as f64 / 1_000_000_000.0
}
#[cfg(target_os = "windows")]
Timestamp::PerformanceCounter(t) => {
t.signed_duration_since_secs(start.performance_counter())
}
#[cfg(target_os = "macos")]
Timestamp::MachAbsoluteTime(c) => Timestamp::MachAbsoluteTime(c + rhs),
Timestamp::MachAbsoluteTime(t) => {
t.signed_duration_since_secs(start.mach_absolute_time())
}
}
}
}

impl std::ops::Sub<Duration> for Timestamp {
type Output = Timestamp;

fn sub(self, rhs: Duration) -> Self::Output {
pub fn duration_since_unix_epoch(&self) -> Duration {
match self {
Timestamp::Instant(i) => Timestamp::Instant(i.checked_sub(rhs).unwrap_or(i)),
Timestamp::SystemTime(t) => Timestamp::SystemTime(t - rhs),
#[cfg(windows)]
Timestamp::PerformanceCounter(c) => Timestamp::PerformanceCounter(c - rhs),
Timestamp::Instant(t) => {
let now = Timestamps::now();
let duration_since = now.instant().duration_since(*t);
let system_time = now.system_time() - duration_since;
system_time
.duration_since(UNIX_EPOCH)
.expect("insight into the past is not supported")
}
Timestamp::SystemTime(t) => t
.duration_since(UNIX_EPOCH)
.expect("insight into the past is not supported"),
#[cfg(target_os = "windows")]
Timestamp::PerformanceCounter(t) => t.duration_since_unix_epoch(),
#[cfg(target_os = "macos")]
Timestamp::MachAbsoluteTime(c) => Timestamp::MachAbsoluteTime(c - rhs),
Timestamp::MachAbsoluteTime(t) => t.duration_since_unix_epoch(),
}
}
}

#[derive(Clone, Copy, Debug)]
pub struct Timestamps {
instant: Instant,
system_time: SystemTime,
#[cfg(windows)]
performance_counter: PerformanceCounterTimestamp,
#[cfg(target_os = "macos")]
mach_absolute_time: MachAbsoluteTimestamp,
}

impl Timestamps {
pub fn now() -> Self {
Self {
instant: Instant::now(),
system_time: SystemTime::now(),
#[cfg(windows)]
performance_counter: PerformanceCounterTimestamp::now(),
#[cfg(target_os = "macos")]
mach_absolute_time: MachAbsoluteTimestamp::now(),
}
}

pub fn instant(&self) -> Instant {
self.instant
impl From<Instant> for Timestamp {
fn from(instant: Instant) -> Self {
Timestamp::Instant(instant)
}
}

pub fn system_time(&self) -> SystemTime {
self.system_time
impl From<SystemTime> for Timestamp {
fn from(system_time: SystemTime) -> Self {
Timestamp::SystemTime(system_time)
}
}

#[cfg(target_os = "macos")]
pub fn mach_absolute_time(&self) -> MachAbsoluteTimestamp {
self.mach_absolute_time
#[cfg(target_os = "windows")]
impl From<PerformanceCounterTimestamp> for Timestamp {
fn from(performance_counter: PerformanceCounterTimestamp) -> Self {
Timestamp::PerformanceCounter(performance_counter)
}
}

impl std::ops::Add<Duration> for Timestamps {
type Output = Timestamps;

fn add(self, rhs: Duration) -> Self::Output {
Self {
instant: self.instant + rhs,
system_time: self.system_time + rhs,
#[cfg(windows)]
performance_counter: self.performance_counter + rhs,
#[cfg(target_os = "macos")]
mach_absolute_time: self.mach_absolute_time + rhs,
}
#[cfg(target_os = "macos")]
impl From<MachAbsoluteTimestamp> for Timestamp {
fn from(mach_absolute_time: MachAbsoluteTimestamp) -> Self {
Timestamp::MachAbsoluteTime(mach_absolute_time)
}
}
Loading