Skip to content
This repository has been archived by the owner on Jun 8, 2024. It is now read-only.

Commit

Permalink
support zero-width span extents
Browse files Browse the repository at this point in the history
  • Loading branch information
KodrAus committed Nov 14, 2023
1 parent 96bc87f commit 6df2bcb
Show file tree
Hide file tree
Showing 9 changed files with 89 additions and 128 deletions.
10 changes: 5 additions & 5 deletions core/src/clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,22 +93,22 @@ impl<C: Clock> Timer<C> {
}
}

pub fn extent(&self) -> Extent {
pub fn extent(&self) -> Option<Extent> {
let end = self.clock.now();

match (self.start, end) {
(Some(start), Some(end)) => Extent::new(start..end),
_ => Extent::empty(),
(Some(start), Some(end)) => Extent::span(start..end),
_ => None,
}
}

pub fn elapsed(&self) -> Option<Duration> {
self.extent().len()
self.extent().map(|extent| extent.len())
}
}

impl<C: Clock> ToExtent for Timer<C> {
fn to_extent(&self) -> Extent {
fn to_extent(&self) -> Option<Extent> {
self.extent()
}
}
6 changes: 3 additions & 3 deletions core/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::{

#[derive(Clone)]
pub struct Event<'a, P> {
extent: Extent,
extent: Option<Extent>,
tpl: Template<'a>,
props: P,
}
Expand All @@ -22,8 +22,8 @@ impl<'a, P> Event<'a, P> {
}
}

