razeri_plugin_contract/
task_renderer.rs1use crate::SqlRenderer;
8
9#[derive(Debug)]
14pub struct TaskRenderConfig<'a> {
15 pub name: &'a str,
17 pub body: &'a str,
19 pub schedule: Option<String>,
21 pub warehouse: Option<&'a str>,
23 pub comment: Option<&'a str>,
25 pub error_integration: Option<&'a str>,
27 pub condition: Option<&'a str>,
29 pub dependencies: Vec<TaskDependencyConfig<'a>>,
31 pub enabled: bool,
35}
36
37#[derive(Debug)]
39pub struct TaskDependencyConfig<'a> {
40 pub predecessor: &'a str,
42 pub condition: &'a str,
44}
45
46#[must_use]
50pub fn render_dependency_comment(deps: &[TaskDependencyConfig<'_>]) -> String {
51 if deps.is_empty() {
52 return String::new();
53 }
54 let strs: Vec<String> =
55 deps.iter().map(|d| format!("{} ({})", d.predecessor, d.condition)).collect();
56 crate::comment::block_comment(&format!("Dependencies: {}", strs.join(", ")))
57}
58
59pub trait TaskRenderer: SqlRenderer {
63 fn render_schedule_cron(&self, expression: &str, timezone: Option<&str>) -> String {
69 let expression = crate::comment::cron_in_comment(expression);
70 let tz = timezone.unwrap_or("UTC");
71 crate::comment::block_comment(&format!("Schedule: cron {expression} {tz}"))
72 }
73
74 fn render_schedule_interval(&self, minutes: u32) -> String {
76 crate::comment::block_comment(&format!("Schedule: every {minutes} minutes"))
77 }
78
79 fn render_schedule_stream(&self, stream_name: &str) -> String {
81 crate::comment::block_comment(&format!("Stream trigger: {stream_name}"))
82 }
83
84 fn render_schedule_event(&self, source: &str, description: &str) -> String {
86 crate::comment::block_comment(&format!("Event-driven: {source} ({description})"))
87 }
88
89 fn render_create_task(&self, config: &TaskRenderConfig<'_>) -> Option<String> {
104 let _ = config;
105 None
106 }
107
108 fn render_drop_task(&self, name: &str) -> String {
110 crate::comment::block_comment(&format!("Drop task: {name} (platform manages externally)"))
111 }
112
113 fn render_resume_task(&self, name: &str) -> String {
115 crate::comment::block_comment(&format!("Resume task: {name} (no native suspend/resume)"))
116 }
117
118 fn render_suspend_task(&self, name: &str) -> String {
120 crate::comment::block_comment(&format!("Suspend task: {name} (no native suspend/resume)"))
121 }
122
123 fn render_bulk_load(&self, table: &str, source: &str, format: &str) -> Option<String> {
127 let _ = (table, source, format);
128 None
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use crate::DataType;
136
137 struct TestDialect;
138
139 impl SqlRenderer for TestDialect {
140 fn render_data_type(&self, _dt: &DataType) -> String {
141 "TEXT".to_string()
142 }
143
144 fn quote_identifier(&self, name: &str) -> String {
145 format!("\"{name}\"")
146 }
147
148 fn qualify_name(&self, schema: Option<&str>, name: &str) -> String {
149 match schema {
150 Some(s) => format!("\"{s}\".\"{name}\""),
151 None => format!("\"{name}\""),
152 }
153 }
154
155 fn render_current_timestamp(&self) -> String {
156 "CURRENT_TIMESTAMP".to_string()
157 }
158 }
159
160 impl TaskRenderer for TestDialect {}
161
162 #[test]
163 fn task_renderer_defaults_return_comments() {
164 let d = TestDialect;
165
166 assert_eq!(
168 d.render_schedule_cron("0 * * * *", Some("UTC")),
169 crate::comment::block_comment("Schedule: cron 0 * * * * UTC")
170 );
171 assert_eq!(
172 d.render_schedule_interval(15),
173 crate::comment::block_comment("Schedule: every 15 minutes")
174 );
175 assert_eq!(
176 d.render_schedule_stream("my_stream"),
177 crate::comment::block_comment("Stream trigger: my_stream")
178 );
179 assert_eq!(
180 d.render_schedule_event("sns", "order placed"),
181 crate::comment::block_comment("Event-driven: sns (order placed)")
182 );
183
184 let config = TaskRenderConfig {
187 name: "hourly",
188 body: "SELECT 1",
189 schedule: Some(crate::comment::block_comment("Schedule: cron 0 * * * * UTC")),
190 warehouse: None,
191 comment: Some("Hourly check"),
192 error_integration: None,
193 condition: None,
194 dependencies: vec![],
195 enabled: true,
196 };
197 assert_eq!(d.render_create_task(&config), None);
198
199 assert_eq!(
201 d.render_drop_task("hourly"),
202 crate::comment::block_comment("Drop task: hourly (platform manages externally)")
203 );
204 assert_eq!(
205 d.render_resume_task("hourly"),
206 crate::comment::block_comment("Resume task: hourly (no native suspend/resume)")
207 );
208 assert_eq!(
209 d.render_suspend_task("hourly"),
210 crate::comment::block_comment("Suspend task: hourly (no native suspend/resume)")
211 );
212
213 assert_eq!(d.render_bulk_load("raw_orders", "s3://bucket/orders.csv", "csv"), None);
215 }
216
217 #[test]
218 fn render_dependency_comment_formats_correctly() {
219 let deps = vec![
220 TaskDependencyConfig { predecessor: "task_a", condition: "on success" },
221 TaskDependencyConfig { predecessor: "task_b", condition: "on completion" },
222 ];
223 let result = render_dependency_comment(&deps);
224 assert_eq!(
225 result,
226 crate::comment::block_comment(
227 "Dependencies: task_a (on success), task_b (on completion)"
228 )
229 );
230 }
231
232 #[test]
233 fn render_dependency_comment_empty_returns_empty() {
234 assert_eq!(render_dependency_comment(&[]), "");
235 }
236}