import gleam/dynamic/decode.{field, int, list, string, success} import gleam/option.{type Option, None} pub type StrongRef { StrongRef(uri: String, cid: String) } pub fn decode_strong_ref() -> decode.Decoder(StrongRef) { use uri <- field("uri", string) use cid <- field("cid", string) success(StrongRef(uri:, cid:)) } pub type Label { Label(val: String) } pub fn decode_label() -> decode.Decoder(Label) { use val <- field("val", string) success(Label(val:)) } pub type ByteSlice { ByteSlice(byte_start: Int, byte_end: Int) } pub fn decode_byte_slice() -> decode.Decoder(ByteSlice) { use byte_start <- field("byteStart", int) use byte_end <- field("byteEnd", int) success(ByteSlice(byte_start:, byte_end:)) } pub type FacetFeature { Mention(did: String) Link(uri: String) Tag(tag: String) } fn decode_facet_feature() -> decode.Decoder(FacetFeature) { use type_ <- field("$type", string) case type_ { "app.bsky.richtext.facet#mention" -> { use did <- field("did", string) success(Mention(did:)) } "app.bsky.richtext.facet#link" -> { use uri <- field("uri", string) success(Link(uri:)) } "app.bsky.richtext.facet#tag" -> { use tag <- field("tag", string) success(Tag(tag:)) } _ -> decode.failure(Mention(""), expected: "FacetFeature") } } pub type Facet { Facet(index: ByteSlice, features: List(FacetFeature)) } pub fn decode_facet() -> decode.Decoder(Facet) { use index <- field("index", decode_byte_slice()) use features <- field("features", list(of: decode_facet_feature())) success(Facet(index:, features:)) } pub type AspectRatio { AspectRatio(width: Int, height: Int) } pub fn decode_aspect_ratio() -> decode.Decoder(AspectRatio) { use width <- field("width", int) use height <- field("height", int) success(AspectRatio(width:, height:)) } pub type Image { Image( alt: String, thumb: Option(String), fullsize: Option(String), aspect_ratio: Option(AspectRatio), ) } pub fn decode_image() -> decode.Decoder(Image) { use alt <- field("alt", string) success(Image(alt:, thumb: None, fullsize: None, aspect_ratio: None)) } pub type External { External( uri: String, title: String, description: String, thumb: Option(String), ) } pub fn decode_external() -> decode.Decoder(External) { use uri <- field("uri", string) use title <- field("title", string) use description <- field("description", string) success(External(uri:, title:, description:, thumb: None)) } pub type Embed { Images(images: List(Image)) ExternalLink(external: External) Record(StrongRef) } pub fn decode_embed() -> decode.Decoder(Embed) { use type_ <- field("$type", string) case type_ { "app.bsky.embed.images" -> { use images <- field("images", list(of: decode_image())) success(Images(images:)) } "app.bsky.embed.external" -> { use external <- field("external", decode_external()) success(ExternalLink(external:)) } "app.bsky.embed.record" -> { use record <- field("record", decode_strong_ref()) success(Record(record)) } _ -> decode.failure(Images(images: []), expected: "Embed type") } } pub type Post { Post( text: String, facets: List(Facet), embed: Option(Embed), langs: List(String), labels: List(Label), tags: List(String), created_at: String, ) } pub fn decode_post() -> decode.Decoder(Post) { use text <- field("text", string) use created_at <- field("createdAt", string) success(Post( text:, facets: [], embed: None, langs: [], labels: [], tags: [], created_at:, )) }