pub fn extent(&self) -> &Extent {
&self.extent
pub fn extent(&self) -> Option<&Extent> {
self.extent.as_ref()
}

pub fn tpl(&self) -> Template {
Expand Down
132 changes: 50 additions & 82 deletions core/src/extent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,156 +2,124 @@ use crate::{empty::Empty, timestamp::Timestamp};
use core::{fmt, ops::Range, time::Duration};

#[derive(Debug, Clone)]
pub struct Extent(Option<Range<Timestamp>>);
pub struct Extent {
range: Range<Timestamp>,
is_span: bool,
}

impl Extent {
pub fn new(ts: Range<Timestamp>) -> Self {
Extent(Some(ts))
}

pub fn point(ts: Timestamp) -> Self {
Extent(Some(ts..ts))
Extent {
range: ts..ts,
is_span: false,
}
}

pub fn empty() -> Self {
Extent(None)
pub fn span(ts: Range<Timestamp>) -> Option<Self> {
if ts.start > ts.end {
None
} else {
Some(Extent {
range: ts,
is_span: true,
})
}
}

pub fn as_range(&self) -> Option<&Range<Timestamp>> {
self.0.as_ref()
pub fn as_range(&self) -> &Range<Timestamp> {
&self.range
}

pub fn as_point(&self) -> Option<&Timestamp> {
if self.is_point() {
self.to_point()
Some(&self.range.end)
} else {
None
}
}

pub fn to_point(&self) -> Option<&Timestamp> {
Some(&self.0.as_ref()?.end)
pub fn to_point(&self) -> &Timestamp {
&self.range.end
}

pub fn as_span(&self) -> Option<&Range<Timestamp>> {
let ts = self.0.as_ref()?;

if ts.start != ts.end {
Some(ts)
if self.is_span() {
Some(&self.range)
} else {
None
}
}

pub fn len(&self) -> Option<Duration> {
let ts = self.0.as_ref()?;

ts.end.duration_since(ts.start)
pub fn len(&self) -> Duration {
self.range
.end
.duration_since(self.range.start)
.expect("end is always after start")
}

pub fn is_point(&self) -> bool {
self.0
.as_ref()
.map(|ts| ts.start == ts.end)
.unwrap_or(false)
!self.is_span()
}

pub fn is_span(&self) -> bool {
self.0
.as_ref()
.map(|ts| ts.start != ts.end)
.unwrap_or(false)
}

pub fn is_empty(&self) -> bool {
self.0.is_none()
}

pub fn or_else<T: ToExtent>(&self, or_else: impl FnOnce() -> T) -> Self {
if self.0.is_none() {
or_else().to_extent()
} else {
self.clone()
}
}
}

impl From<Timestamp> for Extent {
fn from(value: Timestamp) -> Self {
Extent::point(value)
}
}

impl From<Range<Timestamp>> for Extent {
fn from(value: Range<Timestamp>) -> Self {
Extent::new(value)
}
}

impl From<Option<Range<Timestamp>>> for Extent {
fn from(value: Option<Range<Timestamp>>) -> Self {
Extent(value)
self.is_span
}
}

impl fmt::Display for Extent {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
Some(ref ts) if ts.start != ts.end => {
fmt::Display::fmt(&ts.start, f)?;
f.write_str("..")?;
fmt::Display::fmt(&ts.end, f)
}
Some(ref ts) => fmt::Display::fmt(&ts.end, f),
None => Ok(()),
if self.is_span() {
fmt::Display::fmt(&self.range.start, f)?;
f.write_str("..")?;
fmt::Display::fmt(&self.range.end, f)
} else {
fmt::Display::fmt(&self.range.end, f)
}
}
}

pub trait ToExtent {
fn to_extent(&self) -> Extent;
fn to_extent(&self) -> Option<Extent>;
}

impl<'a, T: ToExtent + ?Sized> ToExtent for &'a T {
fn to_extent(&self) -> Extent {
fn to_extent(&self) -> Option<Extent> {
(**self).to_extent()
}
}

impl ToExtent for Empty {
fn to_extent(&self) -> Extent {
Extent::empty()
fn to_extent(&self) -> Option<Extent> {
None
}
}

impl<T: ToExtent> ToExtent for Option<T> {
fn to_extent(&self) -> Extent {
self.as_ref()
.map(|ts| ts.to_extent())
.unwrap_or_else(Extent::empty)
fn to_extent(&self) -> Option<Extent> {
self.as_ref().and_then(|ts| ts.to_extent())
}
}

impl ToExtent for Extent {
fn to_extent(&self) -> Extent {
self.clone()
fn to_extent(&self) -> Option<Extent> {
Some(self.clone())
}
}

impl ToExtent for Timestamp {
fn to_extent(&self) -> Extent {
Extent::point(*self)
fn to_extent(&self) -> Option<Extent> {
Some(Extent::point(*self))
}
}

impl ToExtent for Range<Timestamp> {
fn to_extent(&self) -> Extent {
Extent::new(self.clone())
fn to_extent(&self) -> Option<Extent> {
Extent::span(self.clone())
}
}

impl ToExtent for Range<Option<Timestamp>> {
fn to_extent(&self) -> Extent {
fn to_extent(&self) -> Option<Extent> {
match (self.start, self.end) {
(Some(start), Some(end)) => (start..end).to_extent(),
(Some(start), None) => start.to_extent(),
Expand Down
32 changes: 18 additions & 14 deletions core/src/timestamp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ use core::{cmp, fmt, str, str::FromStr, time::Duration};

use crate::value::{ToValue, Value};

// 2000-03-01 (mod 400 year, immediately after feb29
const LEAPOCH_SECS: u64 = 946_684_800 + 86400 * (31 + 29);
const DAYS_PER_400Y: u32 = 365 * 400 + 97;
const DAYS_PER_100Y: u32 = 365 * 100 + 24;
const DAYS_PER_4Y: u32 = 365 * 4 + 1;
const DAYS_IN_MONTH: [u8; 12] = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Timestamp(Duration);

Expand All @@ -16,8 +23,12 @@ pub struct Parts {
}

impl Timestamp {
pub fn new(unix_time: Duration) -> Self {
Timestamp(unix_time)
pub fn new(unix_time: Duration) -> Option<Self> {
if unix_time.as_secs() < LEAPOCH_SECS {
None
} else {
Some(Timestamp(unix_time))
}
}

pub fn as_unix_time(&self) -> &Duration {
Expand All @@ -44,15 +55,7 @@ impl Timestamp {
let secs: u64 = dur.as_secs();
let subsecond_nanos = dur.subsec_nanos();

// 2000-03-01 (mod 400 year, immediately after feb29
const LEAPOCH: u64 = 946_684_800 + 86400 * (31 + 29);
const DAYS_PER_400Y: u32 = 365 * 400 + 97;
const DAYS_PER_100Y: u32 = 365 * 100 + 24;
const DAYS_PER_4Y: u32 = 365 * 4 + 1;
const DAYS_IN_MONTH: [u8; 12] = [31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29];

// Note(dcb): this bit is rearranged slightly to avoid integer overflow.
let days: u64 = (secs / 86_400) - (LEAPOCH / 86_400);
let days: u64 = (secs / 86_400) - (LEAPOCH_SECS / 86_400);
let remsecs: u32 = (secs % 86_400) as u32;

let qc_cycles: u32 = (days / u64::from(DAYS_PER_400Y)) as u32;
Expand Down Expand Up @@ -261,12 +264,13 @@ fn parse_rfc3339(fmt: &str) -> Result<Timestamp, ParseTimestampError> {
seconds_within_year += 86400
}

Ok(Timestamp::new(Duration::new(
Timestamp::new(Duration::new(
(start_of_year + i128::from(seconds_within_year))
.try_into()
.map_err(|_| ParseTimestampError {})?,
nanos,
)))
))
.ok_or_else(|| ParseTimestampError {})
}

fn fmt_rfc3339(ts: Timestamp, f: &mut fmt::Formatter) -> fmt::Result {
Expand Down Expand Up @@ -328,7 +332,7 @@ mod tests {

#[test]
fn timestamp_roundtrip() {
let ts = Timestamp::new(Duration::new(1691961703, 17532));
let ts = Timestamp::new(Duration::new(1691961703, 17532)).unwrap();

let fmt = ts.to_string();

Expand Down
10 changes: 2 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,9 @@ pub fn emit(evt: &Event<impl Props>) {

let tpl = evt.tpl();
let props = evt.props();
let extent = evt.extent().cloned().or_else(|| ambient.now().to_extent());

base_emit(
ambient,
ambient,
ambient,
evt.extent().or_else(|| ambient.now()),
tpl,
props,
);
base_emit(ambient, ambient, ambient, extent, tpl, props);
}

pub type With = LocalFrame<emit_core::ambient::Get>;
Expand Down
4 changes: 2 additions & 2 deletions src/macro_hooks.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::{any::Any, fmt, ops::ControlFlow, panic::Location};
use core::{any::Any, fmt, ops::ControlFlow};

use emit_core::{
ambient,
Expand Down Expand Up @@ -419,7 +419,7 @@ pub fn __private_emit(
to.and(ambient),
when.and(ambient),
ambient,
extent.to_extent().or_else(|| ambient.now()),
extent.to_extent().or_else(|| ambient.now().to_extent()),
tpl,
props,
);
Expand Down
8 changes: 1 addition & 7 deletions src/platform/system_clock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,8 @@ use emit_core::{clock::Clock, timestamp::Timestamp};
#[derive(Default, Debug, Clone, Copy)]
pub struct SystemClock;

impl SystemClock {
pub fn now() -> Timestamp {
Timestamp::new(std::time::UNIX_EPOCH.elapsed().unwrap_or_default())
}
}

impl Clock for SystemClock {
fn now(&self) -> Option<Timestamp> {
Some(Self::now())
Timestamp::new(std::time::UNIX_EPOCH.elapsed().unwrap_or_default())
}
}
3 changes: 1 addition & 2 deletions targets/otlp/src/logs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ impl emit_core::emitter::Emitter for OtlpLogsEmitter {
fn emit<P: emit_core::props::Props>(&self, evt: &emit_core::event::Event<P>) {
let time_unix_nano = evt
.extent()
.as_point()
.map(|ts| ts.as_unix_time().as_nanos() as u64)
.map(|extent| extent.to_point().as_unix_time().as_nanos() as u64)
.unwrap_or_default();

let observed_time_unix_nano = time_unix_nano;
Expand Down
Loading

0 comments on commit 6df2bcb

Please sign in to comment.