convex/value/
mod.rs

1use std::collections::BTreeMap;
2
3pub mod export;
4mod json;
5mod sorting;
6use thiserror::Error;
7
8/// A value that can be passed as an argument or returned from Convex functions.
9/// They correspond to the [supported Convex types](https://docs.convex.dev/database/types).
10#[derive(Clone, Debug)]
11#[allow(missing_docs)]
12pub enum Value {
13    Null,
14    Int64(i64),
15    Float64(f64),
16    Boolean(bool),
17    String(String),
18    Bytes(Vec<u8>),
19    Array(Vec<Value>),
20    Object(BTreeMap<String, Value>),
21}
22
23impl<T: Into<Value>> From<Option<T>> for Value {
24    fn from(v: Option<T>) -> Value {
25        v.map(|v| v.into()).unwrap_or(Value::Null)
26    }
27}
28
29impl From<i64> for Value {
30    fn from(v: i64) -> Value {
31        Value::Int64(v)
32    }
33}
34
35impl From<f64> for Value {
36    fn from(v: f64) -> Value {
37        Value::Float64(v)
38    }
39}
40
41impl From<bool> for Value {
42    fn from(v: bool) -> Value {
43        Value::Boolean(v)
44    }
45}
46
47impl From<&str> for Value {
48    fn from(v: &str) -> Value {
49        Value::String(v.into())
50    }
51}
52
53impl From<String> for Value {
54    fn from(v: String) -> Value {
55        Value::String(v)
56    }
57}
58
59impl From<Vec<u8>> for Value {
60    fn from(v: Vec<u8>) -> Value {
61        Value::Bytes(v)
62    }
63}
64
65impl From<Vec<Value>> for Value {
66    fn from(v: Vec<Value>) -> Value {
67        Value::Array(v)
68    }
69}
70
71#[cfg(any(test, feature = "testing"))]
72mod proptest {
73    use proptest::prelude::*;
74
75    use super::Value;
76
77    impl Arbitrary for Value {
78        type Parameters = ();
79        type Strategy = proptest::strategy::BoxedStrategy<Self>;
80
81        fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
82            value_strategy(4, 32, 8).boxed()
83        }
84    }
85
86    fn value_strategy(
87        depth: usize,
88        node_target: usize,
89        branching: usize,
90    ) -> impl Strategy<Value = Value> {
91        // https://altsysrq.github.io/proptest-book/proptest/tutorial/recursive.html
92        let leaf = prop_oneof![
93            1 => Just(Value::Null),
94            1 => any::<i64>().prop_map(Value::from),
95            1 => (prop::num::f64::ANY | prop::num::f64::SIGNALING_NAN).prop_map(Value::from),
96            1 => any::<bool>().prop_map(Value::from),
97            1 => any::<String>().prop_map(Value::String),
98            1 => any::<Vec<u8>>().prop_map(Value::Bytes),
99        ];
100        leaf.prop_recursive(
101            depth as u32,
102            node_target as u32,
103            branching as u32,
104            move |inner| {
105                prop_oneof![
106                    // Manually create the strategies here rather than using the `Arbitrary`
107                    // implementations on `Array`, etc. This lets us explicitly pass `inner`
108                    // through rather than starting the `Value` strategy from
109                    // scratch at each tree level.
110                    prop::collection::vec(inner.clone(), 0..branching).prop_map(Value::Array),
111                    prop::collection::btree_map(any::<String>(), inner, 0..branching)
112                        .prop_map(Value::Object),
113                ]
114            },
115        )
116    }
117}
118
119/// An application error that can be returned from Convex functions. To learn
120/// more about throwing custom application errors, see [Convex Errors](https://docs.convex.dev/functions/error-handling/application-errors#throwing-application-errors).
121#[derive(Error, Clone, PartialEq, Eq)]
122#[error("{:}", message)]
123pub struct ConvexError {
124    /// From any error, redacted from prod deployments.
125    pub message: String,
126    /// Custom application error data payload that can be passed from your
127    /// function to a client.
128    pub data: Value,
129}
130
131impl std::fmt::Debug for ConvexError {
132    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
133        let message = &self.message;
134        write!(f, "{message:#?}")
135    }
136}