1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use anyhow::{bail, Context, Result};
use log::debug;
use std::{fs::File, io::Write, path::PathBuf};
use crate::tera::get_tera;
use crate::utils::{get_current_working_dir, get_full_path_str, is_dir, pretty_print};
#[derive(clap::Args, Debug, Clone)]
pub struct Build {
pub file: PathBuf,
#[arg(long, short)]
pub out: Option<PathBuf>,
#[arg(long, short)]
pub context: Option<PathBuf>,
#[arg(long, short)]
pub no_pretty: Option<bool>,
}
pub async fn call(args: Build) -> Result<()> {
let sql = build(args.clone())?;
match args.out {
Some(path) => {
let mut file = File::create(&path)
.with_context(|| format!("could not create output file {}", path.display()))?;
match file.write_all(sql.as_bytes()) {
Ok(_) => println!("Write to {}", path.display()),
Err(e) => println!("Failed write to {}: {}", path.display(), e),
}
}
None => {
if args.no_pretty.unwrap_or_default() {
print!("{}", sql);
} else {
pretty_print(sql.as_bytes());
}
}
}
Ok(())
}
pub fn build(args: Build) -> Result<String> {
let path = &args.file;
let is_dir = is_dir(path);
if is_dir && path.read_dir()?.next().is_none() {
return Ok("".to_string());
}
let (working_dir, path_str) = get_dirs(args.clone())?;
if is_dir
&& !path
.read_dir()?
.filter_map(Result::ok)
.any(|f| f.path().extension().unwrap_or_default() == "sql")
{
let files = path
.read_dir()?
.map(Result::ok)
.into_iter()
.flatten()
.map(|f| f.path())
.collect::<Vec<_>>();
bail!("top-level doesn't contains any .sql file: {:?}", files);
}
let tera = get_tera(args.file.clone(), working_dir)?;
let loaded_template: Vec<_> = tera.get_template_names().collect();
debug!("loaded templates: {:?}", loaded_template);
let context = tera::Context::new();
let endpoint = if is_dir {
format!("{}/index.sql", path_str)
.trim_start_matches('/')
.to_string()
} else {
path_str.to_string()
};
let out = tera
.render(&endpoint, &context)
.with_context(|| format!("failed to render from {}", path_str))?;
Ok(out.trim().to_string())
}
fn get_dirs(args: Build) -> Result<(PathBuf, String)> {
let path = &args.file;
let working_dir = get_current_working_dir(args.context)?;
debug!("Working dir: {}", &working_dir.display());
let path_str = get_full_path_str(path)?;
let working_dir_str = working_dir.to_str().expect("could not get working dir str");
let path_str = path_str
.trim_start_matches(working_dir_str)
.trim_start_matches('/')
.to_string();
Ok((working_dir, path_str))
}