This commit is contained in:
commit
fab4c9df5d
123 changed files with 5152 additions and 0 deletions
23
.github/workflows/test.yml
vendored
Normal file
23
.github/workflows/test.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
pull_request:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: erlef/setup-beam@v1
|
||||||
|
with:
|
||||||
|
otp-version: "27.1.2"
|
||||||
|
gleam-version: "1.11.1"
|
||||||
|
rebar3-version: "3"
|
||||||
|
# elixir-version: "1"
|
||||||
|
- run: gleam deps download
|
||||||
|
- run: gleam test
|
||||||
|
- run: gleam format --check src test
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.beam
|
||||||
|
*.ez
|
||||||
|
/build
|
||||||
|
erl_crash.dump
|
24
README.md
Normal file
24
README.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
# spacetraders_models
|
||||||
|
|
||||||
|
[](https://hex.pm/packages/spacetraders_models)
|
||||||
|
[](https://hexdocs.pm/spacetraders_models/)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gleam add spacetraders_models@1
|
||||||
|
```
|
||||||
|
```gleam
|
||||||
|
import spacetraders_models
|
||||||
|
|
||||||
|
pub fn main() -> Nil {
|
||||||
|
// TODO: An example of the project in use
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Further documentation can be found at <https://hexdocs.pm/spacetraders_models>.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gleam run # Run the project
|
||||||
|
gleam test # Run the tests
|
||||||
|
```
|
15
gleam.toml
Normal file
15
gleam.toml
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
name = "spacetraders_models"
|
||||||
|
version = "1.0.0"
|
||||||
|
gleam = ">= 1.11.0"
|
||||||
|
description = "Gleam types for the spacetraders.io game API"
|
||||||
|
licences = ["MIT"]
|
||||||
|
repository = { type = "forgejo", host = "git.7cs.dev", user = "lily", repo = "gleam-spacetraders-models" }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gleam_stdlib = ">= 0.44.0 and < 2.0.0"
|
||||||
|
gleam_http = ">= 4.1.0 and < 5.0.0"
|
||||||
|
gleam_time = ">= 1.2.0 and < 2.0.0"
|
||||||
|
gleam_json = ">= 3.0.2 and < 4.0.0"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
gleeunit = ">= 1.0.0 and < 2.0.0"
|
17
manifest.toml
Normal file
17
manifest.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
# This file was generated by Gleam
|
||||||
|
# You typically do not need to edit this file
|
||||||
|
|
||||||
|
packages = [
|
||||||
|
{ name = "gleam_http", version = "4.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "DB25DFC8530B64B77105405B80686541A0D96F7E2D83D807D6B2155FB9A8B1B8" },
|
||||||
|
{ name = "gleam_json", version = "3.0.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "874FA3C3BB6E22DD2BB111966BD40B3759E9094E05257899A7C08F5DE77EC049" },
|
||||||
|
{ name = "gleam_stdlib", version = "0.61.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "3DC407D6EDA98FCE089150C11F3AD892B6F4C3CA77C87A97BAE8D5AB5E41F331" },
|
||||||
|
{ name = "gleam_time", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "D71F1AFF7FEB534FF55E5DC58E534E9201BA75A444619788A2E4DEA4EBD87D16" },
|
||||||
|
{ name = "gleeunit", version = "1.6.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "63022D81C12C17B7F1A60E029964E830A4CBD846BBC6740004FC1F1031AE0326" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[requirements]
|
||||||
|
gleam_http = { version = ">= 4.1.0 and < 5.0.0" }
|
||||||
|
gleam_json = { version = ">= 3.0.2 and < 4.0.0" }
|
||||||
|
gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" }
|
||||||
|
gleam_time = { version = ">= 1.2.0 and < 2.0.0" }
|
||||||
|
gleeunit = { version = ">= 1.0.0 and < 2.0.0" }
|
30
src/spacetraders_models/account.gleam
Normal file
30
src/spacetraders_models/account.gleam
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
pub type Account {
|
||||||
|
Account(
|
||||||
|
id: AccountId,
|
||||||
|
email: Option(String),
|
||||||
|
token: Option(String),
|
||||||
|
created_at: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Account) {
|
||||||
|
use id <- decode.field("id", account_id.decoder())
|
||||||
|
use email <- decode.optional_field(
|
||||||
|
"email",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.string),
|
||||||
|
)
|
||||||
|
use token <- decode.optional_field(
|
||||||
|
"token",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.string),
|
||||||
|
)
|
||||||
|
use created_at <- decode.field("createdAt", time.rfc3339_timestamp_decoder())
|
||||||
|
decode.success(Account(id:, email:, token:, created_at:))
|
||||||
|
}
|
33
src/spacetraders_models/account_id.gleam
Normal file
33
src/spacetraders_models/account_id.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type AccountId {
|
||||||
|
AccountId(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(AccountId, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(AccountId(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(AccountId) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(account_id) -> decode.success(account_id)
|
||||||
|
Error(Nil) -> decode.failure(AccountId("invalid"), "AccountId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(account_id: AccountId) -> String {
|
||||||
|
let AccountId(symbol) = account_id
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(account_id: AccountId) -> Json {
|
||||||
|
json.string(to_string(account_id))
|
||||||
|
}
|
31
src/spacetraders_models/account_token.gleam
Normal file
31
src/spacetraders_models/account_token.gleam
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/result
|
||||||
|
import spacetraders_models/internal/jwt
|
||||||
|
import spacetraders_models/token_parse_error.{
|
||||||
|
type TokenParseError, IncorrectType, InvalidToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub opaque type AccountToken {
|
||||||
|
AccountToken(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(AccountToken, TokenParseError) {
|
||||||
|
use jwt <- result.try(jwt.parse(value) |> result.replace_error(InvalidToken))
|
||||||
|
case jwt.payload.subject {
|
||||||
|
"account-token" -> Ok(AccountToken(value))
|
||||||
|
_ -> Error(IncorrectType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(AccountToken) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(token) -> decode.success(token)
|
||||||
|
Error(_) -> decode.failure(AccountToken("invalid"), "AccountToken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(account_token: AccountToken) -> String {
|
||||||
|
let AccountToken(value) = account_token
|
||||||
|
value
|
||||||
|
}
|
40
src/spacetraders_models/activity_level.gleam
Normal file
40
src/spacetraders_models/activity_level.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ActivityLevel {
|
||||||
|
Weak
|
||||||
|
Growing
|
||||||
|
Strong
|
||||||
|
Restricted
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ActivityLevel, Nil) {
|
||||||
|
case value {
|
||||||
|
"WEAK" -> Ok(Weak)
|
||||||
|
"GROWING" -> Ok(Growing)
|
||||||
|
"STRONG" -> Ok(Strong)
|
||||||
|
"RESTRICTED" -> Ok(Restricted)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ActivityLevel) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(activity_level) -> decode.success(activity_level)
|
||||||
|
Error(Nil) -> decode.failure(Weak, "ActivityLevel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(activity_level: ActivityLevel) -> String {
|
||||||
|
case activity_level {
|
||||||
|
Weak -> "WEAK"
|
||||||
|
Growing -> "GROWING"
|
||||||
|
Strong -> "STRONG"
|
||||||
|
Restricted -> "RESTRICTED"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(activity_level: ActivityLevel) -> Json {
|
||||||
|
json.string(to_string(activity_level))
|
||||||
|
}
|
36
src/spacetraders_models/agent.gleam
Normal file
36
src/spacetraders_models/agent.gleam
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/account_id.{type AccountId}
|
||||||
|
import spacetraders_models/agent_symbol.{type AgentSymbol}
|
||||||
|
import spacetraders_models/faction_symbol.{type FactionSymbol}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type Agent {
|
||||||
|
Agent(
|
||||||
|
account_id: AccountId,
|
||||||
|
symbol: AgentSymbol,
|
||||||
|
headquarters: WaypointSymbol,
|
||||||
|
credits: Int,
|
||||||
|
starting_faction: FactionSymbol,
|
||||||
|
ship_count: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Agent) {
|
||||||
|
use account_id <- decode.field("accountId", account_id.decoder())
|
||||||
|
use symbol <- decode.field("symbol", agent_symbol.decoder())
|
||||||
|
use headquarters <- decode.field("headquarters", waypoint_symbol.decoder())
|
||||||
|
use credits <- decode.field("credits", decode.int)
|
||||||
|
use starting_faction <- decode.field(
|
||||||
|
"startingFaction",
|
||||||
|
faction_symbol.decoder(),
|
||||||
|
)
|
||||||
|
use ship_count <- decode.field("shipCount", decode.int)
|
||||||
|
decode.success(Agent(
|
||||||
|
account_id:,
|
||||||
|
symbol:,
|
||||||
|
headquarters:,
|
||||||
|
credits:,
|
||||||
|
starting_faction:,
|
||||||
|
ship_count:,
|
||||||
|
))
|
||||||
|
}
|
29
src/spacetraders_models/agent_event.gleam
Normal file
29
src/spacetraders_models/agent_event.gleam
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
pub type AgentEvent {
|
||||||
|
AgentEvent(
|
||||||
|
id: AgentEventId,
|
||||||
|
type_: String,
|
||||||
|
message: String,
|
||||||
|
data: Option(Dynamic),
|
||||||
|
created_at: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(AgentEvent) {
|
||||||
|
use id <- decode.field("id", agent_event_id.decoder())
|
||||||
|
use type_ <- decode.field("type", decode.string)
|
||||||
|
use message <- decode.field("message", decode.string)
|
||||||
|
use data <- decode.optional_field(
|
||||||
|
"data",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.dynamic),
|
||||||
|
)
|
||||||
|
use created_at <- decode.field("createdAt", time.rfc3339_timestamp_decoder())
|
||||||
|
decode.success(AgentEvent(id:, type_:, message:, data:, created_at:))
|
||||||
|
}
|
33
src/spacetraders_models/agent_event_id.gleam
Normal file
33
src/spacetraders_models/agent_event_id.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type AgentEventId {
|
||||||
|
AgentEventId(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(AgentEventId, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(AgentEventId(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(AgentEventId) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(agent_event_id) -> decode.success(agent_event_id)
|
||||||
|
Error(Nil) -> decode.failure(AgentEventId("invalid"), "AgentEventId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(agent_event_id: AgentEventId) -> String {
|
||||||
|
let AgentEventId(symbol) = agent_event_id
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(agent_event_id: AgentEventId) -> Json {
|
||||||
|
json.string(to_string(agent_event_id))
|
||||||
|
}
|
33
src/spacetraders_models/agent_symbol.gleam
Normal file
33
src/spacetraders_models/agent_symbol.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type AgentSymbol {
|
||||||
|
AgentSymbol(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(AgentSymbol, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(AgentSymbol(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(AgentSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(agent_symbol) -> decode.success(agent_symbol)
|
||||||
|
Error(Nil) -> decode.failure(AgentSymbol("invalid"), "AgentSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(agent_symbol: AgentSymbol) -> String {
|
||||||
|
let AgentSymbol(symbol) = agent_symbol
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(agent_symbol: AgentSymbol) -> Json {
|
||||||
|
json.string(to_string(agent_symbol))
|
||||||
|
}
|
31
src/spacetraders_models/agent_token.gleam
Normal file
31
src/spacetraders_models/agent_token.gleam
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/result
|
||||||
|
import spacetraders_models/internal/jwt
|
||||||
|
import spacetraders_models/token_parse_error.{
|
||||||
|
type TokenParseError, IncorrectType, InvalidToken,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub opaque type AgentToken {
|
||||||
|
AgentToken(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(AgentToken, TokenParseError) {
|
||||||
|
use jwt <- result.try(jwt.parse(value) |> result.replace_error(InvalidToken))
|
||||||
|
case jwt.payload.subject {
|
||||||
|
"agent-token" -> Ok(AgentToken(value))
|
||||||
|
_ -> Error(IncorrectType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(AgentToken) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(token) -> decode.success(token)
|
||||||
|
Error(_) -> decode.failure(AgentToken("invalid"), "AgentToken")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(agent_token: AgentToken) -> String {
|
||||||
|
let AgentToken(value) = agent_token
|
||||||
|
value
|
||||||
|
}
|
27
src/spacetraders_models/auth_method.gleam
Normal file
27
src/spacetraders_models/auth_method.gleam
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import gleam/http/request.{type Request}
|
||||||
|
import spacetraders_models/account_token.{type AccountToken}
|
||||||
|
import spacetraders_models/agent_token.{type AgentToken}
|
||||||
|
|
||||||
|
pub type AuthMethod {
|
||||||
|
AccountAuth(AccountToken)
|
||||||
|
AgentAuth(AgentToken)
|
||||||
|
NoAuth
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply(req: Request(a), auth_method: AuthMethod) -> Request(a) {
|
||||||
|
case auth_method {
|
||||||
|
NoAuth -> req
|
||||||
|
AccountAuth(account_token) ->
|
||||||
|
req
|
||||||
|
|> request.set_header(
|
||||||
|
"Authorization",
|
||||||
|
"Bearer " <> account_token.to_string(account_token),
|
||||||
|
)
|
||||||
|
AgentAuth(agent_token) ->
|
||||||
|
req
|
||||||
|
|> request.set_header(
|
||||||
|
"Authorization",
|
||||||
|
"Bearer " <> agent_token.to_string(agent_token),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
26
src/spacetraders_models/chart.gleam
Normal file
26
src/spacetraders_models/chart.gleam
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
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/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type Chart {
|
||||||
|
Chart(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
submitted_by: AgentSymbol,
|
||||||
|
submitted_on: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Chart) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
use submitted_by <- decode.field("submittedBy", agent_symbol.decoder())
|
||||||
|
use submitted_on <- decode.field(
|
||||||
|
"submittedOn",
|
||||||
|
time.rfc3339_timestamp_decoder(),
|
||||||
|
)
|
||||||
|
decode.success(Chart(waypoint_symbol:, submitted_by:, submitted_on:))
|
||||||
|
}
|
30
src/spacetraders_models/chart_transaction.gleam
Normal file
30
src/spacetraders_models/chart_transaction.gleam
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type ChartTransaction {
|
||||||
|
ChartTransaction(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
ship_symbol: ShipSymbol,
|
||||||
|
total_price: Int,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ChartTransaction) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
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())
|
||||||
|
decode.success(ChartTransaction(
|
||||||
|
waypoint_symbol:,
|
||||||
|
ship_symbol:,
|
||||||
|
total_price:,
|
||||||
|
timestamp:,
|
||||||
|
))
|
||||||
|
}
|
33
src/spacetraders_models/constellation.gleam
Normal file
33
src/spacetraders_models/constellation.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type Constellation {
|
||||||
|
Constellation(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(Constellation, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(Constellation(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Constellation) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(constellation) -> decode.success(constellation)
|
||||||
|
Error(Nil) -> decode.failure(Constellation("invalid"), "Constellation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(constellation: Constellation) -> String {
|
||||||
|
let Constellation(value) = constellation
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(constellation: Constellation) -> Json {
|
||||||
|
json.string(to_string(constellation))
|
||||||
|
}
|
21
src/spacetraders_models/construction.gleam
Normal file
21
src/spacetraders_models/construction.gleam
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/construction_material.{type ConstructionMaterial}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type Construction {
|
||||||
|
Construction(
|
||||||
|
symbol: WaypointSymbol,
|
||||||
|
materials: List(ConstructionMaterial),
|
||||||
|
is_complete: Bool,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Construction) {
|
||||||
|
use symbol <- decode.field("symbol", waypoint_symbol.decoder())
|
||||||
|
use materials <- decode.field(
|
||||||
|
"materials",
|
||||||
|
decode.list(construction_material.decoder()),
|
||||||
|
)
|
||||||
|
use is_complete <- decode.field("isComplete", decode.bool)
|
||||||
|
decode.success(Construction(symbol:, materials:, is_complete:))
|
||||||
|
}
|
13
src/spacetraders_models/construction_material.gleam
Normal file
13
src/spacetraders_models/construction_material.gleam
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
|
||||||
|
pub type ConstructionMaterial {
|
||||||
|
ConstructionMaterial(trade_symbol: TradeSymbol, required: Int, fulfilled: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ConstructionMaterial) {
|
||||||
|
use trade_symbol <- decode.field("tradeSymbol", trade_symbol.decoder())
|
||||||
|
use required <- decode.field("required", decode.int)
|
||||||
|
use fulfilled <- decode.field("fulfilled", decode.int)
|
||||||
|
decode.success(ConstructionMaterial(trade_symbol:, required:, fulfilled:))
|
||||||
|
}
|
43
src/spacetraders_models/contract.gleam
Normal file
43
src/spacetraders_models/contract.gleam
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
pub type Contract {
|
||||||
|
Contract(
|
||||||
|
id: ContractId,
|
||||||
|
faction_symbol: FactionSymbol,
|
||||||
|
type_: ContractType,
|
||||||
|
terms: ContractTerms,
|
||||||
|
accepted: Bool,
|
||||||
|
fulfilled: Bool,
|
||||||
|
deadline_to_accept: Option(Timestamp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Contract) {
|
||||||
|
use id <- decode.field("id", contract_id.decoder())
|
||||||
|
use faction_symbol <- decode.field("factionSymbol", faction_symbol.decoder())
|
||||||
|
use type_ <- decode.field("type", contract_type.decoder())
|
||||||
|
use terms <- decode.field("terms", contract_terms.decoder())
|
||||||
|
use accepted <- decode.field("accepted", decode.bool)
|
||||||
|
use fulfilled <- decode.field("fulfilled", decode.bool)
|
||||||
|
use deadline_to_accept <- decode.optional_field(
|
||||||
|
"deadlineToAccept",
|
||||||
|
option.None,
|
||||||
|
decode.optional(time.rfc3339_timestamp_decoder()),
|
||||||
|
)
|
||||||
|
decode.success(Contract(
|
||||||
|
id:,
|
||||||
|
faction_symbol:,
|
||||||
|
type_:,
|
||||||
|
terms:,
|
||||||
|
accepted:,
|
||||||
|
fulfilled:,
|
||||||
|
deadline_to_accept:,
|
||||||
|
))
|
||||||
|
}
|
28
src/spacetraders_models/contract_deliver_good.gleam
Normal file
28
src/spacetraders_models/contract_deliver_good.gleam
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type ContractDeliverGood {
|
||||||
|
ContractDeliverGood(
|
||||||
|
trade_symbol: TradeSymbol,
|
||||||
|
destination_symbol: WaypointSymbol,
|
||||||
|
units_required: Int,
|
||||||
|
units_fulfilled: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ContractDeliverGood) {
|
||||||
|
use trade_symbol <- decode.field("tradeSymbol", trade_symbol.decoder())
|
||||||
|
use destination_symbol <- decode.field(
|
||||||
|
"destinationSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
use units_required <- decode.field("unitsRequired", decode.int)
|
||||||
|
use units_fulfilled <- decode.field("unitsFulfilled", decode.int)
|
||||||
|
decode.success(ContractDeliverGood(
|
||||||
|
trade_symbol:,
|
||||||
|
destination_symbol:,
|
||||||
|
units_required:,
|
||||||
|
units_fulfilled:,
|
||||||
|
))
|
||||||
|
}
|
33
src/spacetraders_models/contract_id.gleam
Normal file
33
src/spacetraders_models/contract_id.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type ContractId {
|
||||||
|
ContractId(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ContractId, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(ContractId(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ContractId) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(contract_id) -> decode.success(contract_id)
|
||||||
|
Error(Nil) -> decode.failure(ContractId("invalid"), "ContractId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(contract_id: ContractId) -> String {
|
||||||
|
let ContractId(symbol) = contract_id
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(contract_id: ContractId) -> Json {
|
||||||
|
json.string(to_string(contract_id))
|
||||||
|
}
|
11
src/spacetraders_models/contract_payment.gleam
Normal file
11
src/spacetraders_models/contract_payment.gleam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
|
||||||
|
pub type ContractPayment {
|
||||||
|
ContractPayment(on_accepted: Int, on_fulfilled: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ContractPayment) {
|
||||||
|
use on_accepted <- decode.field("onAccepted", decode.int)
|
||||||
|
use on_fulfilled <- decode.field("onFulfilled", decode.int)
|
||||||
|
decode.success(ContractPayment(on_accepted:, on_fulfilled:))
|
||||||
|
}
|
25
src/spacetraders_models/contract_terms.gleam
Normal file
25
src/spacetraders_models/contract_terms.gleam
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
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
|
||||||
|
|
||||||
|
pub type ContractTerms {
|
||||||
|
ContractTerms(
|
||||||
|
deadline: Timestamp,
|
||||||
|
payment: ContractPayment,
|
||||||
|
deliver: Option(List(ContractDeliverGood)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ContractTerms) {
|
||||||
|
use deadline <- decode.field("deadline", time.rfc3339_timestamp_decoder())
|
||||||
|
use payment <- decode.field("payment", contract_payment.decoder())
|
||||||
|
use deliver <- decode.optional_field(
|
||||||
|
"deliver",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(contract_deliver_good.decoder())),
|
||||||
|
)
|
||||||
|
decode.success(ContractTerms(deadline:, payment:, deliver:))
|
||||||
|
}
|
37
src/spacetraders_models/contract_type.gleam
Normal file
37
src/spacetraders_models/contract_type.gleam
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ContractType {
|
||||||
|
Procurement
|
||||||
|
Transport
|
||||||
|
Shuttle
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ContractType, Nil) {
|
||||||
|
case value {
|
||||||
|
"PROCUREMENT" -> Ok(Procurement)
|
||||||
|
"TRANSPORT" -> Ok(Transport)
|
||||||
|
"SHUTTLE" -> Ok(Shuttle)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ContractType) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(contract_type) -> decode.success(contract_type)
|
||||||
|
Error(Nil) -> decode.failure(Procurement, "ContractType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(contract_type: ContractType) -> String {
|
||||||
|
case contract_type {
|
||||||
|
Procurement -> "PROCUREMENT"
|
||||||
|
Transport -> "TRANSPORT"
|
||||||
|
Shuttle -> "SHUTTLE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(contract_type: ContractType) -> Json {
|
||||||
|
json.string(to_string(contract_type))
|
||||||
|
}
|
31
src/spacetraders_models/cooldown.gleam
Normal file
31
src/spacetraders_models/cooldown.gleam
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
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}
|
||||||
|
|
||||||
|
pub type Cooldown {
|
||||||
|
Cooldown(
|
||||||
|
ship_symbol: ShipSymbol,
|
||||||
|
total_seconds: Int,
|
||||||
|
remaining_seconds: Int,
|
||||||
|
expiration: Option(Timestamp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Cooldown) {
|
||||||
|
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||||
|
use total_seconds <- decode.field("totalSeconds", decode.int)
|
||||||
|
use remaining_seconds <- decode.field("remainingSeconds", decode.int)
|
||||||
|
use expiration <- decode.optional_field(
|
||||||
|
"expiration",
|
||||||
|
option.None,
|
||||||
|
decode.optional(time.rfc3339_timestamp_decoder()),
|
||||||
|
)
|
||||||
|
decode.success(Cooldown(
|
||||||
|
ship_symbol:,
|
||||||
|
total_seconds:,
|
||||||
|
remaining_seconds:,
|
||||||
|
expiration:,
|
||||||
|
))
|
||||||
|
}
|
34
src/spacetraders_models/crew_rotation.gleam
Normal file
34
src/spacetraders_models/crew_rotation.gleam
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type CrewRotation {
|
||||||
|
Strict
|
||||||
|
Relaxed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(CrewRotation, Nil) {
|
||||||
|
case value {
|
||||||
|
"STRICT" -> Ok(Strict)
|
||||||
|
"RELAXED" -> Ok(Relaxed)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(CrewRotation) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(crew_rotation) -> decode.success(crew_rotation)
|
||||||
|
Error(Nil) -> decode.failure(Strict, "CrewRotation")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(crew_rotation: CrewRotation) -> String {
|
||||||
|
case crew_rotation {
|
||||||
|
Strict -> "STRICT"
|
||||||
|
Relaxed -> "RELAXED"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(crew_rotation: CrewRotation) -> Json {
|
||||||
|
json.string(to_string(crew_rotation))
|
||||||
|
}
|
40
src/spacetraders_models/engine_symbol.gleam
Normal file
40
src/spacetraders_models/engine_symbol.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type EngineSymbol {
|
||||||
|
EngineImpulseDriveI
|
||||||
|
EngineIonDriveI
|
||||||
|
EngineIonDriveII
|
||||||
|
EngineHyperDriveI
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(EngineSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"ENGINE_IMPULSE_DRIVE_I" -> Ok(EngineImpulseDriveI)
|
||||||
|
"ENGINE_ION_DRIVE_I" -> Ok(EngineIonDriveI)
|
||||||
|
"ENGINE_ION_DRIVE_II" -> Ok(EngineIonDriveII)
|
||||||
|
"ENGINE_HYPER_DRIVE_I" -> Ok(EngineHyperDriveI)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(EngineSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(engine_symbol) -> decode.success(engine_symbol)
|
||||||
|
Error(Nil) -> decode.failure(EngineImpulseDriveI, "EngineSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(engine_symbol: EngineSymbol) -> String {
|
||||||
|
case engine_symbol {
|
||||||
|
EngineImpulseDriveI -> "ENGINE_IMPULSE_DRIVE_I"
|
||||||
|
EngineIonDriveI -> "ENGINE_ION_DRIVE_I"
|
||||||
|
EngineIonDriveII -> "ENGINE_ION_DRIVE_II"
|
||||||
|
EngineHyperDriveI -> "ENGINE_HYPER_DRIVE_I"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(engine_symbol: EngineSymbol) -> Json {
|
||||||
|
json.string(to_string(engine_symbol))
|
||||||
|
}
|
13
src/spacetraders_models/extraction.gleam
Normal file
13
src/spacetraders_models/extraction.gleam
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/extraction_yield.{type ExtractionYield}
|
||||||
|
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||||
|
|
||||||
|
pub type Extraction {
|
||||||
|
Extraction(ship_symbol: ShipSymbol, yield: ExtractionYield)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Extraction) {
|
||||||
|
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||||
|
use yield <- decode.field("yield", extraction_yield.decoder())
|
||||||
|
decode.success(Extraction(ship_symbol:, yield:))
|
||||||
|
}
|
12
src/spacetraders_models/extraction_yield.gleam
Normal file
12
src/spacetraders_models/extraction_yield.gleam
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
|
||||||
|
pub type ExtractionYield {
|
||||||
|
ExtractionYield(symbol: TradeSymbol, units: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ExtractionYield) {
|
||||||
|
use symbol <- decode.field("symbol", trade_symbol.decoder())
|
||||||
|
use units <- decode.field("units", decode.int)
|
||||||
|
decode.success(ExtractionYield(symbol:, units:))
|
||||||
|
}
|
37
src/spacetraders_models/faction.gleam
Normal file
37
src/spacetraders_models/faction.gleam
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/faction_symbol.{type FactionSymbol}
|
||||||
|
import spacetraders_models/faction_trait.{type FactionTrait}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type Faction {
|
||||||
|
Faction(
|
||||||
|
symbol: FactionSymbol,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
headquarters: Option(WaypointSymbol),
|
||||||
|
traits: List(FactionTrait),
|
||||||
|
is_recruiting: Bool,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Faction) {
|
||||||
|
use symbol <- decode.field("symbol", faction_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use headquarters <- decode.optional_field(
|
||||||
|
"headquarters",
|
||||||
|
option.None,
|
||||||
|
decode.optional(waypoint_symbol.decoder()),
|
||||||
|
)
|
||||||
|
use traits <- decode.field("traits", decode.list(faction_trait.decoder()))
|
||||||
|
use is_recruiting <- decode.field("isRecruiting", decode.bool)
|
||||||
|
decode.success(Faction(
|
||||||
|
symbol:,
|
||||||
|
name:,
|
||||||
|
description:,
|
||||||
|
headquarters:,
|
||||||
|
traits:,
|
||||||
|
is_recruiting:,
|
||||||
|
))
|
||||||
|
}
|
85
src/spacetraders_models/faction_symbol.gleam
Normal file
85
src/spacetraders_models/faction_symbol.gleam
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type FactionSymbol {
|
||||||
|
Cosmic
|
||||||
|
Void
|
||||||
|
Galactic
|
||||||
|
Quantum
|
||||||
|
Dominion
|
||||||
|
Astro
|
||||||
|
Corsairs
|
||||||
|
Obsidian
|
||||||
|
Aegis
|
||||||
|
United
|
||||||
|
Solitary
|
||||||
|
Cobalt
|
||||||
|
Omega
|
||||||
|
Echo
|
||||||
|
Lord
|
||||||
|
Cult
|
||||||
|
Ancients
|
||||||
|
Shadow
|
||||||
|
Ethereal
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(FactionSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"COSMIC" -> Ok(Cosmic)
|
||||||
|
"VOID" -> Ok(Void)
|
||||||
|
"GALACTIC" -> Ok(Galactic)
|
||||||
|
"QUANTUM" -> Ok(Quantum)
|
||||||
|
"DOMINION" -> Ok(Dominion)
|
||||||
|
"ASTRO" -> Ok(Astro)
|
||||||
|
"CORSAIRS" -> Ok(Corsairs)
|
||||||
|
"OBSIDIAN" -> Ok(Obsidian)
|
||||||
|
"AEGIS" -> Ok(Aegis)
|
||||||
|
"UNITED" -> Ok(United)
|
||||||
|
"SOLITARY" -> Ok(Solitary)
|
||||||
|
"COBALT" -> Ok(Cobalt)
|
||||||
|
"OMEGA" -> Ok(Omega)
|
||||||
|
"ECHO" -> Ok(Echo)
|
||||||
|
"LORDS" -> Ok(Lord)
|
||||||
|
"CULT" -> Ok(Cult)
|
||||||
|
"ANCIENTS" -> Ok(Ancients)
|
||||||
|
"SHADOW" -> Ok(Shadow)
|
||||||
|
"ETHEREAL" -> Ok(Ethereal)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(FactionSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(faction_symbol) -> decode.success(faction_symbol)
|
||||||
|
Error(Nil) -> decode.failure(Cosmic, "FactionSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(faction_symbol: FactionSymbol) -> String {
|
||||||
|
case faction_symbol {
|
||||||
|
Cosmic -> "COSMIC"
|
||||||
|
Void -> "VOID"
|
||||||
|
Galactic -> "GALACTIC"
|
||||||
|
Quantum -> "QUANTUM"
|
||||||
|
Dominion -> "DOMINION"
|
||||||
|
Astro -> "ASTRO"
|
||||||
|
Corsairs -> "CORSAIRS"
|
||||||
|
Obsidian -> "OBSIDIAN"
|
||||||
|
Aegis -> "AEGIS"
|
||||||
|
United -> "UNITED"
|
||||||
|
Solitary -> "SOLITARY"
|
||||||
|
Cobalt -> "COBALT"
|
||||||
|
Omega -> "OMEGA"
|
||||||
|
Echo -> "ECHO"
|
||||||
|
Lord -> "LORDS"
|
||||||
|
Cult -> "CULT"
|
||||||
|
Ancients -> "ANCIENTS"
|
||||||
|
Shadow -> "SHADOW"
|
||||||
|
Ethereal -> "ETHEREAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(faction_symbol: FactionSymbol) -> Json {
|
||||||
|
json.string(to_string(faction_symbol))
|
||||||
|
}
|
13
src/spacetraders_models/faction_trait.gleam
Normal file
13
src/spacetraders_models/faction_trait.gleam
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/faction_trait_symbol.{type FactionTraitSymbol}
|
||||||
|
|
||||||
|
pub type FactionTrait {
|
||||||
|
FactionTrait(symbol: FactionTraitSymbol, name: String, description: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(FactionTrait) {
|
||||||
|
use symbol <- decode.field("symbol", faction_trait_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
decode.success(FactionTrait(symbol:, name:, description:))
|
||||||
|
}
|
205
src/spacetraders_models/faction_trait_symbol.gleam
Normal file
205
src/spacetraders_models/faction_trait_symbol.gleam
Normal file
|
@ -0,0 +1,205 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type FactionTraitSymbol {
|
||||||
|
Bureaucratic
|
||||||
|
Secretive
|
||||||
|
Capitalistic
|
||||||
|
Industrious
|
||||||
|
Peaceful
|
||||||
|
Distrustful
|
||||||
|
Welcoming
|
||||||
|
Smugglers
|
||||||
|
Scavengers
|
||||||
|
Rebellious
|
||||||
|
Exiles
|
||||||
|
Pirates
|
||||||
|
Raiders
|
||||||
|
Clan
|
||||||
|
Guild
|
||||||
|
Dominion
|
||||||
|
Fringe
|
||||||
|
Forsaken
|
||||||
|
Isolated
|
||||||
|
Localized
|
||||||
|
Established
|
||||||
|
Notable
|
||||||
|
Dominant
|
||||||
|
Inescapable
|
||||||
|
Innovative
|
||||||
|
Bold
|
||||||
|
Visionary
|
||||||
|
Curious
|
||||||
|
Daring
|
||||||
|
Exploratory
|
||||||
|
Resourceful
|
||||||
|
Flexible
|
||||||
|
Cooperative
|
||||||
|
United
|
||||||
|
Strategic
|
||||||
|
Intelligent
|
||||||
|
ResearchFocused
|
||||||
|
Collaborative
|
||||||
|
Progressive
|
||||||
|
Militaristic
|
||||||
|
TechnologicallyAdvanced
|
||||||
|
Aggressive
|
||||||
|
Imperialistic
|
||||||
|
TreasureHunters
|
||||||
|
Dexterous
|
||||||
|
Unpredictable
|
||||||
|
Brutal
|
||||||
|
Fleeting
|
||||||
|
Adaptable
|
||||||
|
SelfSufficient
|
||||||
|
Defensive
|
||||||
|
Proud
|
||||||
|
Diverse
|
||||||
|
Independent
|
||||||
|
SelfInterested
|
||||||
|
Fragmented
|
||||||
|
Commercial
|
||||||
|
FreeMarkets
|
||||||
|
Entrepreneurial
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(FactionTraitSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"BUREAUCRATIC" -> Ok(Bureaucratic)
|
||||||
|
"SECRETIVE" -> Ok(Secretive)
|
||||||
|
"CAPITALISTIC" -> Ok(Capitalistic)
|
||||||
|
"INDUSTRIOUS" -> Ok(Industrious)
|
||||||
|
"PEACEFUL" -> Ok(Peaceful)
|
||||||
|
"DISTRUSTFUL" -> Ok(Distrustful)
|
||||||
|
"WELCOMING" -> Ok(Welcoming)
|
||||||
|
"SMUGGLERS" -> Ok(Smugglers)
|
||||||
|
"SCAVENGERS" -> Ok(Scavengers)
|
||||||
|
"REBELLIOUS" -> Ok(Rebellious)
|
||||||
|
"EXILES" -> Ok(Exiles)
|
||||||
|
"PIRATES" -> Ok(Pirates)
|
||||||
|
"RAIDERS" -> Ok(Raiders)
|
||||||
|
"CLAN" -> Ok(Clan)
|
||||||
|
"GUILD" -> Ok(Guild)
|
||||||
|
"DOMINION" -> Ok(Dominion)
|
||||||
|
"FRINGE" -> Ok(Fringe)
|
||||||
|
"FORSAKEN" -> Ok(Forsaken)
|
||||||
|
"ISOLATED" -> Ok(Isolated)
|
||||||
|
"LOCALIZED" -> Ok(Localized)
|
||||||
|
"ESTABLISHED" -> Ok(Established)
|
||||||
|
"NOTABLE" -> Ok(Notable)
|
||||||
|
"DOMINANT" -> Ok(Dominant)
|
||||||
|
"INESCAPABLE" -> Ok(Inescapable)
|
||||||
|
"INNOVATIVE" -> Ok(Innovative)
|
||||||
|
"BOLD" -> Ok(Bold)
|
||||||
|
"VISIONARY" -> Ok(Visionary)
|
||||||
|
"CURIOUS" -> Ok(Curious)
|
||||||
|
"DARING" -> Ok(Daring)
|
||||||
|
"EXPLORATORY" -> Ok(Exploratory)
|
||||||
|
"RESOURCEFUL" -> Ok(Resourceful)
|
||||||
|
"FLEXIBLE" -> Ok(Flexible)
|
||||||
|
"COOPERATIVE" -> Ok(Cooperative)
|
||||||
|
"UNITED" -> Ok(United)
|
||||||
|
"STRATEGIC" -> Ok(Strategic)
|
||||||
|
"INTELLIGENT" -> Ok(Intelligent)
|
||||||
|
"RESEARCH_FOCUSED" -> Ok(ResearchFocused)
|
||||||
|
"COLLABORATIVE" -> Ok(Collaborative)
|
||||||
|
"PROGRESSIVE" -> Ok(Progressive)
|
||||||
|
"MILITARISTIC" -> Ok(Militaristic)
|
||||||
|
"TECHNOLOGICALLY_ADVANCED" -> Ok(TechnologicallyAdvanced)
|
||||||
|
"AGGRESSIVE" -> Ok(Aggressive)
|
||||||
|
"IMPERIALISTIC" -> Ok(Imperialistic)
|
||||||
|
"TREASURE_HUNTERS" -> Ok(TreasureHunters)
|
||||||
|
"DEXTEROUS" -> Ok(Dexterous)
|
||||||
|
"UNPREDICTABLE" -> Ok(Unpredictable)
|
||||||
|
"BRUTAL" -> Ok(Brutal)
|
||||||
|
"FLEETING" -> Ok(Fleeting)
|
||||||
|
"ADAPTABLE" -> Ok(Adaptable)
|
||||||
|
"SELF_SUFFICIENT" -> Ok(SelfSufficient)
|
||||||
|
"DEFENSIVE" -> Ok(Defensive)
|
||||||
|
"PROUD" -> Ok(Proud)
|
||||||
|
"DIVERSE" -> Ok(Diverse)
|
||||||
|
"INDEPENDENT" -> Ok(Independent)
|
||||||
|
"SELF_INTERESTED" -> Ok(SelfInterested)
|
||||||
|
"FRAGMENTED" -> Ok(Fragmented)
|
||||||
|
"COMMERCIAL" -> Ok(Commercial)
|
||||||
|
"FREE_MARKETS" -> Ok(FreeMarkets)
|
||||||
|
"ENTREPRENEURIAL" -> Ok(Entrepreneurial)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(FactionTraitSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(faction_trait_symbol) -> decode.success(faction_trait_symbol)
|
||||||
|
Error(Nil) -> decode.failure(Bureaucratic, "FactionTraitSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(faction_trait_symbol: FactionTraitSymbol) -> String {
|
||||||
|
case faction_trait_symbol {
|
||||||
|
Bureaucratic -> "BUREAUCRATIC"
|
||||||
|
Secretive -> "SECRETIVE"
|
||||||
|
Capitalistic -> "CAPITALISTIC"
|
||||||
|
Industrious -> "INDUSTRIOUS"
|
||||||
|
Peaceful -> "PEACEFUL"
|
||||||
|
Distrustful -> "DISTRUSTFUL"
|
||||||
|
Welcoming -> "WELCOMING"
|
||||||
|
Smugglers -> "SMUGGLERS"
|
||||||
|
Scavengers -> "SCAVENGERS"
|
||||||
|
Rebellious -> "REBELLIOUS"
|
||||||
|
Exiles -> "EXILES"
|
||||||
|
Pirates -> "PIRATES"
|
||||||
|
Raiders -> "RAIDERS"
|
||||||
|
Clan -> "CLAN"
|
||||||
|
Guild -> "GUILD"
|
||||||
|
Dominion -> "DOMINION"
|
||||||
|
Fringe -> "FRINGE"
|
||||||
|
Forsaken -> "FORSAKEN"
|
||||||
|
Isolated -> "ISOLATED"
|
||||||
|
Localized -> "LOCALIZED"
|
||||||
|
Established -> "ESTABLISHED"
|
||||||
|
Notable -> "NOTABLE"
|
||||||
|
Dominant -> "DOMINANT"
|
||||||
|
Inescapable -> "INESCAPABLE"
|
||||||
|
Innovative -> "INNOVATIVE"
|
||||||
|
Bold -> "BOLD"
|
||||||
|
Visionary -> "VISIONARY"
|
||||||
|
Curious -> "CURIOUS"
|
||||||
|
Daring -> "DARING"
|
||||||
|
Exploratory -> "EXPLORATORY"
|
||||||
|
Resourceful -> "RESOURCEFUL"
|
||||||
|
Flexible -> "FLEXIBLE"
|
||||||
|
Cooperative -> "COOPERATIVE"
|
||||||
|
United -> "UNITED"
|
||||||
|
Strategic -> "STRATEGIC"
|
||||||
|
Intelligent -> "INTELLIGENT"
|
||||||
|
ResearchFocused -> "RESEARCH_FOCUSED"
|
||||||
|
Collaborative -> "COLLABORATIVE"
|
||||||
|
Progressive -> "PROGRESSIVE"
|
||||||
|
Militaristic -> "MILITARISTIC"
|
||||||
|
TechnologicallyAdvanced -> "TECHNOLOGICALLY_ADVANCED"
|
||||||
|
Aggressive -> "AGGRESSIVE"
|
||||||
|
Imperialistic -> "IMPERIALISTIC"
|
||||||
|
TreasureHunters -> "TREASURE_HUNTERS"
|
||||||
|
Dexterous -> "DEXTEROUS"
|
||||||
|
Unpredictable -> "UNPREDICTABLE"
|
||||||
|
Brutal -> "BRUTAL"
|
||||||
|
Fleeting -> "FLEETING"
|
||||||
|
Adaptable -> "ADAPTABLE"
|
||||||
|
SelfSufficient -> "SELF_SUFFICIENT"
|
||||||
|
Defensive -> "DEFENSIVE"
|
||||||
|
Proud -> "PROUD"
|
||||||
|
Diverse -> "DIVERSE"
|
||||||
|
Independent -> "INDEPENDENT"
|
||||||
|
SelfInterested -> "SELF_INTERESTED"
|
||||||
|
Fragmented -> "FRAGMENTED"
|
||||||
|
Commercial -> "COMMERCIAL"
|
||||||
|
FreeMarkets -> "FREE_MARKETS"
|
||||||
|
Entrepreneurial -> "ENTREPRENEURIAL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(faction_trait_symbol: FactionTraitSymbol) -> Json {
|
||||||
|
json.string(to_string(faction_trait_symbol))
|
||||||
|
}
|
76
src/spacetraders_models/frame_symbol.gleam
Normal file
76
src/spacetraders_models/frame_symbol.gleam
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type FrameSymbol {
|
||||||
|
FrameProbe
|
||||||
|
FrameDrone
|
||||||
|
FrameInterceptor
|
||||||
|
FrameRacer
|
||||||
|
FrameFighter
|
||||||
|
FrameFrigate
|
||||||
|
FrameShuttle
|
||||||
|
FrameExplorer
|
||||||
|
FrameMiner
|
||||||
|
FrameLightFreighter
|
||||||
|
FrameHeavyFreighter
|
||||||
|
FrameTransport
|
||||||
|
FrameDestroyer
|
||||||
|
FrameCruiser
|
||||||
|
FrameCarrier
|
||||||
|
FrameBulkFreighter
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(FrameSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"FRAME_PROBE" -> Ok(FrameProbe)
|
||||||
|
"FRAME_DRONE" -> Ok(FrameDrone)
|
||||||
|
"FRAME_INTERCEPTOR" -> Ok(FrameInterceptor)
|
||||||
|
"FRAME_RACER" -> Ok(FrameRacer)
|
||||||
|
"FRAME_FIGHTER" -> Ok(FrameFighter)
|
||||||
|
"FRAME_FRIGATE" -> Ok(FrameFrigate)
|
||||||
|
"FRAME_SHUTTLE" -> Ok(FrameShuttle)
|
||||||
|
"FRAME_EXPLORER" -> Ok(FrameExplorer)
|
||||||
|
"FRAME_MINER" -> Ok(FrameMiner)
|
||||||
|
"FRAME_LIGHT_FREIGHTER" -> Ok(FrameLightFreighter)
|
||||||
|
"FRAME_HEAVY_FREIGHTER" -> Ok(FrameHeavyFreighter)
|
||||||
|
"FRAME_TRANSPORT" -> Ok(FrameTransport)
|
||||||
|
"FRAME_DESTROYER" -> Ok(FrameDestroyer)
|
||||||
|
"FRAME_CRUISER" -> Ok(FrameCruiser)
|
||||||
|
"FRAME_CARRIER" -> Ok(FrameCarrier)
|
||||||
|
"FRAME_BULK_FREIGHTER" -> Ok(FrameBulkFreighter)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(FrameSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(frame_symbol) -> decode.success(frame_symbol)
|
||||||
|
Error(Nil) -> decode.failure(FrameProbe, "FrameSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(frame_symbol: FrameSymbol) -> String {
|
||||||
|
case frame_symbol {
|
||||||
|
FrameProbe -> "FRAME_PROBE"
|
||||||
|
FrameDrone -> "FRAME_DRONE"
|
||||||
|
FrameInterceptor -> "FRAME_INTERCEPTOR"
|
||||||
|
FrameRacer -> "FRAME_RACER"
|
||||||
|
FrameFighter -> "FRAME_FIGHTER"
|
||||||
|
FrameFrigate -> "FRAME_FRIGATE"
|
||||||
|
FrameShuttle -> "FRAME_SHUTTLE"
|
||||||
|
FrameExplorer -> "FRAME_EXPLORER"
|
||||||
|
FrameMiner -> "FRAME_MINER"
|
||||||
|
FrameLightFreighter -> "FRAME_LIGHT_FREIGHTER"
|
||||||
|
FrameHeavyFreighter -> "FRAME_HEAVY_FREIGHTER"
|
||||||
|
FrameTransport -> "FRAME_TRANSPORT"
|
||||||
|
FrameDestroyer -> "FRAME_DESTROYER"
|
||||||
|
FrameCruiser -> "FRAME_CRUISER"
|
||||||
|
FrameCarrier -> "FRAME_CARRIER"
|
||||||
|
FrameBulkFreighter -> "FRAME_BULK_FREIGHTER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(frame_symbol: FrameSymbol) -> Json {
|
||||||
|
json.string(to_string(frame_symbol))
|
||||||
|
}
|
143
src/spacetraders_models/internal/jwt.gleam
Normal file
143
src/spacetraders_models/internal/jwt.gleam
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
import gleam/bit_array
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
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
|
||||||
|
|
||||||
|
pub type JwtDecodeError {
|
||||||
|
MissingHeader
|
||||||
|
MissingPayload
|
||||||
|
MissingSignature
|
||||||
|
InvalidHeader
|
||||||
|
InvalidPayload
|
||||||
|
InvalidSignature
|
||||||
|
InvalidExpiration
|
||||||
|
TokenExpired
|
||||||
|
TokenNotValidYet
|
||||||
|
InvalidNotBefore
|
||||||
|
InvalidAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type JwtAlgorithm {
|
||||||
|
HS256
|
||||||
|
HS384
|
||||||
|
HS512
|
||||||
|
RS256
|
||||||
|
RS384
|
||||||
|
RS512
|
||||||
|
ES256
|
||||||
|
ES384
|
||||||
|
ES512
|
||||||
|
PS256
|
||||||
|
PS384
|
||||||
|
PS512
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_jwt_algorithm(value: String) -> Result(JwtAlgorithm, Nil) {
|
||||||
|
case value {
|
||||||
|
"HS256" -> Ok(HS256)
|
||||||
|
"HS384" -> Ok(HS384)
|
||||||
|
"HS512" -> Ok(HS512)
|
||||||
|
"RS256" -> Ok(RS256)
|
||||||
|
"RS384" -> Ok(RS384)
|
||||||
|
"RS512" -> Ok(RS512)
|
||||||
|
"ES256" -> Ok(ES256)
|
||||||
|
"ES384" -> Ok(ES384)
|
||||||
|
"ES512" -> Ok(ES512)
|
||||||
|
"PS256" -> Ok(PS256)
|
||||||
|
"PS384" -> Ok(PS384)
|
||||||
|
"PS512" -> Ok(PS512)
|
||||||
|
"none" -> Ok(None)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn jwt_algorithm_decoder() -> Decoder(JwtAlgorithm) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse_jwt_algorithm(value) {
|
||||||
|
Ok(jwt_algorithm) -> decode.success(jwt_algorithm)
|
||||||
|
Error(Nil) -> decode.failure(None, "JwtAlgorithm")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type JwtHeader {
|
||||||
|
JwtHeader(algorithm: JwtAlgorithm, token_type: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jwt_header_decoder() -> Decoder(JwtHeader) {
|
||||||
|
use algorithm <- decode.field("alg", jwt_algorithm_decoder())
|
||||||
|
use token_type <- decode.field("typ", decode.string)
|
||||||
|
decode.success(JwtHeader(algorithm:, token_type:))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type JwtPayload {
|
||||||
|
JwtPayload(
|
||||||
|
identifier: String,
|
||||||
|
version: String,
|
||||||
|
reset_date: Option(Date),
|
||||||
|
issued_at: Timestamp,
|
||||||
|
subject: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jwt_payload_decoder() -> Decoder(JwtPayload) {
|
||||||
|
use identifier <- decode.field("identifier", decode.string)
|
||||||
|
use version <- decode.field("version", decode.string)
|
||||||
|
use reset_date <- decode.optional_field(
|
||||||
|
"reset_date",
|
||||||
|
option.None,
|
||||||
|
decode.optional(time.iso8601_date_decoder()),
|
||||||
|
)
|
||||||
|
use issued_at_int <- decode.field("iat", decode.int)
|
||||||
|
let issued_at = timestamp.from_unix_seconds(issued_at_int)
|
||||||
|
use subject <- decode.field("sub", decode.string)
|
||||||
|
decode.success(JwtPayload(
|
||||||
|
identifier:,
|
||||||
|
version:,
|
||||||
|
reset_date:,
|
||||||
|
issued_at:,
|
||||||
|
subject:,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Jwt {
|
||||||
|
Jwt(header: JwtHeader, payload: JwtPayload, signature: String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(token: String) -> Result(Jwt, JwtDecodeError) {
|
||||||
|
case string.split(token, ".") {
|
||||||
|
[encoded_header, encoded_payload, signature, ..] -> {
|
||||||
|
use header <- result.try(
|
||||||
|
bit_array.base64_url_decode(encoded_header)
|
||||||
|
|> result.replace_error(InvalidHeader)
|
||||||
|
|> result.try(fn(x) {
|
||||||
|
bit_array.to_string(x) |> result.replace_error(InvalidHeader)
|
||||||
|
})
|
||||||
|
|> result.try(fn(x) {
|
||||||
|
json.parse(x, jwt_header_decoder())
|
||||||
|
|> result.replace_error(InvalidHeader)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
use payload <- result.try(
|
||||||
|
bit_array.base64_url_decode(encoded_payload)
|
||||||
|
|> result.replace_error(InvalidPayload)
|
||||||
|
|> result.try(fn(x) {
|
||||||
|
bit_array.to_string(x) |> result.replace_error(InvalidPayload)
|
||||||
|
})
|
||||||
|
|> result.try(fn(x) {
|
||||||
|
json.parse(x, jwt_payload_decoder())
|
||||||
|
|> result.replace_error(InvalidPayload)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
Ok(Jwt(header:, payload:, signature:))
|
||||||
|
}
|
||||||
|
[_, _] -> Error(MissingSignature)
|
||||||
|
[_] -> Error(MissingPayload)
|
||||||
|
[] -> Error(MissingHeader)
|
||||||
|
}
|
||||||
|
}
|
219
src/spacetraders_models/internal/time.gleam
Normal file
219
src/spacetraders_models/internal/time.gleam
Normal file
|
@ -0,0 +1,219 @@
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
15
src/spacetraders_models/jump_gate.gleam
Normal file
15
src/spacetraders_models/jump_gate.gleam
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type JumpGate {
|
||||||
|
JumpGate(symbol: WaypointSymbol, connections: List(WaypointSymbol))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(JumpGate) {
|
||||||
|
use symbol <- decode.field("symbol", waypoint_symbol.decoder())
|
||||||
|
use connections <- decode.field(
|
||||||
|
"connections",
|
||||||
|
decode.list(waypoint_symbol.decoder()),
|
||||||
|
)
|
||||||
|
decode.success(JumpGate(symbol:, connections:))
|
||||||
|
}
|
42
src/spacetraders_models/market.gleam
Normal file
42
src/spacetraders_models/market.gleam
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/market_symbol.{type MarketSymbol}
|
||||||
|
import spacetraders_models/market_trade_good.{type MarketTradeGood}
|
||||||
|
import spacetraders_models/market_transaction.{type MarketTransaction}
|
||||||
|
import spacetraders_models/trade_good.{type TradeGood}
|
||||||
|
|
||||||
|
pub type Market {
|
||||||
|
Market(
|
||||||
|
symbol: MarketSymbol,
|
||||||
|
exports: List(TradeGood),
|
||||||
|
imports: List(TradeGood),
|
||||||
|
exchange: List(TradeGood),
|
||||||
|
transactions: Option(List(MarketTransaction)),
|
||||||
|
trade_goods: Option(List(MarketTradeGood)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Market) {
|
||||||
|
use symbol <- decode.field("symbol", market_symbol.decoder())
|
||||||
|
use exports <- decode.field("exports", decode.list(trade_good.decoder()))
|
||||||
|
use imports <- decode.field("imports", decode.list(trade_good.decoder()))
|
||||||
|
use exchange <- decode.field("exchange", decode.list(trade_good.decoder()))
|
||||||
|
use transactions <- decode.optional_field(
|
||||||
|
"transactions",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(market_transaction.decoder())),
|
||||||
|
)
|
||||||
|
use trade_goods <- decode.optional_field(
|
||||||
|
"tradeGoods",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(market_trade_good.decoder())),
|
||||||
|
)
|
||||||
|
decode.success(Market(
|
||||||
|
symbol:,
|
||||||
|
exports:,
|
||||||
|
imports:,
|
||||||
|
exchange:,
|
||||||
|
transactions:,
|
||||||
|
trade_goods:,
|
||||||
|
))
|
||||||
|
}
|
33
src/spacetraders_models/market_symbol.gleam
Normal file
33
src/spacetraders_models/market_symbol.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type MarketSymbol {
|
||||||
|
MarketSymbol(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(MarketSymbol, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(MarketSymbol(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(MarketSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(market_symbol) -> decode.success(market_symbol)
|
||||||
|
Error(Nil) -> decode.failure(MarketSymbol("invalid"), "MarketSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(market_symbol: MarketSymbol) -> String {
|
||||||
|
let MarketSymbol(symbol) = market_symbol
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(market_symbol: MarketSymbol) -> Json {
|
||||||
|
json.string(to_string(market_symbol))
|
||||||
|
}
|
41
src/spacetraders_models/market_trade_good.gleam
Normal file
41
src/spacetraders_models/market_trade_good.gleam
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/activity_level.{type ActivityLevel}
|
||||||
|
import spacetraders_models/supply_level.{type SupplyLevel}
|
||||||
|
import spacetraders_models/trade_good_type.{type TradeGoodType}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
|
||||||
|
pub type MarketTradeGood {
|
||||||
|
MarketTradeGood(
|
||||||
|
symbol: TradeSymbol,
|
||||||
|
type_: TradeGoodType,
|
||||||
|
trade_volume: Int,
|
||||||
|
supply: SupplyLevel,
|
||||||
|
activity: Option(ActivityLevel),
|
||||||
|
purchase_price: Int,
|
||||||
|
sell_price: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(MarketTradeGood) {
|
||||||
|
use symbol <- decode.field("symbol", trade_symbol.decoder())
|
||||||
|
use type_ <- decode.field("type", trade_good_type.decoder())
|
||||||
|
use trade_volume <- decode.field("tradeVolume", decode.int)
|
||||||
|
use supply <- decode.field("supply", supply_level.decoder())
|
||||||
|
use activity <- decode.optional_field(
|
||||||
|
"activity",
|
||||||
|
option.None,
|
||||||
|
decode.optional(activity_level.decoder()),
|
||||||
|
)
|
||||||
|
use purchase_price <- decode.field("purchasePrice", decode.int)
|
||||||
|
use sell_price <- decode.field("sellPrice", decode.int)
|
||||||
|
decode.success(MarketTradeGood(
|
||||||
|
symbol:,
|
||||||
|
type_:,
|
||||||
|
trade_volume:,
|
||||||
|
supply:,
|
||||||
|
activity:,
|
||||||
|
purchase_price:,
|
||||||
|
sell_price:,
|
||||||
|
))
|
||||||
|
}
|
44
src/spacetraders_models/market_transaction.gleam
Normal file
44
src/spacetraders_models/market_transaction.gleam
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
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/trade_symbol.{type TradeSymbol}
|
||||||
|
import spacetraders_models/transaction_type.{type TransactionType}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type MarketTransaction {
|
||||||
|
MarketTransaction(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
ship_symbol: ShipSymbol,
|
||||||
|
trade_symbol: TradeSymbol,
|
||||||
|
type_: TransactionType,
|
||||||
|
units: Int,
|
||||||
|
price_per_unit: Int,
|
||||||
|
total_price: Int,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(MarketTransaction) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||||
|
use trade_symbol <- decode.field("tradeSymbol", trade_symbol.decoder())
|
||||||
|
use type_ <- decode.field("type", transaction_type.decoder())
|
||||||
|
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())
|
||||||
|
decode.success(MarketTransaction(
|
||||||
|
waypoint_symbol:,
|
||||||
|
ship_symbol:,
|
||||||
|
trade_symbol:,
|
||||||
|
type_:,
|
||||||
|
units:,
|
||||||
|
price_per_unit:,
|
||||||
|
total_price:,
|
||||||
|
timestamp:,
|
||||||
|
))
|
||||||
|
}
|
12
src/spacetraders_models/meta.gleam
Normal file
12
src/spacetraders_models/meta.gleam
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
|
||||||
|
pub type Meta {
|
||||||
|
Meta(total: Int, page: Int, limit: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Meta) {
|
||||||
|
use total <- decode.field("total", decode.int)
|
||||||
|
use page <- decode.field("page", decode.int)
|
||||||
|
use limit <- decode.field("limit", decode.int)
|
||||||
|
decode.success(Meta(total:, page:, limit:))
|
||||||
|
}
|
88
src/spacetraders_models/module_symbol.gleam
Normal file
88
src/spacetraders_models/module_symbol.gleam
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ModuleSymbol {
|
||||||
|
ModuleMineralProcessorI
|
||||||
|
ModuleGasProcessorI
|
||||||
|
ModuleCargoHoldI
|
||||||
|
ModuleCargoHoldII
|
||||||
|
ModuleCargoHoldIII
|
||||||
|
ModuleCrewQuartersI
|
||||||
|
ModuleEnvoyQuartersI
|
||||||
|
ModulePassengerCabinI
|
||||||
|
ModuleMicroRefineryI
|
||||||
|
ModuleOreRefineryI
|
||||||
|
ModuleFuelRefineryI
|
||||||
|
ModuleScienceLabI
|
||||||
|
ModuleJumpDriveI
|
||||||
|
ModuleJumpDriveII
|
||||||
|
ModuleJumpDriveIII
|
||||||
|
ModuleWarpDriveI
|
||||||
|
ModuleWarpDriveII
|
||||||
|
ModuleWarpDriveIII
|
||||||
|
ModuleShieldGeneratorI
|
||||||
|
ModuleShieldGeneratorII
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ModuleSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"MODULE_MINERAL_PROCESSOR_I" -> Ok(ModuleMineralProcessorI)
|
||||||
|
"MODULE_GAS_PROCESSOR_I" -> Ok(ModuleGasProcessorI)
|
||||||
|
"MODULE_CARGO_HOLD_I" -> Ok(ModuleCargoHoldI)
|
||||||
|
"MODULE_CARGO_HOLD_II" -> Ok(ModuleCargoHoldII)
|
||||||
|
"MODULE_CARGO_HOLD_III" -> Ok(ModuleCargoHoldIII)
|
||||||
|
"MODULE_CREW_QUARTERS_I" -> Ok(ModuleCrewQuartersI)
|
||||||
|
"MODULE_ENVOY_QUARTERS_I" -> Ok(ModuleEnvoyQuartersI)
|
||||||
|
"MODULE_PASSENGER_CABIN_I" -> Ok(ModulePassengerCabinI)
|
||||||
|
"MODULE_MICRO_REFINERY_I" -> Ok(ModuleMicroRefineryI)
|
||||||
|
"MODULE_ORE_REFINERY_I" -> Ok(ModuleOreRefineryI)
|
||||||
|
"MODULE_FUEL_REFINERY_I" -> Ok(ModuleFuelRefineryI)
|
||||||
|
"MODULE_SCIENCE_LAB_I" -> Ok(ModuleScienceLabI)
|
||||||
|
"MODULE_JUMP_DRIVE_I" -> Ok(ModuleJumpDriveI)
|
||||||
|
"MODULE_JUMP_DRIVE_II" -> Ok(ModuleJumpDriveII)
|
||||||
|
"MODULE_JUMP_DRIVE_III" -> Ok(ModuleJumpDriveIII)
|
||||||
|
"MODULE_WARP_DRIVE_I" -> Ok(ModuleWarpDriveI)
|
||||||
|
"MODULE_WARP_DRIVE_II" -> Ok(ModuleWarpDriveII)
|
||||||
|
"MODULE_WARP_DRIVE_III" -> Ok(ModuleWarpDriveIII)
|
||||||
|
"MODULE_SHIELD_GENERATOR_I" -> Ok(ModuleShieldGeneratorI)
|
||||||
|
"MODULE_SHIELD_GENERATOR_II" -> Ok(ModuleShieldGeneratorII)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ModuleSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(module_symbol) -> decode.success(module_symbol)
|
||||||
|
Error(Nil) -> decode.failure(ModuleMineralProcessorI, "ModuleSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(module_symbol: ModuleSymbol) -> String {
|
||||||
|
case module_symbol {
|
||||||
|
ModuleMineralProcessorI -> "MODULE_MINERAL_PROCESSOR_I"
|
||||||
|
ModuleGasProcessorI -> "MODULE_GAS_PROCESSOR_I"
|
||||||
|
ModuleCargoHoldI -> "MODULE_CARGO_HOLD_I"
|
||||||
|
ModuleCargoHoldII -> "MODULE_CARGO_HOLD_II"
|
||||||
|
ModuleCargoHoldIII -> "MODULE_CARGO_HOLD_III"
|
||||||
|
ModuleCrewQuartersI -> "MODULE_CREW_QUARTERS_I"
|
||||||
|
ModuleEnvoyQuartersI -> "MODULE_ENVOY_QUARTERS_I"
|
||||||
|
ModulePassengerCabinI -> "MODULE_PASSENGER_CABIN_I"
|
||||||
|
ModuleMicroRefineryI -> "MODULE_MICRO_REFINERY_I"
|
||||||
|
ModuleOreRefineryI -> "MODULE_ORE_REFINERY_I"
|
||||||
|
ModuleFuelRefineryI -> "MODULE_FUEL_REFINERY_I"
|
||||||
|
ModuleScienceLabI -> "MODULE_SCIENCE_LAB_I"
|
||||||
|
ModuleJumpDriveI -> "MODULE_JUMP_DRIVE_I"
|
||||||
|
ModuleJumpDriveII -> "MODULE_JUMP_DRIVE_II"
|
||||||
|
ModuleJumpDriveIII -> "MODULE_JUMP_DRIVE_III"
|
||||||
|
ModuleWarpDriveI -> "MODULE_WARP_DRIVE_I"
|
||||||
|
ModuleWarpDriveII -> "MODULE_WARP_DRIVE_II"
|
||||||
|
ModuleWarpDriveIII -> "MODULE_WARP_DRIVE_III"
|
||||||
|
ModuleShieldGeneratorI -> "MODULE_SHIELD_GENERATOR_I"
|
||||||
|
ModuleShieldGeneratorII -> "MODULE_SHIELD_GENERATOR_II"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(module_symbol: ModuleSymbol) -> Json {
|
||||||
|
json.string(to_string(module_symbol))
|
||||||
|
}
|
70
src/spacetraders_models/mount_deposit.gleam
Normal file
70
src/spacetraders_models/mount_deposit.gleam
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type MountDeposit {
|
||||||
|
QuartzSand
|
||||||
|
SiliconCrystals
|
||||||
|
PreciousStones
|
||||||
|
IceWater
|
||||||
|
AmmoniaIce
|
||||||
|
IronOre
|
||||||
|
CopperOre
|
||||||
|
SilverOre
|
||||||
|
AluminumOre
|
||||||
|
GoldOre
|
||||||
|
PlatinumOre
|
||||||
|
Diamonds
|
||||||
|
UraniteOre
|
||||||
|
MeritiumOre
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(MountDeposit, Nil) {
|
||||||
|
case value {
|
||||||
|
"QUARTZ_SAND" -> Ok(QuartzSand)
|
||||||
|
"SILICON_CRYSTALS" -> Ok(SiliconCrystals)
|
||||||
|
"PRECIOUS_STONES" -> Ok(PreciousStones)
|
||||||
|
"ICE_WATER" -> Ok(IceWater)
|
||||||
|
"AMMONIA_ICE" -> Ok(AmmoniaIce)
|
||||||
|
"IRON_ORE" -> Ok(IronOre)
|
||||||
|
"COPPER_ORE" -> Ok(CopperOre)
|
||||||
|
"SILVER_ORE" -> Ok(SilverOre)
|
||||||
|
"ALUMINUM_ORE" -> Ok(AluminumOre)
|
||||||
|
"GOLD_ORE" -> Ok(GoldOre)
|
||||||
|
"PLATINUM_ORE" -> Ok(PlatinumOre)
|
||||||
|
"DIAMONDS" -> Ok(Diamonds)
|
||||||
|
"URANITE_ORE" -> Ok(UraniteOre)
|
||||||
|
"MERITIUM_ORE" -> Ok(MeritiumOre)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(MountDeposit) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(mount_deposit) -> decode.success(mount_deposit)
|
||||||
|
Error(Nil) -> decode.failure(QuartzSand, "MountDeposit")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(mount_deposit: MountDeposit) -> String {
|
||||||
|
case mount_deposit {
|
||||||
|
QuartzSand -> "QUARTZ_SAND"
|
||||||
|
SiliconCrystals -> "SILICON_CRYSTALS"
|
||||||
|
PreciousStones -> "PRECIOUS_STONES"
|
||||||
|
IceWater -> "ICE_WATER"
|
||||||
|
AmmoniaIce -> "AMMONIA_ICE"
|
||||||
|
IronOre -> "IRON_ORE"
|
||||||
|
CopperOre -> "COPPER_ORE"
|
||||||
|
SilverOre -> "SILVER_ORE"
|
||||||
|
AluminumOre -> "ALUMINUM_ORE"
|
||||||
|
GoldOre -> "GOLD_ORE"
|
||||||
|
PlatinumOre -> "PLATINUM_ORE"
|
||||||
|
Diamonds -> "DIAMONDS"
|
||||||
|
UraniteOre -> "URANITE_ORE"
|
||||||
|
MeritiumOre -> "MERITIUM_ORE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(mount_deposit: MountDeposit) -> Json {
|
||||||
|
json.string(to_string(mount_deposit))
|
||||||
|
}
|
73
src/spacetraders_models/mount_symbol.gleam
Normal file
73
src/spacetraders_models/mount_symbol.gleam
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type MountSymbol {
|
||||||
|
MountGasSiphonI
|
||||||
|
MountGasSiphonII
|
||||||
|
MountGasSiphonIII
|
||||||
|
MountSurveyorI
|
||||||
|
MountSurveyorII
|
||||||
|
MountSurveyorIII
|
||||||
|
MountSensorArrayI
|
||||||
|
MountSensorArrayII
|
||||||
|
MountSensorArrayIII
|
||||||
|
MountMiningLaserI
|
||||||
|
MountMiningLaserII
|
||||||
|
MountMiningLaserIII
|
||||||
|
MountLaserCannonI
|
||||||
|
MountMissileLauncherI
|
||||||
|
MountTurretI
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(MountSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"MOUNT_GAS_SIPHON_I" -> Ok(MountGasSiphonI)
|
||||||
|
"MOUNT_GAS_SIPHON_II" -> Ok(MountGasSiphonII)
|
||||||
|
"MOUNT_GAS_SIPHON_III" -> Ok(MountGasSiphonIII)
|
||||||
|
"MOUNT_SURVEYOR_I" -> Ok(MountSurveyorI)
|
||||||
|
"MOUNT_SURVEYOR_II" -> Ok(MountSurveyorII)
|
||||||
|
"MOUNT_SURVEYOR_III" -> Ok(MountSurveyorIII)
|
||||||
|
"MOUNT_SENSOR_ARRAY_I" -> Ok(MountSensorArrayI)
|
||||||
|
"MOUNT_SENSOR_ARRAY_II" -> Ok(MountSensorArrayII)
|
||||||
|
"MOUNT_SENSOR_ARRAY_III" -> Ok(MountSensorArrayIII)
|
||||||
|
"MOUNT_MINING_LASER_I" -> Ok(MountMiningLaserI)
|
||||||
|
"MOUNT_MINING_LASER_II" -> Ok(MountMiningLaserII)
|
||||||
|
"MOUNT_MINING_LASER_III" -> Ok(MountMiningLaserIII)
|
||||||
|
"MOUNT_LASER_CANNON_I" -> Ok(MountLaserCannonI)
|
||||||
|
"MOUNT_MISSILE_LAUNCHER_I" -> Ok(MountMissileLauncherI)
|
||||||
|
"MOUNT_TURRET_I" -> Ok(MountTurretI)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(MountSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(mount_symbol) -> decode.success(mount_symbol)
|
||||||
|
Error(Nil) -> decode.failure(MountGasSiphonI, "MountSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(mount_symbol: MountSymbol) -> String {
|
||||||
|
case mount_symbol {
|
||||||
|
MountGasSiphonI -> "MOUNT_GAS_SIPHON_I"
|
||||||
|
MountGasSiphonII -> "MOUNT_GAS_SIPHON_II"
|
||||||
|
MountGasSiphonIII -> "MOUNT_GAS_SIPHON_III"
|
||||||
|
MountSurveyorI -> "MOUNT_SURVEYOR_I"
|
||||||
|
MountSurveyorII -> "MOUNT_SURVEYOR_II"
|
||||||
|
MountSurveyorIII -> "MOUNT_SURVEYOR_III"
|
||||||
|
MountSensorArrayI -> "MOUNT_SENSOR_ARRAY_I"
|
||||||
|
MountSensorArrayII -> "MOUNT_SENSOR_ARRAY_II"
|
||||||
|
MountSensorArrayIII -> "MOUNT_SENSOR_ARRAY_III"
|
||||||
|
MountMiningLaserI -> "MOUNT_MINING_LASER_I"
|
||||||
|
MountMiningLaserII -> "MOUNT_MINING_LASER_II"
|
||||||
|
MountMiningLaserIII -> "MOUNT_MINING_LASER_III"
|
||||||
|
MountLaserCannonI -> "MOUNT_LASER_CANNON_I"
|
||||||
|
MountMissileLauncherI -> "MOUNT_MISSILE_LAUNCHER_I"
|
||||||
|
MountTurretI -> "MOUNT_TURRET_I"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(mount_symbol: MountSymbol) -> Json {
|
||||||
|
json.string(to_string(mount_symbol))
|
||||||
|
}
|
12
src/spacetraders_models/paged_data.gleam
Normal file
12
src/spacetraders_models/paged_data.gleam
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/meta.{type Meta}
|
||||||
|
|
||||||
|
pub type PagedData(data) {
|
||||||
|
PagedData(data: data, meta: Meta)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder(data_decoder: Decoder(data)) -> Decoder(PagedData(data)) {
|
||||||
|
use data <- decode.field("data", data_decoder)
|
||||||
|
use meta <- decode.field("meta", meta.decoder())
|
||||||
|
decode.success(PagedData(data:, meta:))
|
||||||
|
}
|
32
src/spacetraders_models/public_agent.gleam
Normal file
32
src/spacetraders_models/public_agent.gleam
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/agent_symbol.{type AgentSymbol}
|
||||||
|
import spacetraders_models/faction_symbol.{type FactionSymbol}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type PublicAgent {
|
||||||
|
PublicAgent(
|
||||||
|
symbol: AgentSymbol,
|
||||||
|
headquarters: WaypointSymbol,
|
||||||
|
credits: Int,
|
||||||
|
starting_faction: FactionSymbol,
|
||||||
|
ship_count: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(PublicAgent) {
|
||||||
|
use symbol <- decode.field("symbol", agent_symbol.decoder())
|
||||||
|
use headquarters <- decode.field("headquarters", waypoint_symbol.decoder())
|
||||||
|
use credits <- decode.field("credits", decode.int)
|
||||||
|
use starting_faction <- decode.field(
|
||||||
|
"startingFaction",
|
||||||
|
faction_symbol.decoder(),
|
||||||
|
)
|
||||||
|
use ship_count <- decode.field("shipCount", decode.int)
|
||||||
|
decode.success(PublicAgent(
|
||||||
|
symbol:,
|
||||||
|
headquarters:,
|
||||||
|
credits:,
|
||||||
|
starting_faction:,
|
||||||
|
ship_count:,
|
||||||
|
))
|
||||||
|
}
|
43
src/spacetraders_models/reactor_symbol.gleam
Normal file
43
src/spacetraders_models/reactor_symbol.gleam
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ReactorSymbol {
|
||||||
|
ReactorSolarI
|
||||||
|
ReactorFusionI
|
||||||
|
ReactorFissionI
|
||||||
|
ReactorChemicalI
|
||||||
|
ReactorAntimatterI
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ReactorSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"REACTOR_SOLAR_I" -> Ok(ReactorSolarI)
|
||||||
|
"REACTOR_FUSION_I" -> Ok(ReactorFusionI)
|
||||||
|
"REACTOR_FISSION_I" -> Ok(ReactorFissionI)
|
||||||
|
"REACTOR_CHEMICAL_I" -> Ok(ReactorChemicalI)
|
||||||
|
"REACTOR_ANTIMATTER_I" -> Ok(ReactorAntimatterI)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ReactorSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(reactor_symbol) -> decode.success(reactor_symbol)
|
||||||
|
Error(Nil) -> decode.failure(ReactorSolarI, "ReactorSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(reactor_symbol: ReactorSymbol) -> String {
|
||||||
|
case reactor_symbol {
|
||||||
|
ReactorSolarI -> "REACTOR_SOLAR_I"
|
||||||
|
ReactorFusionI -> "REACTOR_FUSION_I"
|
||||||
|
ReactorFissionI -> "REACTOR_FISSION_I"
|
||||||
|
ReactorChemicalI -> "REACTOR_CHEMICAL_I"
|
||||||
|
ReactorAntimatterI -> "REACTOR_ANTIMATTER_I"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(reactor_symbol: ReactorSymbol) -> Json {
|
||||||
|
json.string(to_string(reactor_symbol))
|
||||||
|
}
|
55
src/spacetraders_models/refinement_produce.gleam
Normal file
55
src/spacetraders_models/refinement_produce.gleam
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type RefinementProduce {
|
||||||
|
Iron
|
||||||
|
Copper
|
||||||
|
Silver
|
||||||
|
Gold
|
||||||
|
Aluminum
|
||||||
|
Platinum
|
||||||
|
Uranite
|
||||||
|
Meritium
|
||||||
|
Fuel
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(RefinementProduce, Nil) {
|
||||||
|
case value {
|
||||||
|
"IRON" -> Ok(Iron)
|
||||||
|
"COPPER" -> Ok(Copper)
|
||||||
|
"SILVER" -> Ok(Silver)
|
||||||
|
"GOLD" -> Ok(Gold)
|
||||||
|
"ALUMINUM" -> Ok(Aluminum)
|
||||||
|
"PLATINUM" -> Ok(Platinum)
|
||||||
|
"URANITE" -> Ok(Uranite)
|
||||||
|
"MERITIUM" -> Ok(Meritium)
|
||||||
|
"FUEL" -> Ok(Fuel)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(RefinementProduce) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(refinement_produce) -> decode.success(refinement_produce)
|
||||||
|
Error(Nil) -> decode.failure(Iron, "RefinementProduce")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(refinement_produce: RefinementProduce) -> String {
|
||||||
|
case refinement_produce {
|
||||||
|
Iron -> "IRON"
|
||||||
|
Copper -> "COPPER"
|
||||||
|
Silver -> "SILVER"
|
||||||
|
Gold -> "GOLD"
|
||||||
|
Aluminum -> "ALUMINUM"
|
||||||
|
Platinum -> "PLATINUM"
|
||||||
|
Uranite -> "URANITE"
|
||||||
|
Meritium -> "MERITIUM"
|
||||||
|
Fuel -> "FUEL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(refinement_produce: RefinementProduce) -> Json {
|
||||||
|
json.string(to_string(refinement_produce))
|
||||||
|
}
|
12
src/spacetraders_models/refinement_yield.gleam
Normal file
12
src/spacetraders_models/refinement_yield.gleam
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
|
||||||
|
pub type RefinementYield {
|
||||||
|
RefinementYield(trade_symbol: TradeSymbol, units: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(RefinementYield) {
|
||||||
|
use trade_symbol <- decode.field("tradeSymbol", trade_symbol.decoder())
|
||||||
|
use units <- decode.field("units", decode.int)
|
||||||
|
decode.success(RefinementYield(trade_symbol:, units:))
|
||||||
|
}
|
30
src/spacetraders_models/repair_transaction.gleam
Normal file
30
src/spacetraders_models/repair_transaction.gleam
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type RepairTransaction {
|
||||||
|
RepairTransaction(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
ship_symbol: ShipSymbol,
|
||||||
|
total_price: Int,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(RepairTransaction) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
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())
|
||||||
|
decode.success(RepairTransaction(
|
||||||
|
waypoint_symbol:,
|
||||||
|
ship_symbol:,
|
||||||
|
total_price:,
|
||||||
|
timestamp:,
|
||||||
|
))
|
||||||
|
}
|
52
src/spacetraders_models/scanned_ship.gleam
Normal file
52
src/spacetraders_models/scanned_ship.gleam
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/scanned_ship_engine.{type ScannedShipEngine}
|
||||||
|
import spacetraders_models/scanned_ship_frame.{type ScannedShipFrame}
|
||||||
|
import spacetraders_models/scanned_ship_mount.{type ScannedShipMount}
|
||||||
|
import spacetraders_models/scanned_ship_reactor.{type ScannedShipReactor}
|
||||||
|
import spacetraders_models/ship_nav.{type ShipNav}
|
||||||
|
import spacetraders_models/ship_registration.{type ShipRegistration}
|
||||||
|
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||||
|
|
||||||
|
pub type ScannedShip {
|
||||||
|
ScannedShip(
|
||||||
|
symbol: ShipSymbol,
|
||||||
|
registration: ShipRegistration,
|
||||||
|
nav: ShipNav,
|
||||||
|
frame: Option(ScannedShipFrame),
|
||||||
|
reactor: Option(ScannedShipReactor),
|
||||||
|
engine: ScannedShipEngine,
|
||||||
|
mounts: Option(List(ScannedShipMount)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedShip) {
|
||||||
|
use symbol <- decode.field("symbol", ship_symbol.decoder())
|
||||||
|
use registration <- decode.field("registration", ship_registration.decoder())
|
||||||
|
use nav <- decode.field("nav", ship_nav.decoder())
|
||||||
|
use frame <- decode.optional_field(
|
||||||
|
"frame",
|
||||||
|
option.None,
|
||||||
|
decode.optional(scanned_ship_frame.decoder()),
|
||||||
|
)
|
||||||
|
use reactor <- decode.optional_field(
|
||||||
|
"reactor",
|
||||||
|
option.None,
|
||||||
|
decode.optional(scanned_ship_reactor.decoder()),
|
||||||
|
)
|
||||||
|
use engine <- decode.field("engine", scanned_ship_engine.decoder())
|
||||||
|
use mounts <- decode.optional_field(
|
||||||
|
"mounts",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(scanned_ship_mount.decoder())),
|
||||||
|
)
|
||||||
|
decode.success(ScannedShip(
|
||||||
|
symbol:,
|
||||||
|
registration:,
|
||||||
|
nav:,
|
||||||
|
frame:,
|
||||||
|
reactor:,
|
||||||
|
engine:,
|
||||||
|
mounts:,
|
||||||
|
))
|
||||||
|
}
|
11
src/spacetraders_models/scanned_ship_engine.gleam
Normal file
11
src/spacetraders_models/scanned_ship_engine.gleam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/engine_symbol.{type EngineSymbol}
|
||||||
|
|
||||||
|
pub type ScannedShipEngine {
|
||||||
|
ScannedShipEngine(symbol: EngineSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedShipEngine) {
|
||||||
|
use symbol <- decode.field("symbol", engine_symbol.decoder())
|
||||||
|
decode.success(ScannedShipEngine(symbol:))
|
||||||
|
}
|
11
src/spacetraders_models/scanned_ship_frame.gleam
Normal file
11
src/spacetraders_models/scanned_ship_frame.gleam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/frame_symbol.{type FrameSymbol}
|
||||||
|
|
||||||
|
pub type ScannedShipFrame {
|
||||||
|
ScannedShipFrame(symbol: FrameSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedShipFrame) {
|
||||||
|
use symbol <- decode.field("symbol", frame_symbol.decoder())
|
||||||
|
decode.success(ScannedShipFrame(symbol:))
|
||||||
|
}
|
11
src/spacetraders_models/scanned_ship_mount.gleam
Normal file
11
src/spacetraders_models/scanned_ship_mount.gleam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/mount_symbol.{type MountSymbol}
|
||||||
|
|
||||||
|
pub type ScannedShipMount {
|
||||||
|
ScannedShipMount(symbol: MountSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedShipMount) {
|
||||||
|
use symbol <- decode.field("symbol", mount_symbol.decoder())
|
||||||
|
decode.success(ScannedShipMount(symbol:))
|
||||||
|
}
|
11
src/spacetraders_models/scanned_ship_reactor.gleam
Normal file
11
src/spacetraders_models/scanned_ship_reactor.gleam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/reactor_symbol.{type ReactorSymbol}
|
||||||
|
|
||||||
|
pub type ScannedShipReactor {
|
||||||
|
ScannedShipReactor(symbol: ReactorSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedShipReactor) {
|
||||||
|
use symbol <- decode.field("symbol", reactor_symbol.decoder())
|
||||||
|
decode.success(ScannedShipReactor(symbol:))
|
||||||
|
}
|
32
src/spacetraders_models/scanned_system.gleam
Normal file
32
src/spacetraders_models/scanned_system.gleam
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/sector_symbol.{type SectorSymbol}
|
||||||
|
import spacetraders_models/system_symbol.{type SystemSymbol}
|
||||||
|
import spacetraders_models/system_type.{type SystemType}
|
||||||
|
|
||||||
|
pub type ScannedSystem {
|
||||||
|
ScannedSystem(
|
||||||
|
symbol: SystemSymbol,
|
||||||
|
sector_symbol: SectorSymbol,
|
||||||
|
type_: SystemType,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
distance: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedSystem) {
|
||||||
|
use symbol <- decode.field("symbol", system_symbol.decoder())
|
||||||
|
use sector_symbol <- decode.field("sectorSymbol", sector_symbol.decoder())
|
||||||
|
use type_ <- decode.field("type", system_type.decoder())
|
||||||
|
use x <- decode.field("x", decode.int)
|
||||||
|
use y <- decode.field("y", decode.int)
|
||||||
|
use distance <- decode.field("distance", decode.int)
|
||||||
|
decode.success(ScannedSystem(
|
||||||
|
symbol:,
|
||||||
|
sector_symbol:,
|
||||||
|
type_:,
|
||||||
|
x:,
|
||||||
|
y:,
|
||||||
|
distance:,
|
||||||
|
))
|
||||||
|
}
|
57
src/spacetraders_models/scanned_waypoint.gleam
Normal file
57
src/spacetraders_models/scanned_waypoint.gleam
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/chart.{type Chart}
|
||||||
|
import spacetraders_models/system_symbol.{type SystemSymbol}
|
||||||
|
import spacetraders_models/waypoint_faction.{type WaypointFaction}
|
||||||
|
import spacetraders_models/waypoint_orbital.{type WaypointOrbital}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
import spacetraders_models/waypoint_trait.{type WaypointTrait}
|
||||||
|
import spacetraders_models/waypoint_type.{type WaypointType}
|
||||||
|
|
||||||
|
pub type ScannedWaypoint {
|
||||||
|
ScannedWaypoint(
|
||||||
|
symbol: WaypointSymbol,
|
||||||
|
type_: WaypointType,
|
||||||
|
system_symbol: SystemSymbol,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
orbitals: List(WaypointOrbital),
|
||||||
|
faction: Option(WaypointFaction),
|
||||||
|
traits: List(WaypointTrait),
|
||||||
|
chart: Option(Chart),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScannedWaypoint) {
|
||||||
|
use symbol <- decode.field("symbol", waypoint_symbol.decoder())
|
||||||
|
use type_ <- decode.field("type", waypoint_type.decoder())
|
||||||
|
use system_symbol <- decode.field("systemSymbol", system_symbol.decoder())
|
||||||
|
use x <- decode.field("x", decode.int)
|
||||||
|
use y <- decode.field("y", decode.int)
|
||||||
|
use orbitals <- decode.field(
|
||||||
|
"orbitals",
|
||||||
|
decode.list(waypoint_orbital.decoder()),
|
||||||
|
)
|
||||||
|
use faction <- decode.optional_field(
|
||||||
|
"faction",
|
||||||
|
option.None,
|
||||||
|
decode.optional(waypoint_faction.decoder()),
|
||||||
|
)
|
||||||
|
use traits <- decode.field("traits", decode.list(waypoint_trait.decoder()))
|
||||||
|
use chart <- decode.optional_field(
|
||||||
|
"chart",
|
||||||
|
option.None,
|
||||||
|
decode.optional(chart.decoder()),
|
||||||
|
)
|
||||||
|
decode.success(ScannedWaypoint(
|
||||||
|
symbol:,
|
||||||
|
type_:,
|
||||||
|
system_symbol:,
|
||||||
|
x:,
|
||||||
|
y:,
|
||||||
|
orbitals:,
|
||||||
|
faction:,
|
||||||
|
traits:,
|
||||||
|
chart:,
|
||||||
|
))
|
||||||
|
}
|
30
src/spacetraders_models/scrap_transaction.gleam
Normal file
30
src/spacetraders_models/scrap_transaction.gleam
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
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/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type ScrapTransaction {
|
||||||
|
ScrapTransaction(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
ship_symbol: ShipSymbol,
|
||||||
|
total_price: Int,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ScrapTransaction) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
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())
|
||||||
|
decode.success(ScrapTransaction(
|
||||||
|
waypoint_symbol:,
|
||||||
|
ship_symbol:,
|
||||||
|
total_price:,
|
||||||
|
timestamp:,
|
||||||
|
))
|
||||||
|
}
|
33
src/spacetraders_models/sector_symbol.gleam
Normal file
33
src/spacetraders_models/sector_symbol.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type SectorSymbol {
|
||||||
|
SectorSymbol(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(SectorSymbol, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(SectorSymbol(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(SectorSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(sector_symbol) -> decode.success(sector_symbol)
|
||||||
|
Error(Nil) -> decode.failure(SectorSymbol("invalid"), "SectorSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(sector_symbol: SectorSymbol) -> String {
|
||||||
|
let SectorSymbol(value) = sector_symbol
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(sector_symbol: SectorSymbol) -> Json {
|
||||||
|
json.string(to_string(sector_symbol))
|
||||||
|
}
|
59
src/spacetraders_models/ship.gleam
Normal file
59
src/spacetraders_models/ship.gleam
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/cooldown.{type Cooldown}
|
||||||
|
import spacetraders_models/ship_cargo.{type ShipCargo}
|
||||||
|
import spacetraders_models/ship_crew.{type ShipCrew}
|
||||||
|
import spacetraders_models/ship_engine.{type ShipEngine}
|
||||||
|
import spacetraders_models/ship_frame.{type ShipFrame}
|
||||||
|
import spacetraders_models/ship_fuel.{type ShipFuel}
|
||||||
|
import spacetraders_models/ship_module.{type ShipModule}
|
||||||
|
import spacetraders_models/ship_mount.{type ShipMount}
|
||||||
|
import spacetraders_models/ship_nav.{type ShipNav}
|
||||||
|
import spacetraders_models/ship_reactor.{type ShipReactor}
|
||||||
|
import spacetraders_models/ship_registration.{type ShipRegistration}
|
||||||
|
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||||
|
|
||||||
|
pub type Ship {
|
||||||
|
Ship(
|
||||||
|
symbol: ShipSymbol,
|
||||||
|
registration: ShipRegistration,
|
||||||
|
nav: ShipNav,
|
||||||
|
crew: ShipCrew,
|
||||||
|
frame: ShipFrame,
|
||||||
|
reactor: ShipReactor,
|
||||||
|
engine: ShipEngine,
|
||||||
|
modules: List(ShipModule),
|
||||||
|
mounts: List(ShipMount),
|
||||||
|
cargo: ShipCargo,
|
||||||
|
cooldown: Cooldown,
|
||||||
|
fuel: ShipFuel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Ship) {
|
||||||
|
use symbol <- decode.field("symbol", ship_symbol.decoder())
|
||||||
|
use registration <- decode.field("registration", ship_registration.decoder())
|
||||||
|
use nav <- decode.field("nav", ship_nav.decoder())
|
||||||
|
use crew <- decode.field("crew", ship_crew.decoder())
|
||||||
|
use frame <- decode.field("frame", ship_frame.decoder())
|
||||||
|
use reactor <- decode.field("reactor", ship_reactor.decoder())
|
||||||
|
use engine <- decode.field("engine", ship_engine.decoder())
|
||||||
|
use modules <- decode.field("modules", decode.list(ship_module.decoder()))
|
||||||
|
use mounts <- decode.field("mounts", decode.list(ship_mount.decoder()))
|
||||||
|
use cargo <- decode.field("cargo", ship_cargo.decoder())
|
||||||
|
use cooldown <- decode.field("cooldown", cooldown.decoder())
|
||||||
|
use fuel <- decode.field("fuel", ship_fuel.decoder())
|
||||||
|
decode.success(Ship(
|
||||||
|
symbol:,
|
||||||
|
registration:,
|
||||||
|
nav:,
|
||||||
|
crew:,
|
||||||
|
frame:,
|
||||||
|
reactor:,
|
||||||
|
engine:,
|
||||||
|
modules:,
|
||||||
|
mounts:,
|
||||||
|
cargo:,
|
||||||
|
cooldown:,
|
||||||
|
fuel:,
|
||||||
|
))
|
||||||
|
}
|
16
src/spacetraders_models/ship_cargo.gleam
Normal file
16
src/spacetraders_models/ship_cargo.gleam
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/ship_cargo_item.{type ShipCargoItem}
|
||||||
|
|
||||||
|
pub type ShipCargo {
|
||||||
|
ShipCargo(capacity: Int, units: Int, inventory: List(ShipCargoItem))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipCargo) {
|
||||||
|
use capacity <- decode.field("capacity", decode.int)
|
||||||
|
use units <- decode.field("units", decode.int)
|
||||||
|
use inventory <- decode.field(
|
||||||
|
"inventory",
|
||||||
|
decode.list(ship_cargo_item.decoder()),
|
||||||
|
)
|
||||||
|
decode.success(ShipCargo(capacity:, units:, inventory:))
|
||||||
|
}
|
19
src/spacetraders_models/ship_cargo_item.gleam
Normal file
19
src/spacetraders_models/ship_cargo_item.gleam
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
|
||||||
|
pub type ShipCargoItem {
|
||||||
|
ShipCargoItem(
|
||||||
|
symbol: TradeSymbol,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
units: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipCargoItem) {
|
||||||
|
use symbol <- decode.field("symbol", trade_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use units <- decode.field("units", decode.int)
|
||||||
|
decode.success(ShipCargoItem(symbol:, name:, description:, units:))
|
||||||
|
}
|
37
src/spacetraders_models/ship_component.gleam
Normal file
37
src/spacetraders_models/ship_component.gleam
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ShipComponent {
|
||||||
|
Frame
|
||||||
|
Reactor
|
||||||
|
Engine
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipComponent, Nil) {
|
||||||
|
case value {
|
||||||
|
"FRAME" -> Ok(Frame)
|
||||||
|
"REACTOR" -> Ok(Reactor)
|
||||||
|
"ENGINE" -> Ok(Engine)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipComponent) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_component) -> decode.success(ship_component)
|
||||||
|
Error(Nil) -> decode.failure(Frame, "ShipComponent")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(ship_component: ShipComponent) -> String {
|
||||||
|
case ship_component {
|
||||||
|
Frame -> "FRAME"
|
||||||
|
Reactor -> "REACTOR"
|
||||||
|
Engine -> "ENGINE"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_component: ShipComponent) -> Json {
|
||||||
|
json.string(to_string(ship_component))
|
||||||
|
}
|
40
src/spacetraders_models/ship_component_condition.gleam
Normal file
40
src/spacetraders_models/ship_component_condition.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/int
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub opaque type ShipComponentCondition {
|
||||||
|
ShipComponentCondition(Float)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min: Float = 0.0
|
||||||
|
|
||||||
|
pub const max: Float = 1.0
|
||||||
|
|
||||||
|
pub fn parse(value: Float) -> Result(ShipComponentCondition, Nil) {
|
||||||
|
case value >=. min && value <=. max {
|
||||||
|
True -> Ok(ShipComponentCondition(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipComponentCondition) {
|
||||||
|
use value <- decode.then(
|
||||||
|
decode.one_of(decode.float, [
|
||||||
|
decode.then(decode.int, fn(i) { decode.success(int.to_float(i)) }),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_component_condition) -> decode.success(ship_component_condition)
|
||||||
|
Error(Nil) ->
|
||||||
|
decode.failure(ShipComponentCondition(0.0), "ShipComponentCondition")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_float(ship_component_condition: ShipComponentCondition) -> Float {
|
||||||
|
let ShipComponentCondition(value) = ship_component_condition
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_component_condition: ShipComponentCondition) -> Json {
|
||||||
|
json.float(to_float(ship_component_condition))
|
||||||
|
}
|
40
src/spacetraders_models/ship_component_integrity.gleam
Normal file
40
src/spacetraders_models/ship_component_integrity.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/int
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub opaque type ShipComponentIntegrity {
|
||||||
|
ShipComponentIntegrity(Float)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min: Float = 0.0
|
||||||
|
|
||||||
|
pub const max: Float = 1.0
|
||||||
|
|
||||||
|
pub fn parse(value: Float) -> Result(ShipComponentIntegrity, Nil) {
|
||||||
|
case value >=. min && value <=. max {
|
||||||
|
True -> Ok(ShipComponentIntegrity(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipComponentIntegrity) {
|
||||||
|
use value <- decode.then(
|
||||||
|
decode.one_of(decode.float, [
|
||||||
|
decode.then(decode.int, fn(i) { decode.success(int.to_float(i)) }),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_component_integrity) -> decode.success(ship_component_integrity)
|
||||||
|
Error(Nil) ->
|
||||||
|
decode.failure(ShipComponentIntegrity(0.0), "ShipComponentIntegrity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_float(ship_component_integrity: ShipComponentIntegrity) -> Float {
|
||||||
|
let ShipComponentIntegrity(value) = ship_component_integrity
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_component_integrity: ShipComponentIntegrity) -> Json {
|
||||||
|
json.float(to_float(ship_component_integrity))
|
||||||
|
}
|
28
src/spacetraders_models/ship_component_quality.gleam
Normal file
28
src/spacetraders_models/ship_component_quality.gleam
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub opaque type ShipComponentQuality {
|
||||||
|
ShipComponentQuality(Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: Int) -> Result(ShipComponentQuality, Nil) {
|
||||||
|
Ok(ShipComponentQuality(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipComponentQuality) {
|
||||||
|
use value <- decode.then(decode.int)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_component_quality) -> decode.success(ship_component_quality)
|
||||||
|
Error(Nil) ->
|
||||||
|
decode.failure(ShipComponentQuality(0), "ShipComponentQuality")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_int(ship_component_quality: ShipComponentQuality) -> Int {
|
||||||
|
let ShipComponentQuality(value) = ship_component_quality
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_component_quality: ShipComponentQuality) -> Json {
|
||||||
|
json.int(to_int(ship_component_quality))
|
||||||
|
}
|
22
src/spacetraders_models/ship_condition_event.gleam
Normal file
22
src/spacetraders_models/ship_condition_event.gleam
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/ship_component.{type ShipComponent}
|
||||||
|
import spacetraders_models/ship_condition_event_symbol.{
|
||||||
|
type ShipConditionEventSymbol,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ShipConditionEvent {
|
||||||
|
ShipConditionEvent(
|
||||||
|
symbol: ShipConditionEventSymbol,
|
||||||
|
component: ShipComponent,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipConditionEvent) {
|
||||||
|
use symbol <- decode.field("symbol", ship_condition_event_symbol.decoder())
|
||||||
|
use component <- decode.field("component", ship_component.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
decode.success(ShipConditionEvent(symbol:, component:, name:, description:))
|
||||||
|
}
|
112
src/spacetraders_models/ship_condition_event_symbol.gleam
Normal file
112
src/spacetraders_models/ship_condition_event_symbol.gleam
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ShipConditionEventSymbol {
|
||||||
|
ReactorOverload
|
||||||
|
EnergySpikeFromMineral
|
||||||
|
SolarFlareInterference
|
||||||
|
CoolantLeak
|
||||||
|
PowerDistributionFluctuation
|
||||||
|
MagneticFieldDisruption
|
||||||
|
HullMicrometeoriteStrikes
|
||||||
|
StructuralStressFractures
|
||||||
|
CorrosiveMineralContamination
|
||||||
|
ThermalExpansionMismatch
|
||||||
|
VibrationDamageFromDrilling
|
||||||
|
ElectromagneticFieldInterference
|
||||||
|
ImpactWithExtractedDebris
|
||||||
|
FuelEfficiencyDegradation
|
||||||
|
CoolantSystemAgeing
|
||||||
|
DustMicroabrasions
|
||||||
|
ThrusterNozzleWear
|
||||||
|
ExhaustPortClogging
|
||||||
|
BearingLubricationFade
|
||||||
|
SensorCalibrationDrift
|
||||||
|
HullMicrometeoriteDamage
|
||||||
|
SpaceDebrisCollision
|
||||||
|
ThermalStress
|
||||||
|
VibrationOverload
|
||||||
|
PressureDifferentialStress
|
||||||
|
ElectromagneticSurgeEffects
|
||||||
|
AtmosphericEntryHeat
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipConditionEventSymbol, Nil) {
|
||||||
|
case value {
|
||||||
|
"REACTOR_OVERLOAD" -> Ok(ReactorOverload)
|
||||||
|
"ENERGY_SPIKE_FROM_MINERAL" -> Ok(EnergySpikeFromMineral)
|
||||||
|
"SOLAR_FLARE_INTERFERENCE" -> Ok(SolarFlareInterference)
|
||||||
|
"COOLANT_LEAK" -> Ok(CoolantLeak)
|
||||||
|
"POWER_DISTRIBUTION_FLUCTUATION" -> Ok(PowerDistributionFluctuation)
|
||||||
|
"MAGNETIC_FIELD_DISRUPTION" -> Ok(MagneticFieldDisruption)
|
||||||
|
"HULL_MICROMETEORITE_STRIKES" -> Ok(HullMicrometeoriteStrikes)
|
||||||
|
"STRUCTURAL_STRESS_FRACTURES" -> Ok(StructuralStressFractures)
|
||||||
|
"CORROSIVE_MINERAL_CONTAMINATION" -> Ok(CorrosiveMineralContamination)
|
||||||
|
"THERMAL_EXPANSION_MISMATCH" -> Ok(ThermalExpansionMismatch)
|
||||||
|
"VIBRATION_DAMAGE_FROM_DRILLING" -> Ok(VibrationDamageFromDrilling)
|
||||||
|
"ELECTROMAGNETIC_FIELD_INTERFERENCE" -> Ok(ElectromagneticFieldInterference)
|
||||||
|
"IMPACT_WITH_EXTRACTED_DEBRIS" -> Ok(ImpactWithExtractedDebris)
|
||||||
|
"FUEL_EFFICIENCY_DEGRADATION" -> Ok(FuelEfficiencyDegradation)
|
||||||
|
"COOLANT_SYSTEM_AGEING" -> Ok(CoolantSystemAgeing)
|
||||||
|
"DUST_MICROABRASIONS" -> Ok(DustMicroabrasions)
|
||||||
|
"THRUSTER_NOZZLE_WEAR" -> Ok(ThrusterNozzleWear)
|
||||||
|
"EXHAUST_PORT_CLOGGING" -> Ok(ExhaustPortClogging)
|
||||||
|
"BEARING_LUBRICATION_FADE" -> Ok(BearingLubricationFade)
|
||||||
|
"SENSOR_CALIBRATION_DRIFT" -> Ok(SensorCalibrationDrift)
|
||||||
|
"HULL_MICROMETEORITE_DAMAGE" -> Ok(HullMicrometeoriteDamage)
|
||||||
|
"SPACE_DEBRIS_COLLISION" -> Ok(SpaceDebrisCollision)
|
||||||
|
"THERMAL_STRESS" -> Ok(ThermalStress)
|
||||||
|
"VIBRATION_OVERLOAD" -> Ok(VibrationOverload)
|
||||||
|
"PRESSURE_DIFFERENTIAL_STRESS" -> Ok(PressureDifferentialStress)
|
||||||
|
"ELECTROMAGNETIC_SURGE_EFFECTS" -> Ok(ElectromagneticSurgeEffects)
|
||||||
|
"ATMOSPHERIC_ENTRY_HEAT" -> Ok(AtmosphericEntryHeat)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipConditionEventSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_condition_event_symbol) ->
|
||||||
|
decode.success(ship_condition_event_symbol)
|
||||||
|
Error(Nil) -> decode.failure(ReactorOverload, "ShipConditionEventSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(
|
||||||
|
ship_condition_event_symbol: ShipConditionEventSymbol,
|
||||||
|
) -> String {
|
||||||
|
case ship_condition_event_symbol {
|
||||||
|
ReactorOverload -> "REACTOR_OVERLOAD"
|
||||||
|
EnergySpikeFromMineral -> "ENERGY_SPIKE_FROM_MINERAL"
|
||||||
|
SolarFlareInterference -> "SOLAR_FLARE_INTERFERENCE"
|
||||||
|
CoolantLeak -> "COOLANT_LEAK"
|
||||||
|
PowerDistributionFluctuation -> "POWER_DISTRIBUTION_FLUCTUATION"
|
||||||
|
MagneticFieldDisruption -> "MAGNETIC_FIELD_DISRUPTION"
|
||||||
|
HullMicrometeoriteStrikes -> "HULL_MICROMETEORITE_STRIKES"
|
||||||
|
StructuralStressFractures -> "STRUCTURAL_STRESS_FRACTURES"
|
||||||
|
CorrosiveMineralContamination -> "CORROSIVE_MINERAL_CONTAMINATION"
|
||||||
|
ThermalExpansionMismatch -> "THERMAL_EXPANSION_MISMATCH"
|
||||||
|
VibrationDamageFromDrilling -> "VIBRATION_DAMAGE_FROM_DRILLING"
|
||||||
|
ElectromagneticFieldInterference -> "ELECTROMAGNETIC_FIELD_INTERFERENCE"
|
||||||
|
ImpactWithExtractedDebris -> "IMPACT_WITH_EXTRACTED_DEBRIS"
|
||||||
|
FuelEfficiencyDegradation -> "FUEL_EFFICIENCY_DEGRADATION"
|
||||||
|
CoolantSystemAgeing -> "COOLANT_SYSTEM_AGEING"
|
||||||
|
DustMicroabrasions -> "DUST_MICROABRASIONS"
|
||||||
|
ThrusterNozzleWear -> "THRUSTER_NOZZLE_WEAR"
|
||||||
|
ExhaustPortClogging -> "EXHAUST_PORT_CLOGGING"
|
||||||
|
BearingLubricationFade -> "BEARING_LUBRICATION_FADE"
|
||||||
|
SensorCalibrationDrift -> "SENSOR_CALIBRATION_DRIFT"
|
||||||
|
HullMicrometeoriteDamage -> "HULL_MICROMETEORITE_DAMAGE"
|
||||||
|
SpaceDebrisCollision -> "SPACE_DEBRIS_COLLISION"
|
||||||
|
ThermalStress -> "THERMAL_STRESS"
|
||||||
|
VibrationOverload -> "VIBRATION_OVERLOAD"
|
||||||
|
PressureDifferentialStress -> "PRESSURE_DIFFERENTIAL_STRESS"
|
||||||
|
ElectromagneticSurgeEffects -> "ELECTROMAGNETIC_SURGE_EFFECTS"
|
||||||
|
AtmosphericEntryHeat -> "ATMOSPHERIC_ENTRY_HEAT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_condition_event_symbol: ShipConditionEventSymbol) -> Json {
|
||||||
|
json.string(to_string(ship_condition_event_symbol))
|
||||||
|
}
|
30
src/spacetraders_models/ship_crew.gleam
Normal file
30
src/spacetraders_models/ship_crew.gleam
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/crew_rotation.{type CrewRotation}
|
||||||
|
|
||||||
|
pub type ShipCrew {
|
||||||
|
ShipCrew(
|
||||||
|
current: Int,
|
||||||
|
required: Int,
|
||||||
|
capacity: Int,
|
||||||
|
rotation: CrewRotation,
|
||||||
|
morale: Int,
|
||||||
|
wages: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipCrew) {
|
||||||
|
use current <- decode.field("current", decode.int)
|
||||||
|
use required <- decode.field("required", decode.int)
|
||||||
|
use capacity <- decode.field("capacity", decode.int)
|
||||||
|
use rotation <- decode.field("rotation", crew_rotation.decoder())
|
||||||
|
use morale <- decode.field("morale", decode.int)
|
||||||
|
use wages <- decode.field("wages", decode.int)
|
||||||
|
decode.success(ShipCrew(
|
||||||
|
current:,
|
||||||
|
required:,
|
||||||
|
capacity:,
|
||||||
|
rotation:,
|
||||||
|
morale:,
|
||||||
|
wages:,
|
||||||
|
))
|
||||||
|
}
|
40
src/spacetraders_models/ship_engine.gleam
Normal file
40
src/spacetraders_models/ship_engine.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/engine_symbol.{type EngineSymbol}
|
||||||
|
import spacetraders_models/ship_component_condition.{type ShipComponentCondition}
|
||||||
|
import spacetraders_models/ship_component_integrity.{type ShipComponentIntegrity}
|
||||||
|
import spacetraders_models/ship_component_quality.{type ShipComponentQuality}
|
||||||
|
import spacetraders_models/ship_requirements.{type ShipRequirements}
|
||||||
|
|
||||||
|
pub type ShipEngine {
|
||||||
|
ShipEngine(
|
||||||
|
symbol: EngineSymbol,
|
||||||
|
name: String,
|
||||||
|
condition: ShipComponentCondition,
|
||||||
|
integrity: ShipComponentIntegrity,
|
||||||
|
description: String,
|
||||||
|
speed: Int,
|
||||||
|
requirements: ShipRequirements,
|
||||||
|
quality: ShipComponentQuality,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipEngine) {
|
||||||
|
use symbol <- decode.field("symbol", engine_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use condition <- decode.field("condition", ship_component_condition.decoder())
|
||||||
|
use integrity <- decode.field("integrity", ship_component_integrity.decoder())
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use speed <- decode.field("speed", decode.int)
|
||||||
|
use requirements <- decode.field("requirements", ship_requirements.decoder())
|
||||||
|
use quality <- decode.field("quality", ship_component_quality.decoder())
|
||||||
|
decode.success(ShipEngine(
|
||||||
|
symbol:,
|
||||||
|
name:,
|
||||||
|
condition:,
|
||||||
|
integrity:,
|
||||||
|
description:,
|
||||||
|
speed:,
|
||||||
|
requirements:,
|
||||||
|
quality:,
|
||||||
|
))
|
||||||
|
}
|
46
src/spacetraders_models/ship_frame.gleam
Normal file
46
src/spacetraders_models/ship_frame.gleam
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/frame_symbol.{type FrameSymbol}
|
||||||
|
import spacetraders_models/ship_component_condition.{type ShipComponentCondition}
|
||||||
|
import spacetraders_models/ship_component_integrity.{type ShipComponentIntegrity}
|
||||||
|
import spacetraders_models/ship_component_quality.{type ShipComponentQuality}
|
||||||
|
import spacetraders_models/ship_requirements.{type ShipRequirements}
|
||||||
|
|
||||||
|
pub type ShipFrame {
|
||||||
|
ShipFrame(
|
||||||
|
symbol: FrameSymbol,
|
||||||
|
name: String,
|
||||||
|
condition: ShipComponentCondition,
|
||||||
|
integrity: ShipComponentIntegrity,
|
||||||
|
description: String,
|
||||||
|
module_slots: Int,
|
||||||
|
mounting_points: Int,
|
||||||
|
fuel_capacity: Int,
|
||||||
|
requirements: ShipRequirements,
|
||||||
|
quality: ShipComponentQuality,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipFrame) {
|
||||||
|
use symbol <- decode.field("symbol", frame_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use condition <- decode.field("condition", ship_component_condition.decoder())
|
||||||
|
use integrity <- decode.field("integrity", ship_component_integrity.decoder())
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use module_slots <- decode.field("moduleSlots", decode.int)
|
||||||
|
use mounting_points <- decode.field("mountingPoints", decode.int)
|
||||||
|
use fuel_capacity <- decode.field("fuelCapacity", decode.int)
|
||||||
|
use requirements <- decode.field("requirements", ship_requirements.decoder())
|
||||||
|
use quality <- decode.field("quality", ship_component_quality.decoder())
|
||||||
|
decode.success(ShipFrame(
|
||||||
|
symbol:,
|
||||||
|
name:,
|
||||||
|
condition:,
|
||||||
|
integrity:,
|
||||||
|
description:,
|
||||||
|
module_slots:,
|
||||||
|
mounting_points:,
|
||||||
|
fuel_capacity:,
|
||||||
|
requirements:,
|
||||||
|
quality:,
|
||||||
|
))
|
||||||
|
}
|
18
src/spacetraders_models/ship_fuel.gleam
Normal file
18
src/spacetraders_models/ship_fuel.gleam
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/ship_fuel_consumed.{type ShipFuelConsumed}
|
||||||
|
|
||||||
|
pub type ShipFuel {
|
||||||
|
ShipFuel(current: Int, capacity: Int, consumed: Option(ShipFuelConsumed))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipFuel) {
|
||||||
|
use current <- decode.field("current", decode.int)
|
||||||
|
use capacity <- decode.field("capacity", decode.int)
|
||||||
|
use consumed <- decode.optional_field(
|
||||||
|
"consumed",
|
||||||
|
option.None,
|
||||||
|
decode.optional(ship_fuel_consumed.decoder()),
|
||||||
|
)
|
||||||
|
decode.success(ShipFuel(current:, capacity:, consumed:))
|
||||||
|
}
|
13
src/spacetraders_models/ship_fuel_consumed.gleam
Normal file
13
src/spacetraders_models/ship_fuel_consumed.gleam
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/time/timestamp.{type Timestamp}
|
||||||
|
import spacetraders_models/internal/time
|
||||||
|
|
||||||
|
pub type ShipFuelConsumed {
|
||||||
|
ShipFuelConsumed(amount: Int, timestamp: Timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipFuelConsumed) {
|
||||||
|
use amount <- decode.field("amount", decode.int)
|
||||||
|
use timestamp <- decode.field("timestamp", time.rfc3339_timestamp_decoder())
|
||||||
|
decode.success(ShipFuelConsumed(amount:, timestamp:))
|
||||||
|
}
|
34
src/spacetraders_models/ship_modification_transaction.gleam
Normal file
34
src/spacetraders_models/ship_modification_transaction.gleam
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
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/trade_symbol.{type TradeSymbol}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type ShipModificationTransaction {
|
||||||
|
ShipModificationTransaction(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
ship_symbol: ShipSymbol,
|
||||||
|
trade_symbol: TradeSymbol,
|
||||||
|
total_price: Int,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipModificationTransaction) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
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())
|
||||||
|
decode.success(ShipModificationTransaction(
|
||||||
|
waypoint_symbol:,
|
||||||
|
ship_symbol:,
|
||||||
|
trade_symbol:,
|
||||||
|
total_price:,
|
||||||
|
timestamp:,
|
||||||
|
))
|
||||||
|
}
|
40
src/spacetraders_models/ship_module.gleam
Normal file
40
src/spacetraders_models/ship_module.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/module_symbol.{type ModuleSymbol}
|
||||||
|
import spacetraders_models/ship_requirements.{type ShipRequirements}
|
||||||
|
|
||||||
|
pub type ShipModule {
|
||||||
|
ShipModule(
|
||||||
|
symbol: ModuleSymbol,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
capacity: Option(Int),
|
||||||
|
range: Option(Int),
|
||||||
|
requirements: ShipRequirements,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipModule) {
|
||||||
|
use symbol <- decode.field("symbol", module_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use capacity <- decode.optional_field(
|
||||||
|
"capacity",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.int),
|
||||||
|
)
|
||||||
|
use range <- decode.optional_field(
|
||||||
|
"range",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.int),
|
||||||
|
)
|
||||||
|
use requirements <- decode.field("requirements", ship_requirements.decoder())
|
||||||
|
decode.success(ShipModule(
|
||||||
|
symbol:,
|
||||||
|
name:,
|
||||||
|
description:,
|
||||||
|
capacity:,
|
||||||
|
range:,
|
||||||
|
requirements:,
|
||||||
|
))
|
||||||
|
}
|
41
src/spacetraders_models/ship_mount.gleam
Normal file
41
src/spacetraders_models/ship_mount.gleam
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/mount_deposit.{type MountDeposit}
|
||||||
|
import spacetraders_models/mount_symbol.{type MountSymbol}
|
||||||
|
import spacetraders_models/ship_requirements.{type ShipRequirements}
|
||||||
|
|
||||||
|
pub type ShipMount {
|
||||||
|
ShipMount(
|
||||||
|
symbol: MountSymbol,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
strength: Option(Int),
|
||||||
|
deposits: Option(List(MountDeposit)),
|
||||||
|
requirements: ShipRequirements,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipMount) {
|
||||||
|
use symbol <- decode.field("symbol", mount_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use strength <- decode.optional_field(
|
||||||
|
"strength",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.int),
|
||||||
|
)
|
||||||
|
use deposits <- decode.optional_field(
|
||||||
|
"deposits",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(mount_deposit.decoder())),
|
||||||
|
)
|
||||||
|
use requirements <- decode.field("requirements", ship_requirements.decoder())
|
||||||
|
decode.success(ShipMount(
|
||||||
|
symbol:,
|
||||||
|
name:,
|
||||||
|
description:,
|
||||||
|
strength:,
|
||||||
|
deposits:,
|
||||||
|
requirements:,
|
||||||
|
))
|
||||||
|
}
|
34
src/spacetraders_models/ship_nav.gleam
Normal file
34
src/spacetraders_models/ship_nav.gleam
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/ship_nav_flight_mode.{type ShipNavFlightMode}
|
||||||
|
import spacetraders_models/ship_nav_route.{type ShipNavRoute}
|
||||||
|
import spacetraders_models/ship_nav_status.{type ShipNavStatus}
|
||||||
|
import spacetraders_models/system_symbol.{type SystemSymbol}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type ShipNav {
|
||||||
|
ShipNav(
|
||||||
|
system_symbol: SystemSymbol,
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
route: ShipNavRoute,
|
||||||
|
status: ShipNavStatus,
|
||||||
|
flight_mode: ShipNavFlightMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipNav) {
|
||||||
|
use system_symbol <- decode.field("systemSymbol", system_symbol.decoder())
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
use route <- decode.field("route", ship_nav_route.decoder())
|
||||||
|
use status <- decode.field("status", ship_nav_status.decoder())
|
||||||
|
use flight_mode <- decode.field("flightMode", ship_nav_flight_mode.decoder())
|
||||||
|
decode.success(ShipNav(
|
||||||
|
system_symbol:,
|
||||||
|
waypoint_symbol:,
|
||||||
|
route:,
|
||||||
|
status:,
|
||||||
|
flight_mode:,
|
||||||
|
))
|
||||||
|
}
|
40
src/spacetraders_models/ship_nav_flight_mode.gleam
Normal file
40
src/spacetraders_models/ship_nav_flight_mode.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ShipNavFlightMode {
|
||||||
|
Drift
|
||||||
|
Stealth
|
||||||
|
Cruise
|
||||||
|
Burn
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipNavFlightMode, Nil) {
|
||||||
|
case value {
|
||||||
|
"DRIFT" -> Ok(Drift)
|
||||||
|
"STEALTH" -> Ok(Stealth)
|
||||||
|
"CRUISE" -> Ok(Cruise)
|
||||||
|
"BURN" -> Ok(Burn)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipNavFlightMode) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_nav_flight_mode) -> decode.success(ship_nav_flight_mode)
|
||||||
|
Error(Nil) -> decode.failure(Drift, "ShipNavFlightMode")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(ship_nav_flight_mode: ShipNavFlightMode) -> String {
|
||||||
|
case ship_nav_flight_mode {
|
||||||
|
Drift -> "DRIFT"
|
||||||
|
Stealth -> "STEALTH"
|
||||||
|
Cruise -> "CRUISE"
|
||||||
|
Burn -> "BURN"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_nav_flight_mode: ShipNavFlightMode) -> Json {
|
||||||
|
json.string(to_string(ship_nav_flight_mode))
|
||||||
|
}
|
27
src/spacetraders_models/ship_nav_route.gleam
Normal file
27
src/spacetraders_models/ship_nav_route.gleam
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
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}
|
||||||
|
|
||||||
|
pub type ShipNavRoute {
|
||||||
|
ShipNavRoute(
|
||||||
|
destination: ShipNavRouteWaypoint,
|
||||||
|
origin: ShipNavRouteWaypoint,
|
||||||
|
departure_time: Timestamp,
|
||||||
|
arrival: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipNavRoute) {
|
||||||
|
use destination <- decode.field(
|
||||||
|
"destination",
|
||||||
|
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())
|
||||||
|
decode.success(ShipNavRoute(destination:, origin:, departure_time:, arrival:))
|
||||||
|
}
|
23
src/spacetraders_models/ship_nav_route_waypoint.gleam
Normal file
23
src/spacetraders_models/ship_nav_route_waypoint.gleam
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/system_symbol.{type SystemSymbol}
|
||||||
|
import spacetraders_models/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
import spacetraders_models/waypoint_type.{type WaypointType}
|
||||||
|
|
||||||
|
pub type ShipNavRouteWaypoint {
|
||||||
|
ShipNavRouteWaypoint(
|
||||||
|
symbol: WaypointSymbol,
|
||||||
|
type_: WaypointType,
|
||||||
|
system_symbol: SystemSymbol,
|
||||||
|
x: Int,
|
||||||
|
y: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipNavRouteWaypoint) {
|
||||||
|
use symbol <- decode.field("symbol", waypoint_symbol.decoder())
|
||||||
|
use type_ <- decode.field("type", waypoint_type.decoder())
|
||||||
|
use system_symbol <- decode.field("systemSymbol", system_symbol.decoder())
|
||||||
|
use x <- decode.field("x", decode.int)
|
||||||
|
use y <- decode.field("y", decode.int)
|
||||||
|
decode.success(ShipNavRouteWaypoint(symbol:, type_:, system_symbol:, x:, y:))
|
||||||
|
}
|
37
src/spacetraders_models/ship_nav_status.gleam
Normal file
37
src/spacetraders_models/ship_nav_status.gleam
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ShipNavStatus {
|
||||||
|
InTransit
|
||||||
|
InOrbit
|
||||||
|
Docked
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipNavStatus, Nil) {
|
||||||
|
case value {
|
||||||
|
"IN_TRANSIT" -> Ok(InTransit)
|
||||||
|
"IN_ORBIT" -> Ok(InOrbit)
|
||||||
|
"DOCKED" -> Ok(Docked)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipNavStatus) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_nav_status) -> decode.success(ship_nav_status)
|
||||||
|
Error(Nil) -> decode.failure(InTransit, "ShipNavStatus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(ship_nav_status: ShipNavStatus) -> String {
|
||||||
|
case ship_nav_status {
|
||||||
|
InTransit -> "IN_TRANSIT"
|
||||||
|
InOrbit -> "IN_ORBIT"
|
||||||
|
Docked -> "DOCKED"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_nav_status: ShipNavStatus) -> Json {
|
||||||
|
json.string(to_string(ship_nav_status))
|
||||||
|
}
|
40
src/spacetraders_models/ship_reactor.gleam
Normal file
40
src/spacetraders_models/ship_reactor.gleam
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/reactor_symbol.{type ReactorSymbol}
|
||||||
|
import spacetraders_models/ship_component_condition.{type ShipComponentCondition}
|
||||||
|
import spacetraders_models/ship_component_integrity.{type ShipComponentIntegrity}
|
||||||
|
import spacetraders_models/ship_component_quality.{type ShipComponentQuality}
|
||||||
|
import spacetraders_models/ship_requirements.{type ShipRequirements}
|
||||||
|
|
||||||
|
pub type ShipReactor {
|
||||||
|
ShipReactor(
|
||||||
|
symbol: ReactorSymbol,
|
||||||
|
name: String,
|
||||||
|
condition: ShipComponentCondition,
|
||||||
|
integrity: ShipComponentIntegrity,
|
||||||
|
description: String,
|
||||||
|
power_output: Int,
|
||||||
|
requirements: ShipRequirements,
|
||||||
|
quality: ShipComponentQuality,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipReactor) {
|
||||||
|
use symbol <- decode.field("symbol", reactor_symbol.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use condition <- decode.field("condition", ship_component_condition.decoder())
|
||||||
|
use integrity <- decode.field("integrity", ship_component_integrity.decoder())
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use power_output <- decode.field("powerOutput", decode.int)
|
||||||
|
use requirements <- decode.field("requirements", ship_requirements.decoder())
|
||||||
|
use quality <- decode.field("quality", ship_component_quality.decoder())
|
||||||
|
decode.success(ShipReactor(
|
||||||
|
symbol:,
|
||||||
|
name:,
|
||||||
|
condition:,
|
||||||
|
integrity:,
|
||||||
|
description:,
|
||||||
|
power_output:,
|
||||||
|
requirements:,
|
||||||
|
quality:,
|
||||||
|
))
|
||||||
|
}
|
19
src/spacetraders_models/ship_registration.gleam
Normal file
19
src/spacetraders_models/ship_registration.gleam
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/faction_symbol.{type FactionSymbol}
|
||||||
|
import spacetraders_models/ship_role.{type ShipRole}
|
||||||
|
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||||
|
|
||||||
|
pub type ShipRegistration {
|
||||||
|
ShipRegistration(
|
||||||
|
name: ShipSymbol,
|
||||||
|
faction_symbol: FactionSymbol,
|
||||||
|
role: ShipRole,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipRegistration) {
|
||||||
|
use name <- decode.field("name", ship_symbol.decoder())
|
||||||
|
use faction_symbol <- decode.field("factionSymbol", faction_symbol.decoder())
|
||||||
|
use role <- decode.field("role", ship_role.decoder())
|
||||||
|
decode.success(ShipRegistration(name:, faction_symbol:, role:))
|
||||||
|
}
|
25
src/spacetraders_models/ship_requirements.gleam
Normal file
25
src/spacetraders_models/ship_requirements.gleam
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
|
||||||
|
pub type ShipRequirements {
|
||||||
|
ShipRequirements(power: Option(Int), crew: Option(Int), slots: Option(Int))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipRequirements) {
|
||||||
|
use power <- decode.optional_field(
|
||||||
|
"power",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.int),
|
||||||
|
)
|
||||||
|
use crew <- decode.optional_field(
|
||||||
|
"crew",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.int),
|
||||||
|
)
|
||||||
|
use slots <- decode.optional_field(
|
||||||
|
"slots",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.int),
|
||||||
|
)
|
||||||
|
decode.success(ShipRequirements(power:, crew:, slots:))
|
||||||
|
}
|
70
src/spacetraders_models/ship_role.gleam
Normal file
70
src/spacetraders_models/ship_role.gleam
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ShipRole {
|
||||||
|
Fabricator
|
||||||
|
Harvester
|
||||||
|
Hauler
|
||||||
|
Interceptor
|
||||||
|
Excavator
|
||||||
|
Transport
|
||||||
|
Repair
|
||||||
|
Surveyor
|
||||||
|
Command
|
||||||
|
Carrier
|
||||||
|
Patrol
|
||||||
|
Satellite
|
||||||
|
Explorer
|
||||||
|
Refinery
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipRole, Nil) {
|
||||||
|
case value {
|
||||||
|
"FABRICATOR" -> Ok(Fabricator)
|
||||||
|
"HARVESTER" -> Ok(Harvester)
|
||||||
|
"HAULER" -> Ok(Hauler)
|
||||||
|
"INTERCEPTOR" -> Ok(Interceptor)
|
||||||
|
"EXCAVATOR" -> Ok(Excavator)
|
||||||
|
"TRANSPORT" -> Ok(Transport)
|
||||||
|
"REPAIR" -> Ok(Repair)
|
||||||
|
"SURVEYOR" -> Ok(Surveyor)
|
||||||
|
"COMMAND" -> Ok(Command)
|
||||||
|
"CARRIER" -> Ok(Carrier)
|
||||||
|
"PATROL" -> Ok(Patrol)
|
||||||
|
"SATELLITE" -> Ok(Satellite)
|
||||||
|
"EXPLORER" -> Ok(Explorer)
|
||||||
|
"REFINERY" -> Ok(Refinery)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipRole) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_role) -> decode.success(ship_role)
|
||||||
|
Error(Nil) -> decode.failure(Fabricator, "ShipRole")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(ship_role: ShipRole) -> String {
|
||||||
|
case ship_role {
|
||||||
|
Fabricator -> "FABRICATOR"
|
||||||
|
Harvester -> "HARVESTER"
|
||||||
|
Hauler -> "HAULER"
|
||||||
|
Interceptor -> "INTERCEPTOR"
|
||||||
|
Excavator -> "EXCAVATOR"
|
||||||
|
Transport -> "TRANSPORT"
|
||||||
|
Repair -> "REPAIR"
|
||||||
|
Surveyor -> "SURVEYOR"
|
||||||
|
Command -> "COMMAND"
|
||||||
|
Carrier -> "CARRIER"
|
||||||
|
Patrol -> "PATROL"
|
||||||
|
Satellite -> "SATELLITE"
|
||||||
|
Explorer -> "EXPLORER"
|
||||||
|
Refinery -> "REFINERY"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_role: ShipRole) -> Json {
|
||||||
|
json.string(to_string(ship_role))
|
||||||
|
}
|
33
src/spacetraders_models/ship_symbol.gleam
Normal file
33
src/spacetraders_models/ship_symbol.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type ShipSymbol {
|
||||||
|
ShipSymbol(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipSymbol, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(ShipSymbol(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_symbol) -> decode.success(ship_symbol)
|
||||||
|
Error(Nil) -> decode.failure(ShipSymbol("invalid"), "ShipSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(ship_symbol: ShipSymbol) -> String {
|
||||||
|
let ShipSymbol(symbol) = ship_symbol
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_symbol: ShipSymbol) -> Json {
|
||||||
|
json.string(to_string(ship_symbol))
|
||||||
|
}
|
43
src/spacetraders_models/ship_type.gleam
Normal file
43
src/spacetraders_models/ship_type.gleam
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type ShipType {
|
||||||
|
ShipProbe
|
||||||
|
ShipMiningDrone
|
||||||
|
ShipSiphonDrone
|
||||||
|
ShipInterceptor
|
||||||
|
ShipLightHauler
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipType, Nil) {
|
||||||
|
case value {
|
||||||
|
"SHIP_PROBE" -> Ok(ShipProbe)
|
||||||
|
"SHIP_MINING_DRONE" -> Ok(ShipMiningDrone)
|
||||||
|
"SHIP_SIPHON_DRONE" -> Ok(ShipSiphonDrone)
|
||||||
|
"SHIP_INTERCEPTOR" -> Ok(ShipInterceptor)
|
||||||
|
"SHIP_LIGHT_HAULER" -> Ok(ShipLightHauler)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipType) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(ship_type) -> decode.success(ship_type)
|
||||||
|
Error(Nil) -> decode.failure(ShipProbe, "ShipType")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(ship_type: ShipType) -> String {
|
||||||
|
case ship_type {
|
||||||
|
ShipProbe -> "SHIP_PROBE"
|
||||||
|
ShipMiningDrone -> "SHIP_MINING_DRONE"
|
||||||
|
ShipSiphonDrone -> "SHIP_SIPHON_DRONE"
|
||||||
|
ShipInterceptor -> "SHIP_INTERCEPTOR"
|
||||||
|
ShipLightHauler -> "SHIP_LIGHT_HAULER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(ship_type: ShipType) -> Json {
|
||||||
|
json.string(to_string(ship_type))
|
||||||
|
}
|
39
src/spacetraders_models/shipyard.gleam
Normal file
39
src/spacetraders_models/shipyard.gleam
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/ship_type.{type ShipType}
|
||||||
|
import spacetraders_models/shipyard_ship.{type ShipyardShip}
|
||||||
|
import spacetraders_models/shipyard_symbol.{type ShipyardSymbol}
|
||||||
|
import spacetraders_models/shipyard_transaction.{type ShipyardTransaction}
|
||||||
|
|
||||||
|
pub type Shipyard {
|
||||||
|
Shipyard(
|
||||||
|
symbol: ShipyardSymbol,
|
||||||
|
ship_types: List(ShipType),
|
||||||
|
transactions: Option(List(ShipyardTransaction)),
|
||||||
|
ships: Option(List(ShipyardShip)),
|
||||||
|
modifications_fee: Int,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Shipyard) {
|
||||||
|
use symbol <- decode.field("symbol", shipyard_symbol.decoder())
|
||||||
|
use ship_types <- decode.field("shipTypes", decode.list(ship_type.decoder()))
|
||||||
|
use transactions <- decode.optional_field(
|
||||||
|
"transactions",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(shipyard_transaction.decoder())),
|
||||||
|
)
|
||||||
|
use ships <- decode.optional_field(
|
||||||
|
"ships",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.list(shipyard_ship.decoder())),
|
||||||
|
)
|
||||||
|
use modifications_fee <- decode.field("modificationsFee", decode.int)
|
||||||
|
decode.success(Shipyard(
|
||||||
|
symbol:,
|
||||||
|
ship_types:,
|
||||||
|
transactions:,
|
||||||
|
ships:,
|
||||||
|
modifications_fee:,
|
||||||
|
))
|
||||||
|
}
|
61
src/spacetraders_models/shipyard_ship.gleam
Normal file
61
src/spacetraders_models/shipyard_ship.gleam
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
import spacetraders_models/activity_level.{type ActivityLevel}
|
||||||
|
import spacetraders_models/ship_engine.{type ShipEngine}
|
||||||
|
import spacetraders_models/ship_frame.{type ShipFrame}
|
||||||
|
import spacetraders_models/ship_module.{type ShipModule}
|
||||||
|
import spacetraders_models/ship_mount.{type ShipMount}
|
||||||
|
import spacetraders_models/ship_reactor.{type ShipReactor}
|
||||||
|
import spacetraders_models/ship_type.{type ShipType}
|
||||||
|
import spacetraders_models/shipyard_ship_crew.{type ShipyardShipCrew}
|
||||||
|
import spacetraders_models/supply_level.{type SupplyLevel}
|
||||||
|
|
||||||
|
pub type ShipyardShip {
|
||||||
|
ShipyardShip(
|
||||||
|
type_: ShipType,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
activity: Option(ActivityLevel),
|
||||||
|
supply: SupplyLevel,
|
||||||
|
purchase_price: Int,
|
||||||
|
frame: ShipFrame,
|
||||||
|
reactor: ShipReactor,
|
||||||
|
engine: ShipEngine,
|
||||||
|
modules: List(ShipModule),
|
||||||
|
mounts: List(ShipMount),
|
||||||
|
crew: ShipyardShipCrew,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipyardShip) {
|
||||||
|
use type_ <- decode.field("type", ship_type.decoder())
|
||||||
|
use name <- decode.field("name", decode.string)
|
||||||
|
use description <- decode.field("description", decode.string)
|
||||||
|
use activity <- decode.optional_field(
|
||||||
|
"activity",
|
||||||
|
option.None,
|
||||||
|
decode.optional(activity_level.decoder()),
|
||||||
|
)
|
||||||
|
use supply <- decode.field("supply", supply_level.decoder())
|
||||||
|
use purchase_price <- decode.field("purchasePrice", decode.int)
|
||||||
|
use frame <- decode.field("frame", ship_frame.decoder())
|
||||||
|
use reactor <- decode.field("reactor", ship_reactor.decoder())
|
||||||
|
use engine <- decode.field("engine", ship_engine.decoder())
|
||||||
|
use modules <- decode.field("modules", decode.list(ship_module.decoder()))
|
||||||
|
use mounts <- decode.field("mounts", decode.list(ship_mount.decoder()))
|
||||||
|
use crew <- decode.field("crew", shipyard_ship_crew.decoder())
|
||||||
|
decode.success(ShipyardShip(
|
||||||
|
type_:,
|
||||||
|
name:,
|
||||||
|
description:,
|
||||||
|
activity:,
|
||||||
|
supply:,
|
||||||
|
purchase_price:,
|
||||||
|
frame:,
|
||||||
|
reactor:,
|
||||||
|
engine:,
|
||||||
|
modules:,
|
||||||
|
mounts:,
|
||||||
|
crew:,
|
||||||
|
))
|
||||||
|
}
|
11
src/spacetraders_models/shipyard_ship_crew.gleam
Normal file
11
src/spacetraders_models/shipyard_ship_crew.gleam
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
|
||||||
|
pub type ShipyardShipCrew {
|
||||||
|
ShipCrew(required: Int, capacity: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipyardShipCrew) {
|
||||||
|
use required <- decode.field("required", decode.int)
|
||||||
|
use capacity <- decode.field("capacity", decode.int)
|
||||||
|
decode.success(ShipCrew(required:, capacity:))
|
||||||
|
}
|
33
src/spacetraders_models/shipyard_symbol.gleam
Normal file
33
src/spacetraders_models/shipyard_symbol.gleam
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
import gleam/string
|
||||||
|
|
||||||
|
pub opaque type ShipyardSymbol {
|
||||||
|
ShipyardSymbol(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const min_length: Int = 1
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(ShipyardSymbol, Nil) {
|
||||||
|
case string.length(value) >= min_length {
|
||||||
|
True -> Ok(ShipyardSymbol(value))
|
||||||
|
False -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipyardSymbol) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(shipyard_symbol) -> decode.success(shipyard_symbol)
|
||||||
|
Error(Nil) -> decode.failure(ShipyardSymbol("invalid"), "ShipyardSymbol")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(shipyard_symbol: ShipyardSymbol) -> String {
|
||||||
|
let ShipyardSymbol(symbol) = shipyard_symbol
|
||||||
|
symbol
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(shipyard_symbol: ShipyardSymbol) -> Json {
|
||||||
|
json.string(to_string(shipyard_symbol))
|
||||||
|
}
|
34
src/spacetraders_models/shipyard_transaction.gleam
Normal file
34
src/spacetraders_models/shipyard_transaction.gleam
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
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/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type ShipyardTransaction {
|
||||||
|
ShipyardTransaction(
|
||||||
|
waypoint_symbol: WaypointSymbol,
|
||||||
|
ship_type: ShipType,
|
||||||
|
price: Int,
|
||||||
|
agent_symbol: AgentSymbol,
|
||||||
|
timestamp: Timestamp,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(ShipyardTransaction) {
|
||||||
|
use waypoint_symbol <- decode.field(
|
||||||
|
"waypointSymbol",
|
||||||
|
waypoint_symbol.decoder(),
|
||||||
|
)
|
||||||
|
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())
|
||||||
|
decode.success(ShipyardTransaction(
|
||||||
|
waypoint_symbol:,
|
||||||
|
ship_type:,
|
||||||
|
price:,
|
||||||
|
agent_symbol:,
|
||||||
|
timestamp:,
|
||||||
|
))
|
||||||
|
}
|
13
src/spacetraders_models/siphon.gleam
Normal file
13
src/spacetraders_models/siphon.gleam
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/ship_symbol.{type ShipSymbol}
|
||||||
|
import spacetraders_models/siphon_yield.{type SiphonYield}
|
||||||
|
|
||||||
|
pub type Siphon {
|
||||||
|
Siphon(ship_symbol: ShipSymbol, yield: SiphonYield)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Siphon) {
|
||||||
|
use ship_symbol <- decode.field("shipSymbol", ship_symbol.decoder())
|
||||||
|
use yield <- decode.field("yield", siphon_yield.decoder())
|
||||||
|
decode.success(Siphon(ship_symbol:, yield:))
|
||||||
|
}
|
12
src/spacetraders_models/siphon_yield.gleam
Normal file
12
src/spacetraders_models/siphon_yield.gleam
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import spacetraders_models/trade_symbol.{type TradeSymbol}
|
||||||
|
|
||||||
|
pub type SiphonYield {
|
||||||
|
SiphonYield(symbol: TradeSymbol, units: Int)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(SiphonYield) {
|
||||||
|
use symbol <- decode.field("symbol", trade_symbol.decoder())
|
||||||
|
use units <- decode.field("units", decode.int)
|
||||||
|
decode.success(SiphonYield(symbol:, units:))
|
||||||
|
}
|
28
src/spacetraders_models/spacetraders_error.gleam
Normal file
28
src/spacetraders_models/spacetraders_error.gleam
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import gleam/dynamic.{type Dynamic}
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/option.{type Option}
|
||||||
|
|
||||||
|
pub type SpacetradersError {
|
||||||
|
SpacetradersError(
|
||||||
|
code: Int,
|
||||||
|
message: String,
|
||||||
|
data: Option(Dynamic),
|
||||||
|
request_id: Option(String),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(SpacetradersError) {
|
||||||
|
use code <- decode.field("code", decode.int)
|
||||||
|
use message <- decode.field("message", decode.string)
|
||||||
|
use data <- decode.optional_field(
|
||||||
|
"data",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.dynamic),
|
||||||
|
)
|
||||||
|
use request_id <- decode.optional_field(
|
||||||
|
"requestId",
|
||||||
|
option.None,
|
||||||
|
decode.optional(decode.string),
|
||||||
|
)
|
||||||
|
decode.success(SpacetradersError(code:, message:, data:, request_id:))
|
||||||
|
}
|
43
src/spacetraders_models/supply_level.gleam
Normal file
43
src/spacetraders_models/supply_level.gleam
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
import gleam/dynamic/decode.{type Decoder}
|
||||||
|
import gleam/json.{type Json}
|
||||||
|
|
||||||
|
pub type SupplyLevel {
|
||||||
|
Scarce
|
||||||
|
Limited
|
||||||
|
Moderate
|
||||||
|
High
|
||||||
|
Abundant
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse(value: String) -> Result(SupplyLevel, Nil) {
|
||||||
|
case value {
|
||||||
|
"SCARCE" -> Ok(Scarce)
|
||||||
|
"LIMITED" -> Ok(Limited)
|
||||||
|
"MODERATE" -> Ok(Moderate)
|
||||||
|
"HIGH" -> Ok(High)
|
||||||
|
"ABUNDANT" -> Ok(Abundant)
|
||||||
|
_ -> Error(Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(SupplyLevel) {
|
||||||
|
use value <- decode.then(decode.string)
|
||||||
|
case parse(value) {
|
||||||
|
Ok(supply_level) -> decode.success(supply_level)
|
||||||
|
Error(Nil) -> decode.failure(Scarce, "SupplyLevel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_string(supply_level: SupplyLevel) -> String {
|
||||||
|
case supply_level {
|
||||||
|
Scarce -> "SCARCE"
|
||||||
|
Limited -> "LIMITED"
|
||||||
|
Moderate -> "MODERATE"
|
||||||
|
High -> "HIGH"
|
||||||
|
Abundant -> "ABUNDANT"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(supply_level: SupplyLevel) -> Json {
|
||||||
|
json.string(to_string(supply_level))
|
||||||
|
}
|
44
src/spacetraders_models/survey.gleam
Normal file
44
src/spacetraders_models/survey.gleam
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
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/waypoint_symbol.{type WaypointSymbol}
|
||||||
|
|
||||||
|
pub type Survey {
|
||||||
|
Survey(
|
||||||
|
signature: SurveySignature,
|
||||||
|
symbol: WaypointSymbol,
|
||||||
|
deposits: List(SurveyDeposit),
|
||||||
|
expiration: Timestamp,
|
||||||
|
size: SurveySize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decoder() -> Decoder(Survey) {
|
||||||
|
use signature <- decode.field("signature", survey_signature.decoder())
|
||||||
|
use symbol <- decode.field("symbol", waypoint_symbol.decoder())
|
||||||
|
use deposits <- decode.field(
|
||||||
|
"deposits",
|
||||||
|
decode.list(survey_deposit.decoder()),
|
||||||
|
)
|
||||||
|
use expiration <- decode.field("expiration", time.rfc3339_timestamp_decoder())
|
||||||
|
use size <- decode.field("size", survey_size.decoder())
|
||||||
|
decode.success(Survey(signature:, symbol:, deposits:, expiration:, size:))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encode(survey: Survey) -> Json {
|
||||||
|
json.object([
|
||||||
|
#("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)),
|
||||||
|
),
|
||||||
|
#("size", survey_size.encode(survey.size)),
|
||||||
|
])
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue