Current section

Files

Jump to
lap src lap.gleam
Raw

src/lap.gleam

import birl.{type Time}
import birl/duration.{type Unit}
import gleam/int
import gleam/list
import tobble
pub opaque type LapData {
LapData(
first_marker: String,
first_time: Time,
last_time: Time,
duration_unit: Unit,
intervals: List(IntervalData),
)
}
pub opaque type IntervalData {
IntervalData(start_marker: String, end_marker: String, duration: Int)
}
type IntervalTuple =
#(String, String, Int)
pub fn start_in_microseconds(marker: String) -> LapData {
start(marker, duration.MicroSecond)
}
pub fn start_in_milliseconds(marker: String) -> LapData {
start(marker, duration.MilliSecond)
}
pub fn start_in_seconds(marker: String) -> LapData {
start(marker, duration.Second)
}
pub fn start_in_minutes(marker: String) -> LapData {
start(marker, duration.Minute)
}
pub fn start(marker: String, duration_unit: Unit) -> LapData {
// Warmup - I noticed in my testing that the first interval was always
// slightly longer than the others and that by calling birl.now, the intervals
// are more consistent.
list.repeat(birl.now(), 3)
start_with_time(marker, duration_unit, birl.now())
}
@internal
pub fn start_with_time(
marker: String,
duration_unit: Unit,
time: Time,
) -> LapData {
LapData(
first_marker: marker,
first_time: time,
last_time: time,
duration_unit:,
intervals: [],
)
}
pub fn time(data: LapData, marker: String) -> LapData {
time_with_time(data, marker, birl.now())
}
@internal
pub fn time_with_time(data: LapData, marker: String, time: Time) -> LapData {
let #(start_marker, end_marker) = case data.intervals {
[] -> #(data.first_marker, marker)
[previous_interval, ..] -> #(previous_interval.end_marker, marker)
}
LapData(
..data,
last_time: time,
intervals: [
IntervalData(
start_marker,
end_marker,
birl.difference(time, data.last_time)
|> duration.blur_to(data.duration_unit),
),
..data.intervals
],
)
}
pub fn total_time(data: LapData) -> Int {
data.last_time
|> birl.difference(data.first_time)
|> duration.blur_to(data.duration_unit)
}
pub fn sort_max(data: LapData) -> LapData {
let intervals =
data.intervals
|> list.sort(fn(a, b) { int.compare(a.duration, b.duration) })
LapData(..data, intervals: intervals)
}
pub fn intervals(data: LapData) -> List(IntervalTuple) {
data.intervals
|> list.map(fn(interval) {
#(interval.start_marker, interval.end_marker, interval.duration)
})
|> list.reverse
}
pub fn pretty_print(data: LapData) -> String {
let duration_unit_text = case data.duration_unit {
duration.MicroSecond -> "μs"
duration.MilliSecond -> "ms"
duration.Second -> "s"
duration.Minute -> "min"
duration.Hour -> "h"
duration.Day -> "d"
duration.Week -> "w"
duration.Month -> "mo"
duration.Year -> "y"
}
let builder =
tobble.builder()
|> tobble.add_row(["Start", "End", "Interval"])
let table =
data
|> intervals
|> list.fold(builder, fn(builder, interval) {
builder
|> tobble.add_row([
interval.0,
interval.1,
{ interval.2 |> int.to_string } <> " " <> duration_unit_text,
])
})
|> tobble.build
case table {
Ok(table) -> table |> tobble.render
Error(_) -> ""
}
}