Skip to main content

convex_typegen/convex/
client.rs

1//! Runtime helpers bridging generated args and the official [`convex`] crate.
2
3use std::collections::BTreeMap;
4
5use convex::Value as ConvexValue;
6use serde_json::Value as JsonValue;
7
8/// Infallible conversion from JSON-shaped args into [`ConvexValue`] (typically after serde / `TryFrom`).
9pub trait IntoConvexValue
10{
11    /// Convert the type into a Convex Value
12    fn into_convex_value(self) -> ConvexValue;
13}
14
15impl IntoConvexValue for JsonValue
16{
17    fn into_convex_value(self) -> ConvexValue
18    {
19        match self {
20            JsonValue::Null => ConvexValue::Null,
21            JsonValue::Bool(b) => ConvexValue::Boolean(b),
22            JsonValue::Number(n) => {
23                if let Some(i) = n.as_i64() {
24                    ConvexValue::Int64(i)
25                } else if let Some(f) = n.as_f64() {
26                    ConvexValue::Float64(f)
27                } else {
28                    ConvexValue::Null
29                }
30            }
31            JsonValue::String(s) => ConvexValue::String(s),
32            JsonValue::Array(arr) => ConvexValue::Array(arr.into_iter().map(|v| v.into_convex_value()).collect()),
33            JsonValue::Object(map) => {
34                let converted: BTreeMap<String, ConvexValue> =
35                    map.into_iter().map(|(k, v)| (k, v.into_convex_value())).collect();
36                ConvexValue::Object(converted)
37            }
38        }
39    }
40}
41
42/// Lossy-ish mapping from Convex wire values back to JSON ([`ConvexValue::Bytes`] becomes a JSON array of numbers).
43pub trait ConvexValueExt
44{
45    /// Map a Convex runtime value into [`serde_json::Value`].
46    fn into_serde_value(self) -> JsonValue;
47}
48
49impl ConvexValueExt for ConvexValue
50{
51    fn into_serde_value(self) -> JsonValue
52    {
53        match self {
54            ConvexValue::Null => JsonValue::Null,
55            ConvexValue::Boolean(b) => JsonValue::Bool(b),
56            ConvexValue::Int64(i) => JsonValue::Number(i.into()),
57            ConvexValue::Float64(f) => {
58                if let Some(n) = serde_json::Number::from_f64(f) {
59                    JsonValue::Number(n)
60                } else {
61                    JsonValue::Null
62                }
63            }
64            ConvexValue::String(s) => JsonValue::String(s),
65            ConvexValue::Array(arr) => JsonValue::Array(arr.into_iter().map(|v| v.into_serde_value()).collect()),
66            ConvexValue::Object(map) => JsonValue::Object(map.into_iter().map(|(k, v)| (k, v.into_serde_value())).collect()),
67            ConvexValue::Bytes(b) => JsonValue::Array(b.into_iter().map(|byte| JsonValue::Number(byte.into())).collect()),
68        }
69    }
70}
71
72/// Blanket helpers on [`convex::ConvexClient`] for generated argument structs.
73pub trait ConvexClientExt
74{
75    /// Convert function arguments into Convex-compatible format.
76    ///
77    /// Uses [`TryFrom`] on the generated args type; serialization errors
78    /// (for example non-finite floats) are returned as [`serde_json::Error`].
79    fn prepare_args<T>(args: T) -> Result<BTreeMap<String, ConvexValue>, serde_json::Error>
80    where
81        BTreeMap<String, JsonValue>: TryFrom<T, Error = serde_json::Error>,
82    {
83        let map = BTreeMap::try_from(args)?;
84        Ok(map.into_iter().map(|(k, v)| (k, v.into_convex_value())).collect())
85    }
86}
87
88impl ConvexClientExt for convex::ConvexClient {}