Fix album view crashing due to strings being used for some int IDs
This commit is contained in:
parent
3144af69e4
commit
22f8574973
3 changed files with 75 additions and 50 deletions
|
@ -9,7 +9,7 @@ import ibroadcast/request.{DecodeFailed}
|
|||
import ibroadcast/authed_request.{type RequestConfig}
|
||||
import ibroadcast/request_params.{type RequestParams}
|
||||
import ibroadcast/http.{type Requestor}
|
||||
import ibroadcast/map_format
|
||||
import ibroadcast/library_format
|
||||
|
||||
pub type Album {
|
||||
Album(
|
||||
|
@ -118,7 +118,7 @@ fn settings_decoder() {
|
|||
}
|
||||
|
||||
fn albums_decoder() {
|
||||
map_format.decoder(map_format.string_int_decoder(), album_decoder())
|
||||
library_format.decoder(library_format.string_int_decoder(), album_decoder())
|
||||
}
|
||||
|
||||
fn album_decoder() {
|
||||
|
@ -135,7 +135,7 @@ fn album_decoder() {
|
|||
}
|
||||
|
||||
fn artists_decoder() {
|
||||
map_format.decoder(map_format.string_int_decoder(), artist_decoder())
|
||||
library_format.decoder(library_format.string_int_decoder(), artist_decoder())
|
||||
}
|
||||
|
||||
fn artist_decoder() {
|
||||
|
@ -153,7 +153,7 @@ fn artist_decoder() {
|
|||
}
|
||||
|
||||
fn tracks_decoder() {
|
||||
map_format.decoder(map_format.string_int_decoder(), track_decoder())
|
||||
library_format.decoder(library_format.string_int_decoder(), track_decoder())
|
||||
}
|
||||
|
||||
fn track_decoder() {
|
||||
|
@ -163,9 +163,18 @@ fn track_decoder() {
|
|||
use title <- result.try(dynamic.element(2, dynamic.string)(data))
|
||||
use genre <- result.try(dynamic.element(3, dynamic.string)(data))
|
||||
use length <- result.try(dynamic.element(4, dynamic.int)(data))
|
||||
use album_id <- result.try(dynamic.element(5, dynamic.int)(data))
|
||||
use artwork_id <- result.try(dynamic.element(6, dynamic.int)(data))
|
||||
use artist_id <- result.try(dynamic.element(7, dynamic.int)(data))
|
||||
use album_id <- result.try(dynamic.element(
|
||||
5,
|
||||
library_format.string_or_int_decoder(),
|
||||
)(data))
|
||||
use artwork_id <- result.try(dynamic.element(
|
||||
6,
|
||||
library_format.string_or_int_decoder(),
|
||||
)(data))
|
||||
use artist_id <- result.try(dynamic.element(
|
||||
7,
|
||||
library_format.string_or_int_decoder(),
|
||||
)(data))
|
||||
use enid <- result.try(dynamic.element(8, dynamic.int)(data))
|
||||
use uploaded_on <- result.try(dynamic.element(9, dynamic.string)(data))
|
||||
use trashed <- result.try(dynamic.element(10, dynamic.bool)(data))
|
||||
|
|
59
src/ibroadcast/library_format.gleam
Normal file
59
src/ibroadcast/library_format.gleam
Normal file
|
@ -0,0 +1,59 @@
|
|||
import gleam/result
|
||||
import gleam/dict.{type Dict}
|
||||
import gleam/int
|
||||
import gleam/dynamic.{type Decoder}
|
||||
import gleam/list
|
||||
import gleam/string
|
||||
|
||||
/// This decoder will attempt to decode a gleam `Dict` using the provided decoders.
|
||||
pub fn decoder(
|
||||
key_decoder: Decoder(k),
|
||||
val_decoder: Decoder(v),
|
||||
) -> Decoder(Dict(k, v)) {
|
||||
fn(data) {
|
||||
// First decode into a `Dict(Dynamic, Dynamic)`
|
||||
use dynamic_dict <- result.try(dynamic.dict(
|
||||
dynamic.dynamic,
|
||||
dynamic.dynamic,
|
||||
)(data))
|
||||
|
||||
let entries = dict.to_list(dynamic_dict)
|
||||
|
||||
// Fold over that dynamic entries list. The accumulator will be the desired
|
||||
// `Dict(k, v)`
|
||||
use data, #(dyn_key, dyn_val) <- list.try_fold(entries, dict.new())
|
||||
|
||||
// Attempt to decode the current value
|
||||
case key_decoder(dyn_key), val_decoder(dyn_val) {
|
||||
// If it succeeds insert the new entry
|
||||
Ok(key), Ok(val) -> Ok(dict.insert(data, key, val))
|
||||
|
||||
// If the key is not an int, ignore it (there are special string keys that
|
||||
// we want to ignore)
|
||||
Error(_), _ -> Ok(data)
|
||||
|
||||
_, Error(ret2) ->
|
||||
Error([dynamic.DecodeError("Decodable value", string.inspect(ret2), [])])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A decoder that accepts strings that contain integers.
|
||||
pub fn string_int_decoder() -> Decoder(Int) {
|
||||
fn(data) {
|
||||
use strval <- result.try(dynamic.string(data))
|
||||
int.parse(strval)
|
||||
|> result.replace_error([
|
||||
dynamic.DecodeError(
|
||||
expected: "A string representing an int",
|
||||
found: strval,
|
||||
path: [],
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
/// A decoder that accepts strings that contain integers or integers directly.
|
||||
pub fn string_or_int_decoder() -> Decoder(Int) {
|
||||
dynamic.any([dynamic.int, string_int_decoder()])
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
import gleam/result
|
||||
import gleam/dict.{type Dict}
|
||||
import gleam/int
|
||||
import gleam/dynamic.{type Decoder}
|
||||
|
||||
/// This decoder will attempt to decode a gleam `Map` using the provided decoders.
|
||||
/// If either a key or a value fails to decode, that entry is just ignored.
|
||||
pub fn decoder(
|
||||
key_decoder: Decoder(k),
|
||||
val_decoder: Decoder(v),
|
||||
) -> Decoder(Dict(k, v)) {
|
||||
fn(data) {
|
||||
// First decode into a `Map(Dynamic, Dynamic)`
|
||||
use dynamic_map <- result.map(dynamic.dict(dynamic.dynamic, dynamic.dynamic)(
|
||||
data,
|
||||
))
|
||||
// Fold over that dynamic map. The accumulator will be the desired `Map(k, v)`
|
||||
use map, dyn_key, dyn_val <- dict.fold(dynamic_map, dict.new())
|
||||
|
||||
// Attempt to decode the current value
|
||||
case key_decoder(dyn_key), val_decoder(dyn_val) {
|
||||
// If it succeeds insert the new entry
|
||||
Ok(key), Ok(val) -> dict.insert(map, key, val)
|
||||
// Otherwise just ignore it and carry on
|
||||
_, _ -> map
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A decoder that accepts strings that contain integers.
|
||||
pub fn string_int_decoder() -> Decoder(Int) {
|
||||
fn(data) {
|
||||
use strval <- result.try(dynamic.string(data))
|
||||
int.parse(strval)
|
||||
|> result.replace_error([
|
||||
dynamic.DecodeError(
|
||||
expected: "A string representing an int",
|
||||
found: strval,
|
||||
path: [],
|
||||
),
|
||||
])
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue