Current section

Files

Jump to
weapp src wechat object.gleam
Raw

src/wechat/object.gleam

//// object module provides core javascript object model
//// it is like a `gleam/json` object with functions
////
//// # Examples
////
//// ```gleam
//// object.literal([#("s", "a string"), #("ss", "another string")])
//// |> object.set("i", 42)
//// |> object.set("f", fn(i) { i + 1 })
//// ```
////
//// is exactly like
////
//// ```js
//// {
//// s: 'a string',
//// ss: 'another string',
//// i: 42,
//// f(i) { i + 1 }
//// }
//// ```
////
import gleam/dict.{type Dict}
import gleam/dynamic.{type Dynamic}
import gleam/dynamic/decode.{type DecodeError, type Decoder}
import gleam/json.{type Json}
import gleam/list
import gleam/result
import gleam/string
/// JsObject models a javascript object
///
pub type JsObject {
JsObject
}
pub type DecodeErrors = List(DecodeError)
/// error constructors
///
pub type WechatError {
NilError(n: Nil)
WechatError(e: String)
WechatDecodeError(es: DecodeErrors)
}
/// most APIs returns either this
/// or a Promise of this type
///
pub type WechatResult =
Result(JsObject, WechatError)
/// most simple callbacks are of this type
///
pub type WechatCallback =
fn() -> Nil
/// constructor of an empty javascript object
///
@external(javascript, "../wechat_ffi.mjs", "obj_new")
pub fn new() -> JsObject
/// `JSON.stringify` for the javascript object
///
@external(javascript, "../wechat_ffi.mjs", "obj_stringify")
pub fn stringify(o: JsObject) -> String
/// convert to `gleam/dynamic`
///
@external(javascript, "../wechat_ffi.mjs", "obj_dynamic")
pub fn dynamic(o: JsObject) -> Dynamic
/// convert to `gleam/dict`
///
@external(javascript, "../wechat_ffi.mjs", "obj_dynamic")
pub fn dict(o: JsObject) -> Dict(String, JsObject)
/// convert to `gleam/json`
///
@external(javascript, "../wechat_ffi.mjs", "obj_dynamic")
pub fn json(o: JsObject) -> Json
/// retrieve a sub javascript object by key `k`
///
@external(javascript, "../wechat_ffi.mjs", "obj_get")
pub fn path(o: JsObject, k: k) -> Result(JsObject, WechatError)
/// execute synchronously with prameter `v` if this object is a function
///
@external(javascript, "../wechat_ffi.mjs", "obj_call")
pub fn call(o: JsObject, v: v) -> Result(JsObject, WechatError)
/// check if a key `k` exists
///
pub fn exist(o: JsObject, k: k) -> Bool {
path(o, k)
|> result.is_ok
}
/// retrive recursively the sub javascript object by path
///
/// # Examples
///
/// ```gleam
/// let f = fn(x) { x + 1 }
/// let b = object.literal([#("a", f)])
/// object.literal([#("b", b)])
/// |> object.paths("b.a")
/// |> result.map(type_of)
/// |> should.equal(Ok(javascript.FunctionType))
/// ```
///
pub fn paths(o: JsObject, path p: String) -> Result(JsObject, WechatError) {
string.split(p, ".")
|> list.try_fold(o, fn(xo, xp) { path(xo, xp) })
}
/// alias for `path`
///
pub fn get(o: JsObject, k: k) -> Result(JsObject, WechatError) {
path(o, k)
}
/// retrive a sub javascript object by key `k`
/// as if it were a `dict(String, JsObject)`
///
pub fn get_kv(o: JsObject, key k: String) -> Result(JsObject, WechatError) {
dict(o)
|> dict.get(k)
|> result.map_error(NilError(_))
}
/// set a key `k` with value `v`
///
/// # Examples
///
/// ```gleam
/// object.new()
/// |> object.set("a", 1)
/// |> object.get("a")
/// |> result.try(object.int)
/// |> should.equal(Ok(1))
/// ```
///
@external(javascript, "../wechat_ffi.mjs", "obj_set")
pub fn set(o: JsObject, k: k, v: v) -> JsObject
/// merge two javascript objects
///
@external(javascript, "../wechat_ffi.mjs", "obj_assign")
pub fn merge(o: JsObject, n: JsObject) -> JsObject
/// convenient constructor if the `(k, v)` pairs are of same type
/// since gleam is strongly typed
///
pub fn literal(ls: List(#(k, v))) -> JsObject {
list.fold(ls, new(), fn(o, p) { set(o, p.0, p.1) })
}
/// convert to int with `gleam/dynamic`
///
pub fn int(o: JsObject) -> Result(Int, WechatError) {
o
|> dynamic
|> decode.run(decode.int)
|> result.map_error(WechatDecodeError(_))
}
/// convert to float with `gleam/dynamic`
///
pub fn float(o: JsObject) -> Result(Float, WechatError) {
o
|> dynamic
|> decode.run(decode.float)
|> result.map_error(WechatDecodeError(_))
}
/// convert to bool with `gleam/dynamic`
///
pub fn bool(o: JsObject) -> Result(Bool, WechatError) {
o
|> dynamic
|> decode.run(decode.bool)
|> result.map_error(WechatDecodeError(_))
}
/// convert to string with `gleam/dynamic`
///
pub fn string(o: JsObject) -> Result(String, WechatError) {
o
|> dynamic
|> decode.run(decode.string)
|> result.map_error(WechatDecodeError(_))
}
/// retrieve and convert a sub object with key `a` of type `t`
/// using the decoder of type `Decoder(t)`, thanks to `gleam/dynamic`
///
pub fn field(
o: JsObject,
name a: name,
of b: Decoder(t),
) -> Result(t, WechatError) {
let decoder = {
use value <- decode.field(a, b)
decode.success(value)
}
o
|> dynamic
|> decode.run(decoder)
|> result.map_error(WechatDecodeError(_))
}
/// convert to `list(t)` with `gleam/dynamic`
///
pub fn list(
o: JsObject,
of f: Decoder(t),
) -> Result(List(t), WechatError) {
o
|> dynamic
|> decode.run(decode.list(f))
|> result.map_error(WechatDecodeError(_))
}