Packages
mist
5.0.0
6.0.3
6.0.2
6.0.1
6.0.0
5.0.4
5.0.3
5.0.2
5.0.1
5.0.0
5.0.0-rc1
4.0.7
4.0.6
4.0.5
4.0.4
4.0.3
4.0.2
4.0.1
4.0.0
3.0.0
2.0.0
1.2.0
1.1.0
1.0.0
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
0.17.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.2
0.13.1
0.13.0
0.12.0
0.11.0
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.3
0.8.2
0.8.1
0.8.0
0.7.1
0.7.0
0.6.1
0.6.0
0.5.2
0.5.1
0.5.0
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.1
0.2.0
0.1.3
a misty Gleam web server
Current section
Files
Jump to
Current section
Files
src/mist/internal/clock.gleam
import gleam/erlang/process.{type Subject}
import gleam/int
import gleam/otp/actor
import gleam/string
import logging
pub type ClockMessage {
SetTime
}
type ClockTable {
MistClock
}
type TableKey {
DateHeader
}
pub type EtsOpts {
Set
Protected
NamedTable
ReadConcurrency(Bool)
}
pub fn start(
name: process.Name(ClockMessage),
) -> Result(actor.Started(Subject(ClockMessage)), actor.StartError) {
actor.new_with_initialiser(500, fn(subject) {
ets_new(MistClock, [Set, Protected, NamedTable, ReadConcurrency(True)])
process.send(subject, SetTime)
subject
|> actor.initialised
|> actor.selecting(process.new_selector() |> process.select(subject))
|> actor.returning(subject)
|> Ok
})
|> actor.on_message(fn(state, msg) {
case msg {
SetTime -> {
ets_insert(MistClock, #(DateHeader, date()))
process.send_after(state, 1000, SetTime)
actor.continue(state)
}
}
})
|> actor.named(name)
|> actor.start
}
pub fn get_date() -> String {
case ets_lookup_element(MistClock, DateHeader, 2) {
Ok(value) -> value
_ -> {
logging.log(logging.Warning, "Failed to lookup date, re-calculating")
date()
}
}
}
/// Returns today's date in a format suitable to be used as an http date header
/// (see here: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Date).
///
fn date() -> String {
let #(weekday, #(year, month, day), #(hour, minute, second)) = now()
let weekday = weekday_to_short_string(weekday)
let year = int.to_string(year) |> string.pad_start(to: 4, with: "0")
let month = month_to_short_string(month)
let day = int.to_string(day) |> string.pad_start(to: 2, with: "0")
let hour = int.to_string(hour) |> string.pad_start(to: 2, with: "0")
let minute = int.to_string(minute) |> string.pad_start(to: 2, with: "0")
let second = int.to_string(second) |> string.pad_start(to: 2, with: "0")
{ weekday <> ", " }
<> { day <> " " <> month <> " " <> year <> " " }
<> { hour <> ":" <> minute <> ":" <> second <> " GMT" }
}
fn weekday_to_short_string(weekday: Int) -> String {
case weekday {
1 -> "Mon"
2 -> "Tue"
3 -> "Wed"
4 -> "Thu"
5 -> "Fri"
6 -> "Sat"
7 -> "Sun"
_ -> panic as "erlang weekday outside of 1-7 range"
}
}
fn month_to_short_string(month: Int) -> String {
case month {
1 -> "Jan"
2 -> "Feb"
3 -> "Mar"
4 -> "Apr"
5 -> "May"
6 -> "Jun"
7 -> "Jul"
8 -> "Aug"
9 -> "Sep"
10 -> "Oct"
11 -> "Nov"
12 -> "Dec"
_ -> panic as "erlang month outside of 1-12 range"
}
}
@external(erlang, "mist_ffi", "now")
fn now() -> #(Int, #(Int, Int, Int), #(Int, Int, Int))
@external(erlang, "ets", "new")
fn ets_new(table: ClockTable, opts: List(EtsOpts)) -> ClockTable
@external(erlang, "ets", "insert")
fn ets_insert(table: ClockTable, value: #(TableKey, String)) -> Nil
@external(erlang, "mist_ffi", "ets_lookup_element")
fn ets_lookup_element(
table: ClockTable,
key: TableKey,
position: Int,
) -> Result(String, Nil)