Move date and timestamp to separate model files
This commit is contained in:
parent
222bbafd5c
commit
520b5d380c
19 changed files with 189 additions and 282 deletions
|
@ -1,8 +1,7 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/option.{type Option}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/account_id.{type AccountId}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type Account {
|
||||
Account(
|
||||
|
@ -25,6 +24,6 @@ pub fn decoder() -> Decoder(Account) {
|
|||
option.None,
|
||||
decode.optional(decode.string),
|
||||
)
|
||||
use created_at <- decode.field("createdAt", time.rfc3339_timestamp_decoder())
|
||||
use created_at <- decode.field("createdAt", timestamp.decoder())
|
||||
decode.success(Account(id:, email:, token:, created_at:))
|
||||
}
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import gleam/dynamic.{type Dynamic}
|
||||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/option.{type Option}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/agent_event_id.{type AgentEventId}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type AgentEvent {
|
||||
AgentEvent(
|
||||
|
@ -24,6 +23,6 @@ pub fn decoder() -> Decoder(AgentEvent) {
|
|||
option.None,
|
||||
decode.optional(decode.dynamic),
|
||||
)
|
||||
use created_at <- decode.field("createdAt", time.rfc3339_timestamp_decoder())
|
||||
use created_at <- decode.field("createdAt", timestamp.decoder())
|
||||
decode.success(AgentEvent(id:, type_:, message:, data:, created_at:))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/agent_symbol.{type AgentSymbol}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
pub type Chart {
|
||||
|
@ -18,9 +17,6 @@ pub fn decoder() -> Decoder(Chart) {
|
|||
waypoint_symbol.decoder(),
|
||||
)
|
||||
use submitted_by <- decode.field("submittedBy", agent_symbol.decoder())
|
||||
use submitted_on <- decode.field(
|
||||
"submittedOn",
|
||||
time.rfc3339_timestamp_decoder(),
|
||||
)
|
||||
use submitted_on <- decode.field("submittedOn", timestamp.decoder())
|
||||
decode.success(Chart(waypoint_symbol:, submitted_by:, submitted_on:))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
pub type ChartTransaction {
|
||||
|
@ -20,7 +19,7 @@ pub fn decoder() -> Decoder(ChartTransaction) {
|
|||
)
|
||||
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||
use total_price <- decode.field("totalPrice", decode.int)
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(ChartTransaction(
|
||||
waypoint_symbol:,
|
||||
ship_symbol:,
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/option.{type Option}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/contract_id.{type ContractId}
|
||||
import spacetraders_models/contract_terms.{type ContractTerms}
|
||||
import spacetraders_models/contract_type.{type ContractType}
|
||||
import spacetraders_models/faction_symbol.{type FactionSymbol}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type Contract {
|
||||
Contract(
|
||||
|
@ -29,7 +28,7 @@ pub fn decoder() -> Decoder(Contract) {
|
|||
use deadline_to_accept <- decode.optional_field(
|
||||
"deadlineToAccept",
|
||||
option.None,
|
||||
decode.optional(time.rfc3339_timestamp_decoder()),
|
||||
decode.optional(timestamp.decoder()),
|
||||
)
|
||||
decode.success(Contract(
|
||||
id:,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/option.{type Option}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/contract_deliver_good.{type ContractDeliverGood}
|
||||
import spacetraders_models/contract_payment.{type ContractPayment}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type ContractTerms {
|
||||
ContractTerms(
|
||||
|
@ -14,7 +13,7 @@ pub type ContractTerms {
|
|||
}
|
||||
|
||||
pub fn decoder() -> Decoder(ContractTerms) {
|
||||
use deadline <- decode.field("deadline", time.rfc3339_timestamp_decoder())
|
||||
use deadline <- decode.field("deadline", timestamp.decoder())
|
||||
use payment <- decode.field("payment", contract_payment.decoder())
|
||||
use deliver <- decode.optional_field(
|
||||
"deliver",
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/option.{type Option}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type Cooldown {
|
||||
Cooldown(
|
||||
|
@ -20,7 +19,7 @@ pub fn decoder() -> Decoder(Cooldown) {
|
|||
use expiration <- decode.optional_field(
|
||||
"expiration",
|
||||
option.None,
|
||||
decode.optional(time.rfc3339_timestamp_decoder()),
|
||||
decode.optional(timestamp.decoder()),
|
||||
)
|
||||
decode.success(Cooldown(
|
||||
ship_symbol:,
|
||||
|
|
125
src/spacetraders_models/date.gleam
Normal file
125
src/spacetraders_models/date.gleam
Normal file
|
@ -0,0 +1,125 @@
|
|||
import gleam/bit_array
|
||||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/int
|
||||
import gleam/json.{type Json}
|
||||
import gleam/pair
|
||||
import gleam/result
|
||||
import gleam/time/calendar.{
|
||||
type Month, April, August, Date, December, February, January, July, June,
|
||||
March, May, November, October, September,
|
||||
}
|
||||
|
||||
pub type Date =
|
||||
calendar.Date
|
||||
|
||||
pub fn parse(value: String) -> Result(Date, Nil) {
|
||||
let bytes = bit_array.from_string(value)
|
||||
use #(year, bytes) <- result.try(parse_year(from: bytes))
|
||||
use bytes <- result.try(accept_byte(from: bytes, value: byte_minus))
|
||||
use #(month, bytes) <- result.try(parse_month_calendar(from: bytes))
|
||||
use bytes <- result.try(accept_byte(from: bytes, value: byte_minus))
|
||||
use #(day, bytes) <- result.try(parse_day_calendar(from: bytes, year:, month:))
|
||||
use Nil <- result.try(accept_empty(bytes))
|
||||
Ok(Date(year:, month:, day:))
|
||||
}
|
||||
|
||||
pub fn decoder() -> Decoder(Date) {
|
||||
use value <- decode.then(decode.string)
|
||||
case parse(value) {
|
||||
Ok(date) -> decode.success(date)
|
||||
Error(Nil) -> decode.failure(Date(1970, calendar.January, 1), "Date")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(date: Date) -> Json {
|
||||
json.string(
|
||||
int.to_string(date.year)
|
||||
<> "-"
|
||||
<> int.to_string(calendar.month_to_int(date.month))
|
||||
<> "-"
|
||||
<> int.to_string(date.day),
|
||||
)
|
||||
}
|
||||
|
||||
const byte_zero: Int = 0x30
|
||||
|
||||
const byte_nine: Int = 0x39
|
||||
|
||||
const byte_minus: Int = 0x2D
|
||||
|
||||
fn accept_byte(from bytes: BitArray, value value: Int) -> Result(BitArray, Nil) {
|
||||
case bytes {
|
||||
<<byte, remaining_bytes:bytes>> if byte == value -> Ok(remaining_bytes)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_empty(from bytes: BitArray) -> Result(Nil, Nil) {
|
||||
case bytes {
|
||||
<<>> -> Ok(Nil)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_digits(
|
||||
from bytes: BitArray,
|
||||
count count: Int,
|
||||
) -> Result(#(Int, BitArray), Nil) {
|
||||
do_parse_digits(from: bytes, count:, acc: 0, k: 0)
|
||||
}
|
||||
|
||||
fn do_parse_digits(
|
||||
from bytes: BitArray,
|
||||
count count: Int,
|
||||
acc acc: Int,
|
||||
k k: Int,
|
||||
) -> Result(#(Int, BitArray), Nil) {
|
||||
case bytes {
|
||||
_ if k >= count -> Ok(#(acc, bytes))
|
||||
<<byte, remaining_bytes:bytes>> if byte_zero <= byte && byte <= byte_nine ->
|
||||
do_parse_digits(
|
||||
from: remaining_bytes,
|
||||
count:,
|
||||
acc: acc * 10 + { byte - 0x30 },
|
||||
k: k + 1,
|
||||
)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_year(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) {
|
||||
parse_digits(from: bytes, count: 4)
|
||||
}
|
||||
|
||||
// slightly modified version of parse_month that returns calendar.Month instead of Int
|
||||
fn parse_month_calendar(from bytes: BitArray) -> Result(#(Month, BitArray), Nil) {
|
||||
use #(month, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
calendar.month_from_int(month) |> result.map(pair.new(_, bytes))
|
||||
}
|
||||
|
||||
// slightly modified version of parse_day that takes calendar.Month instead of Int
|
||||
fn parse_day_calendar(
|
||||
from bytes: BitArray,
|
||||
year year: Int,
|
||||
month month: Month,
|
||||
) -> Result(#(Int, BitArray), Nil) {
|
||||
use #(day, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
let max_day = case month {
|
||||
January | March | May | July | August | October | December -> 31
|
||||
April | June | September | November -> 30
|
||||
February -> {
|
||||
case is_leap_year(year) {
|
||||
True -> 29
|
||||
False -> 28
|
||||
}
|
||||
}
|
||||
}
|
||||
case 1 <= day && day <= max_day {
|
||||
True -> Ok(#(day, bytes))
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_leap_year(year: Int) -> Bool {
|
||||
year % 4 == 0 && { year % 100 != 0 || year % 400 == 0 }
|
||||
}
|
|
@ -4,9 +4,8 @@ import gleam/json
|
|||
import gleam/option.{type Option}
|
||||
import gleam/result
|
||||
import gleam/string
|
||||
import gleam/time/calendar.{type Date}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/date.{type Date}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type JwtDecodeError {
|
||||
MissingHeader
|
||||
|
@ -91,10 +90,10 @@ fn jwt_payload_decoder() -> Decoder(JwtPayload) {
|
|||
use reset_date <- decode.optional_field(
|
||||
"reset_date",
|
||||
option.None,
|
||||
decode.optional(time.iso8601_date_decoder()),
|
||||
decode.optional(date.decoder()),
|
||||
)
|
||||
use issued_at_int <- decode.field("iat", decode.int)
|
||||
let issued_at = timestamp.from_unix_seconds(issued_at_int)
|
||||
use issued_at_unix <- decode.field("iat", decode.int)
|
||||
let issued_at = timestamp.from_unix(issued_at_unix)
|
||||
use subject <- decode.field("sub", decode.string)
|
||||
decode.success(JwtPayload(
|
||||
identifier:,
|
||||
|
|
|
@ -1,219 +0,0 @@
|
|||
import gleam/bit_array
|
||||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/pair
|
||||
import gleam/result
|
||||
import gleam/time/calendar.{
|
||||
type Date, type Month, type TimeOfDay, April, August, Date, December, February,
|
||||
January, July, June, March, May, November, October, September, TimeOfDay,
|
||||
}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
|
||||
pub fn rfc3339_timestamp_decoder() -> Decoder(Timestamp) {
|
||||
use value <- decode.then(decode.string)
|
||||
case timestamp.parse_rfc3339(value) {
|
||||
Ok(timestamp) -> decode.success(timestamp)
|
||||
Error(Nil) -> decode.failure(timestamp.from_unix_seconds(0), "Timestamp")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_iso8601_date(input: String) -> Result(Date, Nil) {
|
||||
let bytes = bit_array.from_string(input)
|
||||
use #(year, bytes) <- result.try(parse_year(from: bytes))
|
||||
use bytes <- result.try(accept_byte(from: bytes, value: byte_minus))
|
||||
use #(month, bytes) <- result.try(parse_month_calendar(from: bytes))
|
||||
use bytes <- result.try(accept_byte(from: bytes, value: byte_minus))
|
||||
use #(day, bytes) <- result.try(parse_day_calendar(from: bytes, year:, month:))
|
||||
use Nil <- result.try(accept_empty(bytes))
|
||||
Ok(Date(year:, month:, day:))
|
||||
}
|
||||
|
||||
pub fn iso8601_date_decoder() -> Decoder(Date) {
|
||||
use value <- decode.then(decode.string)
|
||||
case parse_iso8601_date(value) {
|
||||
Ok(date) -> decode.success(date)
|
||||
Error(Nil) -> decode.failure(Date(1970, January, 1), "Date")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_iso8601_time_of_day(input: String) -> Result(TimeOfDay, Nil) {
|
||||
let bytes = bit_array.from_string(input)
|
||||
use #(hours, bytes) <- result.try(parse_hours(from: bytes))
|
||||
use bytes <- result.try(accept_byte(from: bytes, value: byte_colon))
|
||||
use #(minutes, bytes) <- result.try(parse_minutes(from: bytes))
|
||||
use bytes <- result.try(accept_byte(from: bytes, value: byte_colon))
|
||||
use #(seconds, bytes) <- result.try(parse_seconds(from: bytes))
|
||||
use #(nanoseconds, bytes) <- result.try(parse_second_fraction_as_nanoseconds(
|
||||
from: bytes,
|
||||
))
|
||||
use Nil <- result.try(accept_empty(bytes))
|
||||
Ok(TimeOfDay(hours:, minutes:, seconds:, nanoseconds:))
|
||||
}
|
||||
|
||||
pub fn iso8601_time_of_day_decoder() -> Decoder(TimeOfDay) {
|
||||
use value <- decode.then(decode.string)
|
||||
case parse_iso8601_time_of_day(value) {
|
||||
Ok(date) -> decode.success(date)
|
||||
Error(Nil) -> decode.failure(TimeOfDay(0, 0, 0, 0), "TimeOfDay")
|
||||
}
|
||||
}
|
||||
|
||||
const byte_zero: Int = 0x30
|
||||
|
||||
const byte_nine: Int = 0x39
|
||||
|
||||
const byte_colon: Int = 0x3A
|
||||
|
||||
const byte_minus: Int = 0x2D
|
||||
|
||||
const nanoseconds_per_second: Int = 1_000_000_000
|
||||
|
||||
fn accept_byte(from bytes: BitArray, value value: Int) -> Result(BitArray, Nil) {
|
||||
case bytes {
|
||||
<<byte, remaining_bytes:bytes>> if byte == value -> Ok(remaining_bytes)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn accept_empty(from bytes: BitArray) -> Result(Nil, Nil) {
|
||||
case bytes {
|
||||
<<>> -> Ok(Nil)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_digits(
|
||||
from bytes: BitArray,
|
||||
count count: Int,
|
||||
) -> Result(#(Int, BitArray), Nil) {
|
||||
do_parse_digits(from: bytes, count:, acc: 0, k: 0)
|
||||
}
|
||||
|
||||
fn do_parse_digits(
|
||||
from bytes: BitArray,
|
||||
count count: Int,
|
||||
acc acc: Int,
|
||||
k k: Int,
|
||||
) -> Result(#(Int, BitArray), Nil) {
|
||||
case bytes {
|
||||
_ if k >= count -> Ok(#(acc, bytes))
|
||||
<<byte, remaining_bytes:bytes>> if byte_zero <= byte && byte <= byte_nine ->
|
||||
do_parse_digits(
|
||||
from: remaining_bytes,
|
||||
count:,
|
||||
acc: acc * 10 + { byte - 0x30 },
|
||||
k: k + 1,
|
||||
)
|
||||
_ -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_year(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) {
|
||||
parse_digits(from: bytes, count: 4)
|
||||
}
|
||||
|
||||
// slightly modified version of parse_month that returns calendar.Month instead of Int
|
||||
fn parse_month_calendar(from bytes: BitArray) -> Result(#(Month, BitArray), Nil) {
|
||||
use #(month, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
calendar.month_from_int(month) |> result.map(pair.new(_, bytes))
|
||||
}
|
||||
|
||||
// slightly modified version of parse_day that takes calendar.Month instead of Int
|
||||
fn parse_day_calendar(
|
||||
from bytes: BitArray,
|
||||
year year: Int,
|
||||
month month: Month,
|
||||
) -> Result(#(Int, BitArray), Nil) {
|
||||
use #(day, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
let max_day = case month {
|
||||
January | March | May | July | August | October | December -> 31
|
||||
April | June | September | November -> 30
|
||||
February -> {
|
||||
case is_leap_year(year) {
|
||||
True -> 29
|
||||
False -> 28
|
||||
}
|
||||
}
|
||||
}
|
||||
case 1 <= day && day <= max_day {
|
||||
True -> Ok(#(day, bytes))
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn is_leap_year(year: Int) -> Bool {
|
||||
year % 4 == 0 && { year % 100 != 0 || year % 400 == 0 }
|
||||
}
|
||||
|
||||
fn parse_hours(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) {
|
||||
use #(hours, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
case 0 <= hours && hours <= 23 {
|
||||
True -> Ok(#(hours, bytes))
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_minutes(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) {
|
||||
use #(minutes, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
case 0 <= minutes && minutes <= 59 {
|
||||
True -> Ok(#(minutes, bytes))
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_seconds(from bytes: BitArray) -> Result(#(Int, BitArray), Nil) {
|
||||
use #(seconds, bytes) <- result.try(parse_digits(from: bytes, count: 2))
|
||||
case 0 <= seconds && seconds <= 60 {
|
||||
True -> Ok(#(seconds, bytes))
|
||||
False -> Error(Nil)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_second_fraction_as_nanoseconds(from bytes: BitArray) {
|
||||
case bytes {
|
||||
<<".", byte, remaining_bytes:bytes>>
|
||||
if byte_zero <= byte && byte <= byte_nine
|
||||
-> {
|
||||
do_parse_second_fraction_as_nanoseconds(
|
||||
from: <<byte, remaining_bytes:bits>>,
|
||||
acc: 0,
|
||||
power: nanoseconds_per_second,
|
||||
)
|
||||
}
|
||||
<<".", _:bytes>> -> Error(Nil)
|
||||
_ -> Ok(#(0, bytes))
|
||||
}
|
||||
}
|
||||
|
||||
fn do_parse_second_fraction_as_nanoseconds(
|
||||
from bytes: BitArray,
|
||||
acc acc: Int,
|
||||
power power: Int,
|
||||
) -> Result(#(Int, BitArray), a) {
|
||||
// Each digit place to the left in the fractional second is 10x fewer
|
||||
// nanoseconds.
|
||||
let power = power / 10
|
||||
|
||||
case bytes {
|
||||
<<byte, remaining_bytes:bytes>>
|
||||
if byte_zero <= byte && byte <= byte_nine && power < 1
|
||||
-> {
|
||||
// We already have the max precision for nanoseconds. Truncate any
|
||||
// remaining digits.
|
||||
do_parse_second_fraction_as_nanoseconds(
|
||||
from: remaining_bytes,
|
||||
acc:,
|
||||
power:,
|
||||
)
|
||||
}
|
||||
<<byte, remaining_bytes:bytes>> if byte_zero <= byte && byte <= byte_nine -> {
|
||||
// We have not yet reached the precision limit. Parse the next digit.
|
||||
let digit = byte - 0x30
|
||||
do_parse_second_fraction_as_nanoseconds(
|
||||
from: remaining_bytes,
|
||||
acc: acc + digit * power,
|
||||
power:,
|
||||
)
|
||||
}
|
||||
_ -> Ok(#(acc, bytes))
|
||||
}
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||
import spacetraders_models/transaction_type.{type TransactionType}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
@ -30,7 +29,7 @@ pub fn decoder() -> Decoder(MarketTransaction) {
|
|||
use units <- decode.field("units", decode.int)
|
||||
use price_per_unit <- decode.field("pricePerUnit", decode.int)
|
||||
use total_price <- decode.field("totalPrice", decode.int)
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(MarketTransaction(
|
||||
waypoint_symbol:,
|
||||
ship_symbol:,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
pub type RepairTransaction {
|
||||
|
@ -20,7 +19,7 @@ pub fn decoder() -> Decoder(RepairTransaction) {
|
|||
)
|
||||
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||
use total_price <- decode.field("totalPrice", decode.int)
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(RepairTransaction(
|
||||
waypoint_symbol:,
|
||||
ship_symbol:,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
pub type ScrapTransaction {
|
||||
|
@ -20,7 +19,7 @@ pub fn decoder() -> Decoder(ScrapTransaction) {
|
|||
)
|
||||
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||
use total_price <- decode.field("totalPrice", decode.int)
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(ScrapTransaction(
|
||||
waypoint_symbol:,
|
||||
ship_symbol:,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type ShipFuelConsumed {
|
||||
ShipFuelConsumed(amount: Int, timestamp: Timestamp)
|
||||
|
@ -8,6 +7,6 @@ pub type ShipFuelConsumed {
|
|||
|
||||
pub fn decoder() -> Decoder(ShipFuelConsumed) {
|
||||
use amount <- decode.field("amount", decode.int)
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(ShipFuelConsumed(amount:, timestamp:))
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
|
@ -23,7 +22,7 @@ pub fn decoder() -> Decoder(ShipModificationTransaction) {
|
|||
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||
use trade_symbol <- decode.field("tradeSymbol", trade_symbol.decoder())
|
||||
use total_price <- decode.field("totalPrice", decode.int)
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(ShipModificationTransaction(
|
||||
waypoint_symbol:,
|
||||
ship_symbol:,
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_nav_route_waypoint.{type ShipNavRouteWaypoint}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
|
||||
pub type ShipNavRoute {
|
||||
ShipNavRoute(
|
||||
|
@ -18,10 +17,7 @@ pub fn decoder() -> Decoder(ShipNavRoute) {
|
|||
ship_nav_route_waypoint.decoder(),
|
||||
)
|
||||
use origin <- decode.field("origin", ship_nav_route_waypoint.decoder())
|
||||
use departure_time <- decode.field(
|
||||
"departureTime",
|
||||
time.rfc3339_timestamp_decoder(),
|
||||
)
|
||||
use arrival <- decode.field("arrival", time.rfc3339_timestamp_decoder())
|
||||
use departure_time <- decode.field("departureTime", timestamp.decoder())
|
||||
use arrival <- decode.field("arrival", timestamp.decoder())
|
||||
decode.success(ShipNavRoute(destination:, origin:, departure_time:, arrival:))
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/agent_symbol.{type AgentSymbol}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/ship_type.{type ShipType}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
pub type ShipyardTransaction {
|
||||
|
@ -23,7 +22,7 @@ pub fn decoder() -> Decoder(ShipyardTransaction) {
|
|||
use ship_type <- decode.field("shipType", ship_type.decoder())
|
||||
use price <- decode.field("price", decode.int)
|
||||
use agent_symbol <- decode.field("agentSymbol", agent_symbol.decoder())
|
||||
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||
use timestamp <- decode.field("timestamp", timestamp.decoder())
|
||||
decode.success(ShipyardTransaction(
|
||||
waypoint_symbol:,
|
||||
ship_type:,
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/json.{type Json}
|
||||
import gleam/time/calendar
|
||||
import gleam/time/timestamp.{type Timestamp}
|
||||
import spacetraders_models/internal/time
|
||||
import spacetraders_models/survey_deposit.{type SurveyDeposit}
|
||||
import spacetraders_models/survey_signature.{type SurveySignature}
|
||||
import spacetraders_models/survey_size.{type SurveySize}
|
||||
import spacetraders_models/timestamp.{type Timestamp}
|
||||
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||
|
||||
pub type Survey {
|
||||
|
@ -25,7 +23,7 @@ pub fn decoder() -> Decoder(Survey) {
|
|||
"deposits",
|
||||
decode.list(survey_deposit.decoder()),
|
||||
)
|
||||
use expiration <- decode.field("expiration", time.rfc3339_timestamp_decoder())
|
||||
use expiration <- decode.field("expiration", timestamp.decoder())
|
||||
use size <- decode.field("size", survey_size.decoder())
|
||||
decode.success(Survey(signature:, symbol:, deposits:, expiration:, size:))
|
||||
}
|
||||
|
@ -35,10 +33,7 @@ pub fn encode(survey: Survey) -> Json {
|
|||
#("signature", survey_signature.encode(survey.signature)),
|
||||
#("symbol", waypoint_symbol.encode(survey.symbol)),
|
||||
#("deposits", json.array(survey.deposits, survey_deposit.encode)),
|
||||
#(
|
||||
"expiration",
|
||||
json.string(timestamp.to_rfc3339(survey.expiration, calendar.utc_offset)),
|
||||
),
|
||||
#("expiration", timestamp.encode(survey.expiration)),
|
||||
#("size", survey_size.encode(survey.size)),
|
||||
])
|
||||
}
|
||||
|
|
27
src/spacetraders_models/timestamp.gleam
Normal file
27
src/spacetraders_models/timestamp.gleam
Normal file
|
@ -0,0 +1,27 @@
|
|||
import gleam/dynamic/decode.{type Decoder}
|
||||
import gleam/json.{type Json}
|
||||
import gleam/time/calendar
|
||||
import gleam/time/timestamp
|
||||
|
||||
pub type Timestamp =
|
||||
timestamp.Timestamp
|
||||
|
||||
pub fn parse(value: String) -> Result(Timestamp, Nil) {
|
||||
timestamp.parse_rfc3339(value)
|
||||
}
|
||||
|
||||
pub fn from_unix(seconds: Int) -> Timestamp {
|
||||
timestamp.from_unix_seconds(seconds)
|
||||
}
|
||||
|
||||
pub fn decoder() -> Decoder(Timestamp) {
|
||||
use value <- decode.then(decode.string)
|
||||
case parse(value) {
|
||||
Ok(timestamp) -> decode.success(timestamp)
|
||||
Error(Nil) -> decode.failure(timestamp.from_unix_seconds(0), "Timestamp")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn encode(timestamp: Timestamp) -> Json {
|
||||
json.string(timestamp.to_rfc3339(timestamp, calendar.utc_offset))
|
||||
}
|
Loading…
Reference in a new issue