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