forge_core/schema/
function.rs1use super::types::RustType;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum FunctionKind {
11 Query,
13 Mutation,
15 Action,
17 Job,
19 Cron,
21 Workflow,
23}
24
25impl FunctionKind {
26 pub fn as_str(&self) -> &'static str {
28 match self {
29 FunctionKind::Query => "query",
30 FunctionKind::Mutation => "mutation",
31 FunctionKind::Action => "action",
32 FunctionKind::Job => "job",
33 FunctionKind::Cron => "cron",
34 FunctionKind::Workflow => "workflow",
35 }
36 }
37
38 pub fn is_client_callable(&self) -> bool {
40 matches!(
41 self,
42 FunctionKind::Query | FunctionKind::Mutation | FunctionKind::Action
43 )
44 }
45}
46
47impl std::fmt::Display for FunctionKind {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 write!(f, "{}", self.as_str())
50 }
51}
52
53#[derive(Debug, Clone)]
55pub struct FunctionArg {
56 pub name: String,
58 pub rust_type: RustType,
60 pub doc: Option<String>,
62}
63
64impl FunctionArg {
65 pub fn new(name: impl Into<String>, rust_type: RustType) -> Self {
67 Self {
68 name: name.into(),
69 rust_type,
70 doc: None,
71 }
72 }
73}
74
75#[derive(Debug, Clone)]
77pub struct FunctionDef {
78 pub name: String,
80 pub kind: FunctionKind,
82 pub args: Vec<FunctionArg>,
84 pub return_type: RustType,
86 pub doc: Option<String>,
88 pub is_async: bool,
90}
91
92impl FunctionDef {
93 pub fn new(name: impl Into<String>, kind: FunctionKind, return_type: RustType) -> Self {
95 Self {
96 name: name.into(),
97 kind,
98 args: Vec::new(),
99 return_type,
100 doc: None,
101 is_async: true,
102 }
103 }
104
105 pub fn query(name: impl Into<String>, return_type: RustType) -> Self {
107 Self::new(name, FunctionKind::Query, return_type)
108 }
109
110 pub fn mutation(name: impl Into<String>, return_type: RustType) -> Self {
112 Self::new(name, FunctionKind::Mutation, return_type)
113 }
114
115 pub fn action(name: impl Into<String>, return_type: RustType) -> Self {
117 Self::new(name, FunctionKind::Action, return_type)
118 }
119
120 pub fn with_arg(mut self, arg: FunctionArg) -> Self {
122 self.args.push(arg);
123 self
124 }
125
126 pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
128 self.doc = Some(doc.into());
129 self
130 }
131
132 pub fn ts_name(&self) -> String {
134 to_camel_case(&self.name)
135 }
136}
137
138fn to_camel_case(s: &str) -> String {
140 let mut result = String::new();
141 let mut capitalize_next = false;
142
143 for c in s.chars() {
144 if c == '_' {
145 capitalize_next = true;
146 } else if capitalize_next {
147 result.push(c.to_uppercase().next().unwrap_or(c));
148 capitalize_next = false;
149 } else {
150 result.push(c);
151 }
152 }
153
154 result
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_function_def_query() {
163 let func = FunctionDef::query("get_user", RustType::Custom("User".to_string()))
164 .with_arg(FunctionArg::new("id", RustType::Uuid))
165 .with_doc("Get a user by ID");
166
167 assert_eq!(func.name, "get_user");
168 assert_eq!(func.kind, FunctionKind::Query);
169 assert_eq!(func.args.len(), 1);
170 assert_eq!(func.ts_name(), "getUser");
171 }
172
173 #[test]
174 fn test_function_def_mutation() {
175 let func = FunctionDef::mutation("create_user", RustType::Custom("User".to_string()));
176 assert_eq!(func.kind, FunctionKind::Mutation);
177 }
178
179 #[test]
180 fn test_to_camel_case() {
181 assert_eq!(to_camel_case("get_user"), "getUser");
182 assert_eq!(to_camel_case("create_project_task"), "createProjectTask");
183 assert_eq!(to_camel_case("getUser"), "getUser");
184 }
185}