Packages
mist
4.0.6
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/atom
import gleam/erlang/process.{type Pid}
import gleam/function
import gleam/int
import gleam/otp/actor
import gleam/result
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(_type, _args) -> Result(Pid, actor.StartError) {
actor.start_spec(
actor.Spec(
init: fn() {
let subj = process.new_subject()
let selector =
process.new_selector()
|> process.selecting(subj, function.identity)
ets_new(MistClock, [Set, Protected, NamedTable, ReadConcurrency(True)])
process.send(subj, SetTime)
actor.Ready(subj, selector)
},
init_timeout: 500,
loop: fn(msg, state) {
case msg {
SetTime -> {
ets_insert(MistClock, #(DateHeader, date()))
process.send_after(state, 1000, SetTime)
actor.continue(state)
}
}
},
),
)
|> result.map(process.subject_owner)
}
pub fn stop(_state) {
atom.create_from_string("ok")
}
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)