{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module Backend.Rust
  ( generateRustCode,
  )
where

import qualified Convex.Action.Parser as Action
import qualified Convex.Parser as P
import qualified Convex.Schema.Parser as Schema
import Data.Char (isUpper, toLower, toUpper)
import Data.List (intercalate, isInfixOf, isPrefixOf, nub, stripPrefix)
import qualified Data.Map as Map
import PathTree

-- Helper function to prepend a given number of spaces (4 per level).
indent :: Int -> String -> String
indent :: Int -> String -> String
indent Int
n String
s = Int -> Char -> String
forall a. Int -> a -> [a]
replicate (Int
n Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
4) Char
' ' String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s

-- | Top-level function to generate the complete Rust module source.
generateRustCode :: P.ParsedProject -> String
generateRustCode :: ParsedProject -> String
generateRustCode ParsedProject
project =
  [String] -> String
unlines
    [ String
"#![allow(dead_code)]",
      String
"#![allow(non_snake_case)]",
      String
"// Generated by the Palaba code generator. DO NOT EDIT.",
      String
"// Save this file as, for example, `src/convex_api.rs`",
      String
"// and then add `pub mod convex_api;` to your `src/lib.rs` or `src/main.rs`.",
      String
"//",
      String
"// Make sure your `Cargo.toml` contains the following dependencies:",
      String
"// convex = \"0.1.3\"",
      String
"// serde = { version = \"1.0\", features = [\"derive\"] }",
      String
"// serde_json = \"1.0\"",
      String
"// thiserror = \"1.0\"",
      String
"// anyhow = \"1.0\"",
      String
"// futures-util = \"0.3\"",
      String
"",
      ParsedProject -> String
generateRustModuleContent ParsedProject
project
    ]

-- | Generates the entire content for a single Rust module file.
generateRustModuleContent :: P.ParsedProject -> String
generateRustModuleContent :: ParsedProject -> String
generateRustModuleContent ParsedProject
project =
  let (String
apiClassCode, [String]
nestedFromFuncs) = [ConvexFunction] -> (String, [String])
generateApiClass (ParsedProject -> [ConvexFunction]
P.ppFunctions ParsedProject
project)
   in [String] -> String
unlines
        [ String
"use convex::{ConvexClient, FunctionResult, Value};",
          String
"use futures_util::stream::Stream;",
          String
"use serde::{Deserialize, Deserializer, Serialize, Serializer};",
          String
"use serde_json;",
          String
"use std::collections::BTreeMap;",
          String
"use std::fmt::{self, Display};",
          String
"use std::marker::PhantomData;",
          String
"use std::pin::Pin;",
          String
"use std::task::{Context, Poll};",
          String
"",
          String -> String
stripNewlines String
generateErrorEnum,
          String
"",
          String -> String
stripNewlines String
generateIdStruct,
          String
"",
          String -> String
stripNewlines String
generateFromConvexValueBoilerplate,
          String
"",
          String -> String
stripNewlines String
generateSubscriptionBoilerplate,
          String
"",
          String -> String
stripNewlines String
apiClassCode, -- API class and all submodules
          String
"",
          String -> String
stripNewlines (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ ParsedProject -> [String] -> String
generateTypesModule ParsedProject
project ([String] -> [String]
forall a. Eq a => [a] -> [a]
nub [String]
nestedFromFuncs)
        ]

-- | Generates a Rust error enum using `thiserror`.
generateErrorEnum :: String
generateErrorEnum :: String
generateErrorEnum =
  [String] -> String
unlines
    [ String
"/// Represents all possible errors that can occur when interacting with the API.",
      String
"#[derive(thiserror::Error, Debug)]",
      String
"pub enum ApiError {",
      Int -> String -> String
indent Int
1 String
"#[error(\"Convex client error: {0}\")]",
      Int -> String -> String
indent Int
1 String
"ConvexClientError(String),",
      String
"",
      Int -> String -> String
indent Int
1 String
"#[error(\"Convex function error: {0}\")]",
      Int -> String -> String
indent Int
1 String
"ConvexFunctionError(String),",
      String
"",
      Int -> String -> String
indent Int
1 String
"#[error(\"Failed to deserialize response: {0}\")]",
      Int -> String -> String
indent Int
1 String
"DeserializationError(#[from] serde_json::Error),",
      String
"",
      Int -> String -> String
indent Int
1 String
"#[error(\"Unexpected null value returned from a non-nullable function\")]",
      Int -> String -> String
indent Int
1 String
"UnexpectedNullError,",
      String
"}",
      String
"",
      String
"impl From<anyhow::Error> for ApiError {",
      Int -> String -> String
indent Int
1 String
"fn from(err: anyhow::Error) -> Self {",
      Int -> String -> String
indent Int
2 String
"ApiError::ConvexClientError(err.to_string())",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}"
    ]

-- | Generates the strongly-typed `Id<T>` struct.
generateIdStruct :: String
generateIdStruct :: String
generateIdStruct =
  [String] -> String
unlines
    [ String
"/// A strongly-typed Convex document ID.",
      String
"#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]",
      String
"pub struct Id<T> {",
      Int -> String -> String
indent Int
1 String
"id: String,",
      Int -> String -> String
indent Int
1 String
"_phantom: PhantomData<T>,",
      String
"}",
      String
"",
      String
"impl<T> Default for Id<T> {",
      Int -> String -> String
indent Int
1 String
"fn default() -> Self {",
      Int -> String -> String
indent Int
2 String
"Self { id: String::new(), _phantom: PhantomData }",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      String
"impl<T> Id<T> {",
      Int -> String -> String
indent Int
1 String
"pub fn new(id: String) -> Self {",
      Int -> String -> String
indent Int
2 String
"Self { id, _phantom: PhantomData }",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      String
"impl<T> Display for Id<T> {",
      Int -> String -> String
indent Int
1 String
"fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {",
      Int -> String -> String
indent Int
2 String
"write!(f, \"{}\", self.id)",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      String
"impl<T> Serialize for Id<T> {",
      Int -> String -> String
indent Int
1 String
"fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>",
      Int -> String -> String
indent Int
1 String
"where",
      Int -> String -> String
indent Int
2 String
"S: Serializer,",
      Int -> String -> String
indent Int
1 String
"{",
      Int -> String -> String
indent Int
2 String
"serializer.serialize_str(&self.id)",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      String
"impl<'de, T> Deserialize<'de> for Id<T> {",
      Int -> String -> String
indent Int
1 String
"fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>",
      Int -> String -> String
indent Int
1 String
"where",
      Int -> String -> String
indent Int
2 String
"D: Deserializer<'de>,",
      Int -> String -> String
indent Int
1 String
"{",
      Int -> String -> String
indent Int
2 String
"let id = String::deserialize(deserializer)?;",
      Int -> String -> String
indent Int
2 String
"Ok(Id::new(id))",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      String
"impl<T> From<Id<T>> for Value {",
      Int -> String -> String
indent Int
1 String
"fn from(val: Id<T>) -> Self {",
      Int -> String -> String
indent Int
2 String
"Value::String(val.id)",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"impl<T> TryFrom<Value> for Id<T> {",
      Int -> String -> String
indent Int
1 String
"type Error = ApiError;",
      String
"",
      Int -> String -> String
indent Int
1 String
"fn try_from(value: Value) -> Result<Self, Self::Error> {",
      Int -> String -> String
indent Int
2 String
"if let Value::String(id) = value {",
      Int -> String -> String
indent Int
3 String
"Ok(Id::new(id))",
      Int -> String -> String
indent Int
2 String
"} else {",
      Int -> String -> String
indent Int
3 String
"Err(ApiError::ConvexClientError(",
      Int -> String -> String
indent Int
4 String
"\"Expected a string for Id\".to_string(),",
      Int -> String -> String
indent Int
3 String
"))",
      Int -> String -> String
indent Int
2 String
"}",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      Int -> String -> String
indent Int
0 String
"impl<T> FromConvexValue for Id<T> {",
      Int -> String -> String
indent Int
1 String
"fn from_convex(value: Value) -> Result<Self, ApiError> {",
      Int -> String -> String
indent Int
2 String
"Id::try_from(value).map_err(ApiError::from)",
      Int -> String -> String
indent Int
1 String
"}",
      Int -> String -> String
indent Int
0 String
"}"
    ]

-- | Generates the generic TypedSubscription struct and its Stream implementation.
generateSubscriptionBoilerplate :: String
generateSubscriptionBoilerplate :: String
generateSubscriptionBoilerplate =
  [String] -> String
unlines
    [ String
"/// A type-safe, auto-deserializing stream of updates from a Convex query subscription.",
      String
"#[derive(Debug)]",
      String
"pub struct TypedSubscription<T> {",
      Int -> String -> String
indent Int
1 String
"raw_subscription: convex::QuerySubscription,",
      Int -> String -> String
indent Int
1 String
"_phantom: PhantomData<T>,",
      String
"}",
      String
"",
      String
"impl<T> Stream for TypedSubscription<T>",
      String
"where",
      Int -> String -> String
indent Int
1 String
"T: FromConvexValue,",
      String
"{",
      Int -> String -> String
indent Int
1 String
"type Item = Result<T, ApiError>;",
      String
"",
      Int -> String -> String
indent Int
1 String
"fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {",
      Int -> String -> String
indent Int
2 String
"let raw_sub_pin: Pin<&mut _> = unsafe { self.map_unchecked_mut(|s| &mut s.raw_subscription) };",
      Int -> String -> String
indent Int
2 String
"match raw_sub_pin.poll_next(cx) {",
      Int -> String -> String
indent Int
3 String
"Poll::Ready(Some(result)) => {",
      Int -> String -> String
indent Int
4 String
"let item = match result {",
      Int -> String -> String
indent Int
5 String
"FunctionResult::Value(value) => T::from_convex(value),",
      Int -> String -> String
indent Int
5 String
"FunctionResult::ErrorMessage(s) => Err(ApiError::ConvexFunctionError(s)),",
      Int -> String -> String
indent Int
5 String
"FunctionResult::ConvexError(err) => Err(ApiError::ConvexClientError(err.to_string())),",
      Int -> String -> String
indent Int
4 String
"};",
      Int -> String -> String
indent Int
4 String
"Poll::Ready(Some(item))",
      Int -> String -> String
indent Int
3 String
"}",
      Int -> String -> String
indent Int
3 String
"Poll::Ready(None) => Poll::Ready(None),",
      Int -> String -> String
indent Int
3 String
"Poll::Pending => Poll::Pending,",
      Int -> String -> String
indent Int
2 String
"}",
      Int -> String -> String
indent Int
1 String
"}",
      String
"}"
    ]

generateFromConvexValueBoilerplate :: String
generateFromConvexValueBoilerplate :: String
generateFromConvexValueBoilerplate =
  [String] -> String
unlines
    [ String
"pub trait FromConvexValue: Sized {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError>;",
      String
"}",
      String
"",
      String
"impl FromConvexValue for bool {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Boolean(b) => Ok(b),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected bool\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for i64 {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Int64(i) => Ok(i),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected i64\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for i32 {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Int64(i) => i",
      String
"                .try_into()",
      String
"                .map_err(|_| ApiError::ConvexClientError(\"i64 out of range for i32\".into())),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected i64\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for f64 {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Float64(f) => Ok(f),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected f64\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for f32 {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Float64(f) => Ok(f as f32),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected f64\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl<T> FromConvexValue for Vec<T>",
      String
"where",
      String
"    T: FromConvexValue,",
      String
"{",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Array(arr) => arr",
      String
"                .into_iter()",
      String
"                .map(T::from_convex)",
      String
"                .collect::<Result<Vec<T>, ApiError>>(),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected array\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl<T> FromConvexValue for Option<T>",
      String
"where",
      String
"    T: FromConvexValue,",
      String
"{",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Null => Ok(None),",
      String
"            other => T::from_convex(other).map(Some),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for Vec<u8> {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::Bytes(bytes) => Ok(bytes),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected bytes\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for String {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        match value {",
      String
"            Value::String(s) => Ok(s),",
      String
"            _ => Err(ApiError::ConvexClientError(\"Expected string\".into())),",
      String
"        }",
      String
"    }",
      String
"}",
      String
"",
      String
"impl FromConvexValue for serde_json::Value {",
      String
"    fn from_convex(value: Value) -> Result<Self, ApiError> {",
      String
"        Ok(value.into())",
      String
"    }",
      String
"}",
      String
""
    ]

generateApiClass :: [Action.ConvexFunction] -> (String, [String])
generateApiClass :: [ConvexFunction] -> (String, [String])
generateApiClass [ConvexFunction]
funcs =
  let tree :: PathTree
tree = [ConvexFunction] -> PathTree
buildPathTree [ConvexFunction]
funcs
      (String
structDefs, String
implDef, [String]
nested) = String -> PathTree -> (String, String, [String])
generateApiStructure String
"Api" PathTree
tree
   in ( [String] -> String
unlines
          [ String
"pub struct Api {",
            Int -> String -> String
indent Int
1 String
"pub client: ConvexClient,",
            String
"}",
            String
structDefs,
            String
implDef
          ],
        [String]
nested
      )

generateApiStructure :: String -> PathTree -> (String, String, [String])
generateApiStructure :: String -> PathTree -> (String, String, [String])
generateApiStructure String
parentName (DirNode Map String PathTree
dirMap) =
  let ([String]
structs, [String]
impls, [[String]]
nested) = [(String, String, [String])] -> ([String], [String], [[String]])
forall a b c. [(a, b, c)] -> ([a], [b], [c])
unzip3 ([(String, String, [String])] -> ([String], [String], [[String]]))
-> [(String, String, [String])] -> ([String], [String], [[String]])
forall a b. (a -> b) -> a -> b
$ ((String, PathTree) -> (String, String, [String]))
-> [(String, PathTree)] -> [(String, String, [String])]
forall a b. (a -> b) -> [a] -> [b]
map ((String -> PathTree -> (String, String, [String]))
-> (String, PathTree) -> (String, String, [String])
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry String -> PathTree -> (String, String, [String])
processEntry) (Map String PathTree -> [(String, PathTree)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String PathTree
dirMap)
      ([(String, PathTree)]
accessors, [(String, PathTree)]
functions) = [(String, PathTree)]
-> ([(String, PathTree)], [(String, PathTree)])
forall {a}. [(a, PathTree)] -> ([(a, PathTree)], [(a, PathTree)])
partitionEntries (Map String PathTree -> [(String, PathTree)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String PathTree
dirMap)
   in ( [String] -> String
unlines [String]
structs,
        [String] -> String
unlines
          [ String
"impl" String -> String -> String
forall a. [a] -> [a] -> [a]
++ (if String
parentName String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"Api" then String
"" else String
"<'a>") String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
parentName String -> String -> String
forall a. [a] -> [a] -> [a]
++ (if String
parentName String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"Api" then String
"" else String
"<'a>") String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
            if String
parentName String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"Api" then Int -> String -> String
indent Int
1 String
"pub fn new(client: ConvexClient) -> Self {\n        Self { client }\n    }" else String
"",
            [String] -> String
unlines (((String, PathTree) -> String) -> [(String, PathTree)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, PathTree) -> String
forall {b}. (String, b) -> String
generateAccessorMethod [(String, PathTree)]
accessors),
            [String] -> String
unlines (((String, PathTree) -> [String])
-> [(String, PathTree)] -> [String]
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (String, PathTree) -> [String]
forall {a}. (a, PathTree) -> [String]
generateMethodsForEntry [(String, PathTree)]
functions),
            String
"}"
          ]
          String -> String -> String
forall a. [a] -> [a] -> [a]
++ [String] -> String
unlines [String]
impls,
        [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
nested
      )
  where
    processEntry :: String -> PathTree -> (String, String, [String])
processEntry String
name (DirNode Map String PathTree
subDir) =
      let structName :: String
structName = String -> String
toPascalCase String
name
          (String
subStructs, String
subImpls, [String]
nestedFromSub) = String -> PathTree -> (String, String, [String])
generateApiStructure String
structName (Map String PathTree -> PathTree
DirNode Map String PathTree
subDir)
          structDef :: String
structDef =
            [String] -> String
unlines
              [ String
"pub struct " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"<'a> {",
                Int -> String -> String
indent Int
1 String
"client: &'a mut ConvexClient,",
                String
"}"
              ]
       in ([String] -> String
unlines [String
structDef, String
subStructs], String
subImpls, [String]
nestedFromSub)
    processEntry String
_ (FuncNode ConvexFunction
func) =
      let (String
_, [String]
nestedFromFunc) = ConvexFunction -> (String, [String])
generateFunction ConvexFunction
func
          (String
_, [String]
nestedFromSub) = if ConvexFunction -> FuncType
Action.funcType ConvexFunction
func FuncType -> FuncType -> Bool
forall a. Eq a => a -> a -> Bool
== FuncType
Action.Query then ConvexFunction -> (String, [String])
generateSubscriptionFunction ConvexFunction
func else (String
"", [])
       in (String
"", String
"", [String]
nestedFromFunc [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
nestedFromSub)

    partitionEntries :: [(a, PathTree)] -> ([(a, PathTree)], [(a, PathTree)])
partitionEntries =
      (([(a, PathTree)], [(a, PathTree)])
 -> (a, PathTree) -> ([(a, PathTree)], [(a, PathTree)]))
-> ([(a, PathTree)], [(a, PathTree)])
-> [(a, PathTree)]
-> ([(a, PathTree)], [(a, PathTree)])
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl
        ( \([(a, PathTree)]
ds, [(a, PathTree)]
fs) (a
name, PathTree
node) -> case PathTree
node of
            DirNode Map String PathTree
_ -> ((a
name, PathTree
node) (a, PathTree) -> [(a, PathTree)] -> [(a, PathTree)]
forall a. a -> [a] -> [a]
: [(a, PathTree)]
ds, [(a, PathTree)]
fs)
            FuncNode ConvexFunction
_ -> ([(a, PathTree)]
ds, (a
name, PathTree
node) (a, PathTree) -> [(a, PathTree)] -> [(a, PathTree)]
forall a. a -> [a] -> [a]
: [(a, PathTree)]
fs)
        )
        ([], [])

    generateAccessorMethod :: (String, b) -> String
generateAccessorMethod (String
name, b
_) =
      let structName :: String
structName = String -> String
toPascalCase String
name
          methodName :: String
methodName = String -> String
toSnakeCase String
name
       in [String] -> String
unlines
            [ Int -> String -> String
indent Int
1 (String
"pub fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
methodName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&mut self) -> " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"<'_> {"),
              Int -> String -> String
indent Int
2 (String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" { client: &mut self.client }"),
              Int -> String -> String
indent Int
1 String
"}"
            ]

    generateMethodsForEntry :: (a, PathTree) -> [String]
generateMethodsForEntry (a
_, FuncNode ConvexFunction
func) =
      let (String
queryDef, [String]
_) = ConvexFunction -> (String, [String])
generateFunction ConvexFunction
func
          (String
subDef, [String]
_) =
            if ConvexFunction -> FuncType
Action.funcType ConvexFunction
func FuncType -> FuncType -> Bool
forall a. Eq a => a -> a -> Bool
== FuncType
Action.Query
              then ConvexFunction -> (String, [String])
generateSubscriptionFunction ConvexFunction
func
              else (String
"", [])
       in [String
queryDef, String
subDef]
    generateMethodsForEntry (a, PathTree)
_ = []
generateApiStructure String
_ PathTree
_ = (String
"", String
"", [])

generateFunction :: Action.ConvexFunction -> (String, [String])
generateFunction :: ConvexFunction -> (String, [String])
generateFunction ConvexFunction
func =
  let funcName :: String
funcName = ConvexFunction -> String
Action.funcName ConvexFunction
func
      args :: [(String, ConvexType)]
args = ConvexFunction -> [(String, ConvexType)]
Action.funcArgs ConvexFunction
func
      fullFuncPath :: String
fullFuncPath = ConvexFunction -> String
Action.funcPath ConvexFunction
func String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
funcName
      (String
argSignature, [String]
nestedFromArgs) = String -> [(String, ConvexType)] -> (String, [String])
generateArgSignatureStruct String
fullFuncPath [(String, ConvexType)]
args
      funcNameSnake :: String
funcNameSnake = String -> String
toSnakeCase String
funcName
      (String
returnHint, Bool
isNullable, [String]
nestedFromReturn) = String -> ConvexType -> (String, Bool, [String])
getReturnType String
funcName (ConvexFunction -> ConvexType
Action.funcReturn ConvexFunction
func)
      handlerCall :: String
handlerCall = case ConvexFunction -> FuncType
Action.funcType ConvexFunction
func of
        FuncType
Action.Query -> String
"query"
        FuncType
Action.Mutation -> String
"mutation"
        FuncType
Action.Action -> String
"action"
      btreemapConstruction :: String
btreemapConstruction = [(String, ConvexType)] -> String
generateBTreeMap (ConvexFunction -> [(String, ConvexType)]
Action.funcArgs ConvexFunction
func)
      returnHandling :: String
returnHandling = String -> Bool -> String
generateReturnHandling String
returnHint Bool
isNullable
      funcCode :: String
funcCode = case [(String, ConvexType)]
args of
        [] ->
          [String] -> String
unlines
            [ Int -> String -> String
indent Int
1 (String
"/// Wraps the `" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` " String -> String -> String
forall a. [a] -> [a] -> [a]
++ FuncType -> String
forall a. Show a => a -> String
show (ConvexFunction -> FuncType
Action.funcType ConvexFunction
func) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"."),
              Int -> String -> String
indent Int
1 (String
"pub async fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&mut self) -> Result<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
returnHint String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
", ApiError> {"),
              String
btreemapConstruction,
              Int -> String -> String
indent Int
2 (String
"let result = self.client." String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
handlerCall String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\", btmap).await?;"),
              String
returnHandling,
              Int -> String -> String
indent Int
1 String
"}"
            ]
        [(String, ConvexType)]
_ ->
          [String] -> String
unlines
            [ Int -> String -> String
indent Int
1 (String
"/// Wraps the `" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` " String -> String -> String
forall a. [a] -> [a] -> [a]
++ FuncType -> String
forall a. Show a => a -> String
show (ConvexFunction -> FuncType
Action.funcType ConvexFunction
func) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"."),
              Int -> String -> String
indent Int
1 (String
"pub async fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&mut self, arg: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
argSignature String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") -> Result<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
returnHint String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
", ApiError> {"),
              String
btreemapConstruction,
              Int -> String -> String
indent Int
2 (String
"let result = self.client." String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
handlerCall String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\", btmap).await?;"),
              String
returnHandling,
              Int -> String -> String
indent Int
1 String
"}"
            ]
   in (String
funcCode, [String]
nestedFromArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
nestedFromReturn)

generateSubscriptionFunction :: Action.ConvexFunction -> (String, [String])
generateSubscriptionFunction :: ConvexFunction -> (String, [String])
generateSubscriptionFunction ConvexFunction
func =
  let funcName :: String
funcName = ConvexFunction -> String
Action.funcName ConvexFunction
func
      args :: [(String, ConvexType)]
args = ConvexFunction -> [(String, ConvexType)]
Action.funcArgs ConvexFunction
func
      (String
argSignature, [String]
nestedFromArgs) = String -> [(String, ConvexType)] -> (String, [String])
generateArgSignatureStruct String
fullFuncPath [(String, ConvexType)]
args
      funcNameSnake :: String
funcNameSnake = String
"subscribe_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toSnakeCase String
funcName
      (String
returnHint, Bool
_, [String]
nestedFromReturn) = String -> ConvexType -> (String, Bool, [String])
getReturnType String
funcName (ConvexFunction -> ConvexType
Action.funcReturn ConvexFunction
func)
      fullFuncPath :: String
fullFuncPath = ConvexFunction -> String
Action.funcPath ConvexFunction
func String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
":" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
funcName
      btreemapConstruction :: String
btreemapConstruction = [(String, ConvexType)] -> String
generateBTreeMap (ConvexFunction -> [(String, ConvexType)]
Action.funcArgs ConvexFunction
func)
      funcCode :: String
funcCode = case [(String, ConvexType)]
args of
        [] ->
          [String] -> String
unlines
            [ Int -> String -> String
indent Int
1 (String
"/// Subscribes to the `" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` query."),
              Int -> String -> String
indent Int
1 (String
"pub async fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&mut self) -> Result<TypedSubscription<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
returnHint String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">, ApiError> {"),
              String
btreemapConstruction,
              Int -> String -> String
indent Int
2 (String
"let raw_subscription = self.client.subscribe(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\", btmap).await?;"),
              Int -> String -> String
indent Int
2 String
"Ok(TypedSubscription {",
              Int -> String -> String
indent Int
3 String
"raw_subscription,",
              Int -> String -> String
indent Int
3 String
"_phantom: PhantomData,",
              Int -> String -> String
indent Int
2 String
"})",
              Int -> String -> String
indent Int
1 String
"}"
            ]
        [(String, ConvexType)]
_ ->
          [String] -> String
unlines
            [ Int -> String -> String
indent Int
1 (String
"/// Subscribes to the `" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` query."),
              Int -> String -> String
indent Int
1 (String
"pub async fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
funcNameSnake String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&mut self, arg: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
argSignature String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") -> Result<TypedSubscription<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
returnHint String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">, ApiError> {"),
              String
btreemapConstruction,
              Int -> String -> String
indent Int
2 (String
"let raw_subscription = self.client.subscribe(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fullFuncPath String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\", btmap).await?;"),
              Int -> String -> String
indent Int
2 String
"Ok(TypedSubscription {",
              Int -> String -> String
indent Int
3 String
"raw_subscription,",
              Int -> String -> String
indent Int
3 String
"_phantom: PhantomData,",
              Int -> String -> String
indent Int
2 String
"})",
              Int -> String -> String
indent Int
1 String
"}"
            ]
   in (String
funcCode, [String]
nestedFromArgs [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
nestedFromReturn)

generateTypesModule :: P.ParsedProject -> [String] -> String
generateTypesModule :: ParsedProject -> [String] -> String
generateTypesModule ParsedProject
project [String]
nestedFromFuncs =
  let (String
tableCode, [String]
nestedFromTables) = Schema -> (String, [String])
generateAllTables (ParsedProject -> Schema
P.ppSchema ParsedProject
project)
      (String
constantsCode, [String]
nestedFromConstants) = Map String ConvexType -> (String, [String])
generateAllConstants (ParsedProject -> Map String ConvexType
P.ppConstants ParsedProject
project)
      allNestedCode :: [String]
allNestedCode = [String] -> [String]
forall a. Eq a => [a] -> [a]
nub ([String]
nestedFromTables [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
nestedFromConstants [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
nestedFromFuncs)
   in [String] -> String
unlines
        [ String
"pub mod types {",
          Int -> String -> String
indent Int
1 String
"use super::*;",
          String
"",
          String
tableCode,
          String
constantsCode,
          [String] -> String
unlines [String]
allNestedCode,
          String
"}"
        ]

generateAllTables :: Schema.Schema -> (String, [String])
generateAllTables :: Schema -> (String, [String])
generateAllTables (Schema.Schema [Table]
tables) =
  let ([String]
tableCodes, [[String]]
nested) = [(String, [String])] -> ([String], [[String]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(String, [String])] -> ([String], [[String]]))
-> [(String, [String])] -> ([String], [[String]])
forall a b. (a -> b) -> a -> b
$ (Table -> (String, [String])) -> [Table] -> [(String, [String])]
forall a b. (a -> b) -> [a] -> [b]
map Table -> (String, [String])
generateTableStruct [Table]
tables
   in ([String] -> String
unlines [String]
tableCodes, [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
nested)

generateTableStruct :: Schema.Table -> (String, [String])
generateTableStruct :: Table -> (String, [String])
generateTableStruct Table
table =
  let className :: String
className = String -> String
toPascalCase (Table -> String
Schema.tableName Table
table) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"Doc"
      ([String]
fieldLines, [[String]]
nestedFromFields) = [(String, [String])] -> ([String], [[String]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(String, [String])] -> ([String], [[String]]))
-> [(String, [String])] -> ([String], [[String]])
forall a b. (a -> b) -> a -> b
$ (Field -> (String, [String])) -> [Field] -> [(String, [String])]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Field -> (String, [String])
generateField String
className) (Table -> [Field]
Schema.tableFields Table
table)
      allFields :: [(String, ConvexType)]
allFields =
        [ (String
"_id", String -> ConvexType
Schema.VId (String -> String
toPascalCase (Table -> String
Schema.tableName Table
table))),
          (String
"_creation_time", ConvexType
Schema.VFloat64)
        ]
          [(String, ConvexType)]
-> [(String, ConvexType)] -> [(String, ConvexType)]
forall a. [a] -> [a] -> [a]
++ (Field -> (String, ConvexType))
-> [Field] -> [(String, ConvexType)]
forall a b. (a -> b) -> [a] -> [b]
map (\Field
f -> (Field -> String
Schema.fieldName Field
f, Field -> ConvexType
Schema.fieldType Field
f)) (Table -> [Field]
Schema.tableFields Table
table)
      fromBlock :: String
fromBlock = String -> [(String, ConvexType)] -> String
generateFromConvexValueImpl (String
className) [(String, ConvexType)]
allFields
      toBlock :: String
toBlock = String -> [(String, ConvexType)] -> String
generateToConvexValueImpl (String
className) [(String, ConvexType)]
allFields
   in ( [String] -> String
unlines
          [ String
"#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq)]",
            (String
"pub struct " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
className String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {"),
            Int -> String -> String
indent Int
1 String
"#[serde(default)]",
            Int -> String -> String
indent Int
1 (String
"pub _id: Id<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
className String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">,"),
            Int -> String -> String
indent Int
1 String
"#[serde(default)]",
            Int -> String -> String
indent Int
1 String
"#[serde(rename = \"_creationTime\")]",
            Int -> String -> String
indent Int
1 String
"pub _creation_time: f64,",
            [String] -> String
unlines [String]
fieldLines,
            String
"}",
            String
"",
            String
fromBlock,
            String
"",
            String
toBlock
          ],
        [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
nestedFromFields
      )

generateAllConstants :: Map.Map String Schema.ConvexType -> (String, [String])
generateAllConstants :: Map String ConvexType -> (String, [String])
generateAllConstants Map String ConvexType
constants =
  let ([String]
constCodes, [[String]]
nested) = [(String, [String])] -> ([String], [[String]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(String, [String])] -> ([String], [[String]]))
-> [(String, [String])] -> ([String], [[String]])
forall a b. (a -> b) -> a -> b
$ ((String, ConvexType) -> (String, [String]))
-> [(String, ConvexType)] -> [(String, [String])]
forall a b. (a -> b) -> [a] -> [b]
map ((String -> ConvexType -> (String, [String]))
-> (String, ConvexType) -> (String, [String])
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry String -> ConvexType -> (String, [String])
generateConstant) (Map String ConvexType -> [(String, ConvexType)]
forall k a. Map k a -> [(k, a)]
Map.toList Map String ConvexType
constants)
   in ([String] -> String
unlines [String]
constCodes, [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
nested)

generateConstant :: String -> Schema.ConvexType -> (String, [String])
generateConstant :: String -> ConvexType -> (String, [String])
generateConstant String
name u :: ConvexType
u@(Schema.VUnion [ConvexType]
literals)
  | (ConvexType -> Bool) -> [ConvexType] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ConvexType -> Bool
Schema.isLiteral [ConvexType]
literals =
      let enumName :: String
enumName = String -> String
toPascalCase String
name
          enumFromConvexValueImpl :: String
enumFromConvexValueImpl = String -> [ConvexType] -> String
generateFromConvexValueImplEnum (String
"types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
enumName) [ConvexType]
literals
          variantNames :: [(String, String)]
variantNames = (ConvexType -> (String, String))
-> [ConvexType] -> [(String, String)]
forall a b. (a -> b) -> [a] -> [b]
map (\ConvexType
l -> let n :: String
n = ConvexType -> String
Schema.getLiteralString ConvexType
l in (String -> String
Schema.sanitizeUnionValues String
n, String
n)) [ConvexType]
literals
          buildVariantLines :: [(String, String)] -> [String]
buildVariantLines [] = []
          buildVariantLines ((String
sanitizedFirst, String
originalFirst) : [(String, String)]
rest) =
            (Int -> String -> String
indent Int
2 String
"#[default]\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 (String
"#[serde(rename = \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
originalFirst String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\")]\n") String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 ((String -> String
toPascalCase String
sanitizedFirst) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
","))
              String -> [String] -> [String]
forall a. a -> [a] -> [a]
: ((String, String) -> String) -> [(String, String)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
sanitizedV, String
originalV) -> Int -> String -> String
indent Int
2 (String
"#[serde(rename = \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
originalV String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\")]\n") String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 (String -> String
toPascalCase String
sanitizedV String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
",")) [(String, String)]
rest
          code :: String
code =
            [String] -> String
unlines
              [ Int -> String -> String
indent Int
1 String
"#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]",
                Int -> String -> String
indent Int
1 (String
"pub enum " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
enumName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {"),
                [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ [(String, String)] -> [String]
buildVariantLines [(String, String)]
variantNames,
                Int -> String -> String
indent Int
1 String
"}",
                String
"",
                String
enumFromConvexValueImpl,
                String
""
              ]
       in (String
code, [])
  | Bool
otherwise =
      let (String
rustTypeName, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType String
name ConvexType
u
       in (Int -> String -> String
indent Int
1 (String
"pub type " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toPascalCase String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
rustTypeName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";"), [String]
nested)
generateConstant String
name ConvexType
t =
  let (String
rustTypeName, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType String
name ConvexType
t
   in (Int -> String -> String
indent Int
1 (String
"pub type " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toPascalCase String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
rustTypeName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";"), [String]
nested)

generateField :: String -> Schema.Field -> (String, [String])
generateField :: String -> Field -> (String, [String])
generateField String
nameHint Field
field =
  let fieldNameSnake :: String
fieldNameSnake = String -> String
toSnakeCase (Field -> String
Schema.fieldName Field
field)
      (String
rustType, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType (String
nameHint String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
capitalize (Field -> String
Schema.fieldName Field
field)) (Field -> ConvexType
Schema.fieldType Field
field)
      serdeRename :: String
serdeRename = if String
fieldNameSnake String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= Field -> String
Schema.fieldName Field
field then Int -> String -> String
indent Int
2 (String
"#[serde(rename = \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Field -> String
Schema.fieldName Field
field String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\")]\n") else String
""
      serdeAttrs :: String
serdeAttrs =
        let defaultAttr :: String
defaultAttr = String
"default"
            skipAttr :: Maybe String
skipAttr =
              if ConvexType -> Bool
needsOptionalWrapper (Field -> ConvexType
Schema.fieldType Field
field)
                then String -> Maybe String
forall a. a -> Maybe a
Just String
"skip_serializing_if = \"Option::is_none\""
                else Maybe String
forall a. Maybe a
Nothing
            allAttrs :: [String]
allAttrs = case Maybe String
skipAttr of
              Just String
s -> [String
defaultAttr, String
s]
              Maybe String
Nothing -> [String
defaultAttr]
         in Int -> String -> String
indent Int
2 (String
"#[serde(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " [String]
allAttrs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")]")
      fieldLine :: String
fieldLine = String
serdeRename String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
serdeAttrs String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 (String
"pub " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldNameSnake String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
rustType String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
",")
   in (String
fieldLine, [String]
nested)

generateArgSignatureStruct :: String -> [(String, Schema.ConvexType)] -> (String, [String])
generateArgSignatureStruct :: String -> [(String, ConvexType)] -> (String, [String])
generateArgSignatureStruct String
_ [] = (String
"", [])
generateArgSignatureStruct String
fullFuncName [(String, ConvexType)]
args =
  let argStructName :: String
argStructName = String -> String
toPascalCase (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> if (Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/' Bool -> Bool -> Bool
|| Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
':') then Char
'_' else Char
c) (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
fullFuncName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"Arg"
      (String
argCode, [String]
subArgs) = String -> ConvexType -> (String, [String])
generateConstant String
argStructName (ConvexType -> (String, [String]))
-> ConvexType -> (String, [String])
forall a b. (a -> b) -> a -> b
$ [(String, ConvexType)] -> ConvexType
Schema.VObject [(String, ConvexType)]
args
   in (String
"types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
argStructName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"Object", String
argCode String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String]
subArgs)

generateBTreeMap :: [(String, Schema.ConvexType)] -> String
generateBTreeMap :: [(String, ConvexType)] -> String
generateBTreeMap [] = Int -> String -> String
indent Int
2 String
"let btmap = BTreeMap::new();"
generateBTreeMap [(String, ConvexType)]
btmap =
  let buildStmts :: (String, ConvexType) -> String
buildStmts (String
name, ConvexType
convexType) =
        let varName :: String
varName = String
"arg." String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toSnakeCase String
name
         in case ConvexType
convexType of
              Schema.VObject [(String, ConvexType)]
_ -> Int -> String -> String
indent Int
1 (String
"btmap.insert(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\".to_string(), " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_convex_value()?);")
              Schema.VOptional ConvexType
innerConvexType -> Int -> String -> String
indent Int
1 (String
"if let Some(v) = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" { btmap.insert(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\".to_string(), " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String, ConvexType) -> String
fieldToConvexValue (String
"v", ConvexType
innerConvexType) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"); }")
              ConvexType
_ -> Int -> String -> String
indent Int
1 (String
"btmap.insert(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\".to_string(), " String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String, ConvexType) -> String
fieldToConvexValue (String
varName, ConvexType
convexType) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
");")
   in [String] -> String
unlines
        [ Int -> String -> String
indent Int
2 String
"let mut btmap = BTreeMap::new();",
          [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ ((String, ConvexType) -> String)
-> [(String, ConvexType)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, ConvexType) -> String
buildStmts [(String, ConvexType)]
btmap
        ]

fieldToConvexValue :: (String, Schema.ConvexType) -> String
fieldToConvexValue :: (String, ConvexType) -> String
fieldToConvexValue (String
fieldName, ConvexType
t) =
  let fieldNameSnake :: String
fieldNameSnake = String -> String
toSnakeCase String
fieldName
      valueExpr :: String
valueExpr = String -> ConvexType -> String
innerValueToConvexNonOptional String
fieldNameSnake ConvexType
t
   in String
valueExpr

toClonedValue :: String -> Schema.ConvexType -> String
toClonedValue :: String -> ConvexType -> String
toClonedValue String
varName (ConvexType
Schema.VString) = String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_string()"
toClonedValue String
varName ConvexType
t
  | ConvexType -> Bool
isPassedByCopy ConvexType
t = String
"*" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName
  | Bool
otherwise = String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".clone()"

isPassedByCopy :: Schema.ConvexType -> Bool
isPassedByCopy :: ConvexType -> Bool
isPassedByCopy ConvexType
Schema.VNumber = Bool
True
isPassedByCopy ConvexType
Schema.VInt64 = Bool
True
isPassedByCopy ConvexType
Schema.VFloat64 = Bool
True
isPassedByCopy ConvexType
Schema.VBoolean = Bool
True
isPassedByCopy ConvexType
_ = Bool
False

getReturnType :: String -> Schema.ConvexType -> (String, Bool, [String])
getReturnType :: String -> ConvexType -> (String, Bool, [String])
getReturnType String
funcName ConvexType
rt =
  let (String
baseType, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType (String
funcName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"Return") ConvexType
rt
      isNullable :: Bool
isNullable = ConvexType -> Bool
needsOptionalWrapper ConvexType
rt
   in if String
baseType String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"()"
        then (String
"()", Bool
False, [String]
nested)
        else (String
baseType, Bool
isNullable, [String]
nested)

generateReturnHandling :: String -> Bool -> String
generateReturnHandling :: String -> Bool -> String
generateReturnHandling String
"()" Bool
_ =
  [String] -> String
unlines
    [ Int -> String -> String
indent Int
2 String
"match result {",
      Int -> String -> String
indent Int
3 String
"FunctionResult::Value(_) => Ok(()),",
      Int -> String -> String
indent Int
3 String
"FunctionResult::ErrorMessage(s) => Err(ApiError::ConvexFunctionError(s)),",
      Int -> String -> String
indent Int
3 String
"FunctionResult::ConvexError(err) => Err(ApiError::ConvexClientError(err.to_string())),",
      Int -> String -> String
indent Int
2 String
"}"
    ]
generateReturnHandling String
_ Bool
isNullable =
  if Bool
isNullable
    then
      [String] -> String
unlines
        [ Int -> String -> String
indent Int
2 String
"match result {",
          Int -> String -> String
indent Int
3 String
"FunctionResult::Value(val) => Ok(FromConvexValue::from_convex(val.clone())?),",
          Int -> String -> String
indent Int
3 String
"FunctionResult::ErrorMessage(s) => Err(ApiError::ConvexFunctionError(s)),",
          Int -> String -> String
indent Int
3 String
"FunctionResult::ConvexError(err) => Err(ApiError::ConvexClientError(err.to_string())),",
          Int -> String -> String
indent Int
2 String
"}"
        ]
    else
      [String] -> String
unlines
        [ Int -> String -> String
indent Int
2 String
"match result {",
          Int -> String -> String
indent Int
3 String
"FunctionResult::Value(Value::Null) => Err(ApiError::UnexpectedNullError),",
          Int -> String -> String
indent Int
3 String
"FunctionResult::Value(val) => Ok(FromConvexValue::from_convex(val.clone())?),",
          Int -> String -> String
indent Int
3 String
"FunctionResult::ErrorMessage(s) => Err(ApiError::ConvexFunctionError(s)),",
          Int -> String -> String
indent Int
3 String
"FunctionResult::ConvexError(err) => Err(ApiError::ConvexClientError(err.to_string())),",
          Int -> String -> String
indent Int
2 String
"}"
        ]

generateToConvexValueImpl :: String -> [(String, Schema.ConvexType)] -> String
generateToConvexValueImpl :: String -> [(String, ConvexType)] -> String
generateToConvexValueImpl String
structName [(String, ConvexType)]
fields =
  let buildMapInserts :: (String, ConvexType) -> String
buildMapInserts (String
fieldName, ConvexType
fieldType) =
        let conversionBlock :: String
conversionBlock = (String, ConvexType) -> String
generateFieldToConvexValue (String
fieldName, ConvexType
fieldType)
         in Int -> String -> String
indent Int
3 String
conversionBlock
      mapInserts :: String
mapInserts = [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ ((String, ConvexType) -> String)
-> [(String, ConvexType)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, ConvexType) -> String
buildMapInserts [(String, ConvexType)]
fields
   in [String] -> String
unlines
        [ String
"impl " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
          Int -> String -> String
indent Int
1 String
"pub fn to_convex_value(&self) -> Result<Value, ApiError> {",
          Int -> String -> String
indent Int
2 String
"let mut btmap = BTreeMap::new();",
          String
mapInserts,
          Int -> String -> String
indent Int
2 String
"Ok(Value::Object(btmap))",
          Int -> String -> String
indent Int
1 String
"}",
          String
"}"
        ]

generateFromConvexValueImplEnum :: String -> [Schema.ConvexType] -> String
generateFromConvexValueImplEnum :: String -> [ConvexType] -> String
generateFromConvexValueImplEnum String
structName [ConvexType]
fields =
  [String] -> String
unlines
    [ String
"impl TryFrom<Value> for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
      Int -> String -> String
indent Int
1 String
"type Error = ApiError;",
      Int -> String -> String
indent Int
1 String
"fn try_from(value: Value) -> Result<Self, Self::Error> {",
      Int -> String -> String
indent Int
2 String
"if let Value::String(s) = &value {",
      Int -> String -> String
indent Int
3 String
"return match s.as_str() {",
      [String] -> String
unlines ([String] -> String)
-> ([String] -> [String]) -> [String] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> String -> String
indent Int
4) ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ String -> [ConvexType] -> [String]
generateEnumMatchCases String
structName [ConvexType]
fields,
      Int -> String -> String
indent Int
2 String
"}",
      Int -> String -> String
indent Int
1 String
"}",
      Int -> String -> String
indent Int
2 String
"Err(ApiError::ConvexClientError(\"Expected a string for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\".to_string()))",
      Int -> String -> String
indent Int
1 String
"}",
      Int -> String -> String
indent Int
1 String
"}",
      String
"",
      Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"impl FromConvexValue for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
      Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"fn from_convex(value: Value) -> Result<Self, ApiError> {",
      Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"::try_from(value)",
      Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
      Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
      String
""
    ]

generateEnumMatchCases :: String -> [Schema.ConvexType] -> [String]
generateEnumMatchCases :: String -> [ConvexType] -> [String]
generateEnumMatchCases String
structName [ConvexType]
fields =
  let cases :: [String]
cases =
        (ConvexType -> String) -> [ConvexType] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map
          ( \case
              (Schema.VLiteral String
s) -> String
"\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\" => Ok(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> String
toPascalCase (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
Schema.sanitizeUnionValues (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
s) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"),"
              ConvexType
_ -> String -> String
forall a. HasCallStack => String -> a
error String
"Expected a literal for enum field"
          )
          [ConvexType]
fields
   in [String]
cases [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
"x => Err(ApiError::ConvexClientError(\"Expected one of " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
enumValues String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" but got \".to_string() + &x.to_string()))"]
  where
    enumValues :: String
enumValues = String
"[" String -> String -> String
forall a. [a] -> [a] -> [a]
++ (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
", " ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (ConvexType -> String) -> [ConvexType] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ConvexType -> String
Schema.getLiteralString [ConvexType]
fields) String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"

-- #[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
-- pub enum AccessLevelEnum {
--     #[default]
--     #[serde(rename = "read")]
--     Read,
--     #[serde(rename = "edit")]
--     Edit,
-- }
--
-- impl From<Value> for AccessLevelEnum {
--     fn from(value: Value) -> Self {
--         match value {
--             Value::String(s) => match s.as_str() {
--                 "read" => AccessLevelEnum::Read,
--                 "edit" => AccessLevelEnum::Edit,
--                 _ => panic!("Unknown AccessLevelEnum value"),
--             },
--             _ => panic!("Expected a string for AccessLevelEnum"),
--         }
--     }
-- }

generateFromConvexValueImpl :: String -> [(String, Schema.ConvexType)] -> String
generateFromConvexValueImpl :: String -> [(String, ConvexType)] -> String
generateFromConvexValueImpl String
structName [(String, ConvexType)]
fields =
  [String] -> String
unlines
    [ String
"impl TryFrom<Value> for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
      Int -> String -> String
indent Int
1 String
"type Error = ApiError;",
      Int -> String -> String
indent Int
1 String
"fn try_from(value: Value) -> Result<Self, Self::Error> {",
      Int -> String -> String
indent Int
2 String
"let obj = match value {",
      Int -> String -> String
indent Int
3 String
"Value::Object(map) => map,",
      Int -> String -> String
indent Int
3 String
"_ => return Err(ApiError::ConvexClientError(\"Expected object\".to_string())),",
      Int -> String -> String
indent Int
2 String
"};",
      [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ ((String, ConvexType) -> String)
-> [(String, ConvexType)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> String -> String
indent Int
2 (String -> String)
-> ((String, ConvexType) -> String)
-> (String, ConvexType)
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> (String, ConvexType) -> String
generateAccessorFnFromConvexValue String
structName) [(String, ConvexType)]
fields,
      [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (Int -> String -> String
indent Int
2) ([String] -> [String]) -> [String] -> [String]
forall a b. (a -> b) -> a -> b
$ String -> [(String, ConvexType)] -> [String]
generateFromConvexValueResult String
structName [(String, ConvexType)]
fields,
      Int -> String -> String
indent Int
1 String
"}",
      String
"}",
      String
"",
      Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"impl FromConvexValue for " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
      Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"fn from_convex(value: Value) -> Result<Self, ApiError> {",
      Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"::try_from(value)",
      Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
      Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
      String
""
    ]

generateFromConvexValueResult :: String -> [(String, Schema.ConvexType)] -> [String]
generateFromConvexValueResult :: String -> [(String, ConvexType)] -> [String]
generateFromConvexValueResult String
structName [(String, ConvexType)]
fields =
  let fieldNames :: [String]
fieldNames = ((String, ConvexType) -> String)
-> [(String, ConvexType)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String -> String
toSnakeCase (String -> String)
-> ((String, ConvexType) -> String)
-> (String, ConvexType)
-> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, ConvexType) -> String
forall a b. (a, b) -> a
fst) [(String, ConvexType)]
fields
      fieldAccessors :: [String]
fieldAccessors = (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\String
name -> String
"get_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
name) [String]
fieldNames
      setterStatements :: [String]
setterStatements =
        (String -> String -> String) -> [String] -> [String] -> [String]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith
          ( \String
name String
getter -> case String
name of
              String
"_creation_time" -> Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"_creation_time: " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
getter String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&obj, \"_creationTime\")?"
              String
n -> Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
": " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
getter String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(&obj, \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
n String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\")?"
          )
          [String]
fieldNames
          [String]
fieldAccessors
   in [ Int -> String -> String
indent Int
2 (String
"Ok(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {"),
        Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
intercalate String
",\n" ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ [String]
setterStatements,
        Int -> String -> String
indent Int
2 String
"})"
      ]

--        Ok(types::GetAssetsReturnObject {
--            _id: Id::new(get__id(&obj, "_id")?),
--            _creation_time: get__creation_time(&obj, "_creationTime")?,
--            project_id: Id::new(get_project_id(&obj, "project_id")?),
--            asset_name: get_asset_name(&obj, "asset_name")?,
--            asset_essence_mtime: get_asset_essence_mtime(&obj, "asset_essence_mtime")?,
--            link_metadata: get_link_metadata(&obj, "link_metadata")?,
--        })

generateAccessorFnFromConvexValue :: String -> (String, Schema.ConvexType) -> String
generateAccessorFnFromConvexValue :: String -> (String, ConvexType) -> String
generateAccessorFnFromConvexValue String
structName (String
fieldName, Schema.VOptional ConvexType
fieldType) =
  let fieldNameSnake :: String
fieldNameSnake = String -> String
toSnakeCase String
fieldName
      getterName :: String
getterName = String
"get_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldNameSnake
      (String
fieldTypeStr, [String]
_) = case String -> String -> Maybe String
forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix (String -> String
forall a. [a] -> [a]
reverse String
"Object") (String -> String
forall a. [a] -> [a]
reverse String
structName) of
        Just String
cleanStructName -> String -> ConvexType -> (String, [String])
toRustType (String -> String
forall a. [a] -> [a]
reverse String
cleanStructName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
capitalize String
fieldName) ConvexType
fieldType
        Maybe String
Nothing -> String -> ConvexType -> (String, [String])
toRustType (String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
capitalize String
fieldName) ConvexType
fieldType
   in [String] -> String
unlines
        [ Int -> String -> String
indent Int
0 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
getterName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(map: &BTreeMap<String, Value>, key: &str) -> Result<Option<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldTypeStr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">, ApiError> {",
          Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"match map.get(key) {",
          Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"Some(v) => {",
          Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"Ok(Some(FromConvexValue::from_convex(v.clone())?))",
          Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
          Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"_ => Ok(None),",
          Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
          Int -> String -> String
indent Int
0 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
          String
""
        ]
generateAccessorFnFromConvexValue String
structName (String
fieldName, ConvexType
fieldType) =
  let fieldNameSnake :: String
fieldNameSnake = String -> String
toSnakeCase String
fieldName
      getterName :: String
getterName = String
"get_" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldNameSnake
      (String
fieldTypeStr, [String]
_) = case String -> String -> Maybe String
forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix (String -> String
forall a. [a] -> [a]
reverse String
"Object") (String -> String
forall a. [a] -> [a]
reverse String
structName) of
        Just String
cleanStructName -> String -> ConvexType -> (String, [String])
toRustType (String -> String
forall a. [a] -> [a]
reverse String
cleanStructName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
capitalize String
fieldName) ConvexType
fieldType
        Maybe String
Nothing -> String -> ConvexType -> (String, [String])
toRustType (String
structName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
capitalize String
fieldName) ConvexType
fieldType
   in [String] -> String
unlines
        [ Int -> String -> String
indent Int
0 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"fn " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
getterName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"(map: &BTreeMap<String, Value>, key: &str) -> Result<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldTypeStr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
", ApiError> {",
          Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"match map.get(key) {",
          Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"Some(v) => {",
          Int -> String -> String
indent Int
3 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"Ok(FromConvexValue::from_convex(v.clone())?)",
          Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
          Int -> String -> String
indent Int
2 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"_ => return Err(ApiError::ConvexClientError(format!(\"Expected field (" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldTypeStr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
") '{}' not found\", key))),",
          Int -> String -> String
indent Int
1 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
          Int -> String -> String
indent Int
0 (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ String
"}",
          String
""
        ]

-- An example implementation:
--
-- pub struct GetAssetsReturnObject {
--     #[serde(default)]
--     pub _id: Id<types::AssetsDoc>,
--     #[serde(rename = "_creationTime")]
--     #[serde(default)]
--     pub _creation_time: f64,
--     #[serde(default)]
--     pub project_id: Id<types::ProjectsDoc>,
--     #[serde(default)]
--     pub asset_name: String,
--     #[serde(default)]
--     pub link_metadata: types::GetAssetsReturnLinkMetadataObject,
--     #[serde(default)]
--     pub asset_essence_mtime: i64,
-- }
--
-- impl TryFrom<Value> for types::GetAssetsReturnObject {
--    type Error = ApiError;
--
--    fn try_from(value: Value) -> Result<Self, Self::Error> {
--        let obj = match value {
--            Value::Object(map) => map,
--            _ => return Err(ApiError::ConvexClientError("Expected object".to_string())),
--        };
--
--        fn get__id(map: &BTreeMap<String, Value>, key: &str) -> Result<String, ApiError> {
--            match map.get(key) {
--                Some(Value::String(s)) => Ok(s.clone()),
--                _ => Err(ApiError::ConvexClientError(format!(
--                    "Expected string for field '{}'",
--                    key
--                ))),
--            }
--        }
--
--        fn get_project_id(map: &BTreeMap<String, Value>, key: &str) -> Result<String, ApiError> {
--            match map.get(key) {
--                Some(Value::String(s)) => Ok(s.clone()),
--                _ => Err(ApiError::ConvexClientError(format!(
--                    "Expected string for field '{}'",
--                    key
--                ))),
--            }
--        }
--
--        fn get__creation_time(map: &BTreeMap<String, Value>, key: &str) -> Result<f64, ApiError> {
--            match map.get(key) {
--                Some(Value::Float64(f)) => Ok(*f),
--                _ => Err(ApiError::ConvexClientError(format!(
--                    "Expected float64 for field '{}'",
--                    key
--                ))),
--            }
--        }
--
--        fn get_asset_essence_mtime(
--            map: &BTreeMap<String, Value>,
--            key: &str,
--        ) -> Result<i64, ApiError> {
--            match map.get(key) {
--                Some(Value::Int64(i)) => Ok(*i),
--                _ => Err(ApiError::ConvexClientError(format!(
--                    "Expected int64 for field '{}'",
--                    key
--                ))),
--            }
--        }
--
--        fn get_asset_name(map: &BTreeMap<String, Value>, key: &str) -> Result<String, ApiError> {
--            match map.get(key) {
--                Some(Value::String(s)) => Ok(s.clone()),
--                _ => Err(ApiError::ConvexClientError(format!(
--                    "Expected string for field '{}'",
--                    key
--                ))),
--            }
--        }
--
--        fn get_link_metadata(
--            map: &BTreeMap<String, Value>,
--            key: &str,
--        ) -> Result<types::GetAssetsReturnLinkMetadataObject, ApiError> {
--            match map.get(key) {
--                Some(Value::Object(inner)) => {
--                    let length = match inner.get("length") {
--                        Some(Value::Int64(i)) => *i,
--                        _ => {
--                            return Err(ApiError::ConvexClientError(
--                                "Expected int64 for 'length'".into(),
--                            ));
--                        }
--                    };
--                    let sample_rate = match inner.get("sample_rate") {
--                        Some(Value::Int64(i)) => *i,
--                        _ => {
--                            return Err(ApiError::ConvexClientError(
--                                "Expected int64 for 'sample_rate'".into(),
--                            ));
--                        }
--                    };
--                    let summary = match inner.get("summary") {
--                        Some(Value::Bytes(b)) => b.clone(),
--                        _ => {
--                            return Err(ApiError::ConvexClientError(
--                                "Expected bytes for 'summary'".into(),
--                            ));
--                        }
--                    };
--                    Ok(types::GetAssetsReturnLinkMetadataObject {
--                        length,
--                        sample_rate,
--                        summary,
--                    })
--                }
--                _ => Err(ApiError::ConvexClientError(
--                    "Expected object for 'link_metadata'".into(),
--                )),
--            }
--        }
--
--        Ok(types::GetAssetsReturnObject {
--            _id: Id::new(get__id(&obj, "_id")?),
--            _creation_time: get__creation_time(&obj, "_creationTime")?,
--            project_id: Id::new(get_project_id(&obj, "project_id")?),
--            asset_name: get_asset_name(&obj, "asset_name")?,
--            asset_essence_mtime: get_asset_essence_mtime(&obj, "asset_essence_mtime")?,
--            link_metadata: get_link_metadata(&obj, "link_metadata")?,
--        })
--    }
-- }

-- Some(Value::Array(v)) => Ok(Some(
--     v.iter()
--         .map(|item| item.clone().try_into())
--         .collect::<Result<Vec<_>, ApiError>>()?,
-- )),

generateFieldToConvexValue :: (String, Schema.ConvexType) -> String
generateFieldToConvexValue :: (String, ConvexType) -> String
generateFieldToConvexValue (String
fieldName, Schema.VOptional ConvexType
inner) =
  let fieldNameSnake :: String
fieldNameSnake = String -> String
toSnakeCase String
fieldName
      -- `v` is the unwrapped value from the Option.
      valueExpr :: String
valueExpr = String -> ConvexType -> String
innerValueToConvexOptional String
"v" ConvexType
inner
   in [String] -> String
unlines
        [ String
"if let Some(v) = &self." String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldNameSnake String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {",
          Int -> String -> String
indent Int
1 (String
"btmap.insert(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\".to_string(), " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
valueExpr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
");"),
          String
"}"
        ]
generateFieldToConvexValue (String
fieldName, ConvexType
fieldType) =
  let fieldNameSnake :: String
fieldNameSnake = String -> String
toSnakeCase String
fieldName
      valueExpr :: String
valueExpr = String -> ConvexType -> String
innerValueToConvexNonOptional (String
"self." String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldNameSnake) ConvexType
fieldType
   in String
"btmap.insert(\"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fieldName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\".to_string(), " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
valueExpr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
");"

-- | Generates the conversion for a non-optional inner value.
innerValueToConvexOptional :: String -> Schema.ConvexType -> String
innerValueToConvexOptional :: String -> ConvexType -> String
innerValueToConvexOptional String
varName (Schema.VArray ConvexType
inner) =
  let itemConversion :: String
itemConversion = String -> ConvexType -> String
innerValueToConvexArray String
"item" ConvexType
inner
      -- We can check `isFallible` by looking for `Value::try_from` or `?` in the conversion.
      isFallible :: Bool
isFallible = String
"?" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion Bool -> Bool -> Bool
|| String
"Value::try_from" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion Bool -> Bool -> Bool
|| String
"to_convex_value" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion
   in if Bool
isFallible
        then String
"Value::Array(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".iter().map(|item| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
itemConversion String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").collect::<Result<Vec<_>, _>>()?)"
        else String
"Value::Array(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".iter().map(|item| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
itemConversion String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").collect())"
innerValueToConvexOptional String
varName (Schema.VObject [(String, ConvexType)]
_) =
  String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_convex_value()"
innerValueToConvexOptional String
varName ConvexType
t
  | ConvexType -> Bool
isComplexForBTreeMap ConvexType
t = String
"Value::try_from(serde_json::to_value(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> ConvexType -> String
toClonedValue String
varName ConvexType
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").unwrap_or(\"unable to serialize\".into()))?"
  | Bool
otherwise = String
"Value::from(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> ConvexType -> String
toClonedValue String
varName ConvexType
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")"

innerValueToConvexArray :: String -> Schema.ConvexType -> String
innerValueToConvexArray :: String -> ConvexType -> String
innerValueToConvexArray String
varName (Schema.VArray ConvexType
inner) =
  let itemConversion :: String
itemConversion = String -> ConvexType -> String
innerValueToConvexArray String
"item" ConvexType
inner
      isFallible :: Bool
isFallible = String
"?" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion Bool -> Bool -> Bool
|| String
"Value::try_from" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion Bool -> Bool -> Bool
|| String
"to_convex_value" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion
   in if Bool
isFallible
        then String
"Value::Array(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".iter().map(|item| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
itemConversion String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").collect::<Result<Vec<_>, _>>()?)"
        else String
"Value::Array(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".iter().map(|item| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
itemConversion String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").collect())"
innerValueToConvexArray String
varName (Schema.VObject [(String, ConvexType)]
_) =
  String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_convex_value()"
innerValueToConvexArray String
varName ConvexType
t
  | ConvexType -> Bool
isComplexForBTreeMap ConvexType
t = String
"Value::try_from(serde_json::to_value(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> ConvexType -> String
toClonedValue String
varName ConvexType
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").unwrap_or(\"unable to serialize\".into()))"
  | Bool
otherwise = String
"Value::from(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> ConvexType -> String
toClonedValue String
varName ConvexType
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")"

innerValueToConvexNonOptional :: String -> Schema.ConvexType -> String
innerValueToConvexNonOptional :: String -> ConvexType -> String
innerValueToConvexNonOptional String
varName (Schema.VArray ConvexType
inner) =
  let itemConversion :: String
itemConversion = String -> ConvexType -> String
innerValueToConvexArray String
"item" ConvexType
inner
      isFallible :: Bool
isFallible = String
"?" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion Bool -> Bool -> Bool
|| String
"Value::try_from" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion Bool -> Bool -> Bool
|| String
"to_convex_value" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isInfixOf` String
itemConversion
   in if Bool
isFallible
        then String
"Value::Array(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".iter().map(|item| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
itemConversion String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").collect::<Result<Vec<_>, _>>()?)"
        else String
"Value::Array(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".iter().map(|item| " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
itemConversion String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").collect())"
innerValueToConvexNonOptional String
varName (ConvexType
Schema.VBytes) = String
"Value::from(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_vec())"
innerValueToConvexNonOptional String
varName (Schema.VObject [(String, ConvexType)]
_) = String
"Value::from(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_convex_value()?)"
innerValueToConvexNonOptional String
varName ConvexType
t
  | ConvexType -> Bool
isComplexForBTreeMap ConvexType
t = String
"Value::try_from(serde_json::to_value(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> ConvexType -> String
toClonedValue String
varName ConvexType
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
").unwrap_or(\"unable to serialize\".into()))?"
  | Bool
otherwise = String
"Value::from(" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> ConvexType -> String
toClonedValueNonOptional String
varName ConvexType
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
")"

toClonedValueNonOptional :: String -> Schema.ConvexType -> String
toClonedValueNonOptional :: String -> ConvexType -> String
toClonedValueNonOptional String
varName (ConvexType
Schema.VString) = String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".to_string()"
toClonedValueNonOptional String
varName ConvexType
t
  | ConvexType -> Bool
isPassedByCopy ConvexType
t = String
varName
  | Bool
otherwise = String
varName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
".clone()"

isComplexForBTreeMap :: Schema.ConvexType -> Bool
isComplexForBTreeMap :: ConvexType -> Bool
isComplexForBTreeMap (Schema.VUnion [ConvexType]
_) = Bool
True
isComplexForBTreeMap (Schema.VReference String
_) = Bool
True
isComplexForBTreeMap ConvexType
Schema.VAny = Bool
True
isComplexForBTreeMap ConvexType
_ = Bool
False

toRustType :: String -> Schema.ConvexType -> (String, [String])
toRustType :: String -> ConvexType -> (String, [String])
toRustType String
nameHint ConvexType
typ = case ConvexType
typ of
  ConvexType
Schema.VString -> (String
"String", [])
  ConvexType
Schema.VNumber -> (String
"f64", [])
  ConvexType
Schema.VInt64 -> (String
"i64", [])
  ConvexType
Schema.VFloat64 -> (String
"f64", [])
  ConvexType
Schema.VBoolean -> (String
"bool", [])
  ConvexType
Schema.VAny -> (String
"serde_json::Value", [])
  ConvexType
Schema.VNull -> (String
"()", [])
  Schema.VId String
t -> (String
"Id<types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toPascalCase String
t String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"Doc>", [])
  ConvexType
Schema.VBytes -> (String
"Vec<u8>", [])
  Schema.VArray ConvexType
inner ->
    let (String
innerType, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType String
nameHint ConvexType
inner
     in (String
"Vec<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
innerType String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">", [String]
nested)
  Schema.VOptional ConvexType
inner ->
    let (String
innerType, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType String
nameHint ConvexType
inner
     in (String
"Option<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
innerType String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">", [String]
nested)
  Schema.VObject [(String, ConvexType)]
fields ->
    let className :: String
className = String -> String
toPascalCase String
nameHint String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"Object"
        ([String]
fieldLines, [[String]]
nestedFields) = [(String, [String])] -> ([String], [[String]])
forall a b. [(a, b)] -> ([a], [b])
unzip ([(String, [String])] -> ([String], [[String]]))
-> [(String, [String])] -> ([String], [[String]])
forall a b. (a -> b) -> a -> b
$ (Field -> (String, [String])) -> [Field] -> [(String, [String])]
forall a b. (a -> b) -> [a] -> [b]
map (String -> Field -> (String, [String])
generateField String
nameHint) (((String, ConvexType) -> Field)
-> [(String, ConvexType)] -> [Field]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
n, ConvexType
t) -> String -> ConvexType -> Field
Schema.Field String
n ConvexType
t) [(String, ConvexType)]
fields)
        implBlock :: String
implBlock = String -> [(String, ConvexType)] -> String
generateToConvexValueImpl (String
className) [(String, ConvexType)]
fields
        fromBlock :: String
fromBlock = String -> [(String, ConvexType)] -> String
generateFromConvexValueImpl (String
className) [(String, ConvexType)]
fields
        newModel :: String
newModel =
          [String] -> String
unlines
            [ Int -> String -> String
indent Int
1 String
"#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq)]",
              Int -> String -> String
indent Int
1 (String
"pub struct " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
className String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {"),
              [String] -> String
unlines [String]
fieldLines,
              Int -> String -> String
indent Int
1 String
"}",
              String
"",
              String
implBlock,
              String
"",
              String
fromBlock
            ]
     in (String
"types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
className, [[String]] -> [String]
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat [[String]]
nestedFields [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String
newModel])
  Schema.VUnion [ConvexType]
types ->
    let nonNullTypes :: [ConvexType]
nonNullTypes = (ConvexType -> Bool) -> [ConvexType] -> [ConvexType]
forall a. (a -> Bool) -> [a] -> [a]
filter (ConvexType -> ConvexType -> Bool
forall a. Eq a => a -> a -> Bool
/= ConvexType
Schema.VNull) [ConvexType]
types
     in case [ConvexType]
nonNullTypes of
          [] -> (String
"Option<()>", [])
          [ConvexType
singleType] ->
            let (String
innerType, [String]
nested) = String -> ConvexType -> (String, [String])
toRustType String
nameHint ConvexType
singleType
             in (String
"Option<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
innerType String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">", [String]
nested)
          [ConvexType]
_ ->
            if (ConvexType -> Bool) -> [ConvexType] -> Bool
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
all ConvexType -> Bool
Schema.isLiteral [ConvexType]
nonNullTypes Bool -> Bool -> Bool
&& Bool -> Bool
not ([ConvexType] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ConvexType]
nonNullTypes)
              then
                let enumName :: String
enumName = String -> String
toPascalCase String
nameHint
                    variantNames :: [String]
variantNames = (ConvexType -> String) -> [ConvexType] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map ConvexType -> String
Schema.getLiteralString [ConvexType]
nonNullTypes
                    buildVariantLines :: [String] -> [String]
buildVariantLines [] = []
                    buildVariantLines (String
first : [String]
rest) =
                      (Int -> String -> String
indent Int
2 String
"#[default]\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 (String
"#[serde(rename = \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
first String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\")]\n") String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 (String -> String
toPascalCase String
first String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
","))
                        String -> [String] -> [String]
forall a. a -> [a] -> [a]
: (String -> String) -> [String] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\String
v -> Int -> String -> String
indent Int
2 (String
"#[serde(rename = \"" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
v String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\")]\n") String -> String -> String
forall a. [a] -> [a] -> [a]
++ Int -> String -> String
indent Int
2 (String -> String
toPascalCase String
v String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
",")) [String]
rest
                    fromBlock :: String
fromBlock = String -> [ConvexType] -> String
generateFromConvexValueImplEnum (String
"types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
enumName) [ConvexType]
nonNullTypes
                    newEnum :: String
newEnum =
                      [String] -> String
unlines
                        [ Int -> String -> String
indent Int
1 String
"#[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]",
                          Int -> String -> String
indent Int
1 (String
"pub enum " String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
enumName String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" {"),
                          [String] -> String
unlines ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ [String] -> [String]
buildVariantLines [String]
variantNames,
                          Int -> String -> String
indent Int
1 String
"}",
                          String
"",
                          String
fromBlock,
                          String
""
                        ]
                 in (String
"types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
enumName, [String
newEnum])
              else (String
"serde_json::Value", []) -- Fallback for complex unions
  Schema.VLiteral String
_ -> (String -> String
toPascalCase String
nameHint, [])
  Schema.VReference String
n -> (String
"types::" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toPascalCase String
n, [])
  ConvexType
Schema.VVoid -> (String
"()", [])

toRustBorrowType :: String -> String
toRustBorrowType :: String -> String
toRustBorrowType String
rustType
  | String
rustType String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"String" = String
"&str"
  | String
rustType String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"serde_json::Value" = String
"&serde_json::Value"
  | String
rustType String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
"Vec<u8>" = String
"&[u8]"
  | String
"Id<" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
rustType = String
"&" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
rustType
  | String
"Vec<" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
rustType = let inner :: String
inner = Int -> String -> String
forall a. Int -> [a] -> [a]
take (String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
rustType Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
5) (Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
4 String
rustType) in String
"&[" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
inner String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"]"
  | String
"Option<" String -> String -> Bool
forall a. Eq a => [a] -> [a] -> Bool
`isPrefixOf` String
rustType = let inner :: String
inner = Int -> String -> String
forall a. Int -> [a] -> [a]
take (String -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length String
rustType Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
8) (Int -> String -> String
forall a. Int -> [a] -> [a]
drop Int
7 String
rustType) in String
"Option<" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String -> String
toRustBorrowType String
inner String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
">"
  | Bool
otherwise = String
rustType

needsOptionalWrapper :: Schema.ConvexType -> Bool
needsOptionalWrapper :: ConvexType -> Bool
needsOptionalWrapper (Schema.VOptional ConvexType
_) = Bool
True
needsOptionalWrapper (Schema.VUnion [ConvexType]
ts) = ConvexType
Schema.VNull ConvexType -> [ConvexType] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ConvexType]
ts
needsOptionalWrapper ConvexType
_ = Bool
False

toPascalCase :: String -> String
toPascalCase :: String -> String
toPascalCase String
s = (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap String -> String
capitalize [String]
parts
  where
    parts :: [String]
parts = String -> [String]
words (String -> [String]) -> String -> [String]
forall a b. (a -> b) -> a -> b
$ (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map (\Char
c -> if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'_' then Char
' ' else Char
c) String
s

capitalize :: String -> String
capitalize :: String -> String
capitalize String
"" = String
""
capitalize (Char
c : String
cs) = Char -> Char
toUpper Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String
cs

toSnakeCase :: String -> String
toSnakeCase :: String -> String
toSnakeCase String
"" = String
""
toSnakeCase (Char
c : String
cs) = Char -> Char
toLower Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
go String
cs
  where
    go :: String -> String
go (Char
c' : String
cs')
      | Char -> Bool
isUpper Char
c' = Char
'_' Char -> String -> String
forall a. a -> [a] -> [a]
: Char -> Char
toLower Char
c' Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
go String
cs'
      | Bool
otherwise = Char
c' Char -> String -> String
forall a. a -> [a] -> [a]
: String -> String
go String
cs'
    go String
"" = String
""

stripNewlines :: String -> String
stripNewlines :: String -> String
stripNewlines String
s = [String] -> String
unlines ([String] -> String)
-> ([String] -> [String]) -> [String] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (String -> String -> Bool
forall a. Eq a => a -> a -> Bool
/= String
"") ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ String -> [String]
lines String
s