schemafy_lib/
generator.rs1use crate::Expander;
2use std::{
3 io,
4 path::{Path, PathBuf},
5};
6
7#[derive(Debug, PartialEq)]
13#[must_use]
14pub struct Generator<'a, 'b> {
15 pub root_name: Option<String>,
19 pub schemafy_path: &'a str,
24 pub input_file: &'b Path,
26}
27
28impl<'a, 'b> Generator<'a, 'b> {
29 pub fn builder() -> GeneratorBuilder<'a, 'b> {
31 GeneratorBuilder::default()
32 }
33
34 pub fn generate(&self) -> proc_macro2::TokenStream {
35 let input_file = if self.input_file.is_relative() {
36 let crate_root = get_crate_root().unwrap();
37 crate_root.join(self.input_file)
38 } else {
39 PathBuf::from(self.input_file)
40 };
41
42 let json = std::fs::read_to_string(&input_file).unwrap_or_else(|err| {
43 panic!("Unable to read `{}`: {}", input_file.to_string_lossy(), err)
44 });
45
46 let schema = serde_json::from_str(&json).unwrap_or_else(|err| {
47 panic!(
48 "Cannot parse `{}` as JSON: {}",
49 input_file.to_string_lossy(),
50 err
51 )
52 });
53 let mut expander = Expander::new(self.root_name.as_deref(), self.schemafy_path, &schema);
54 expander.expand(&schema)
55 }
56
57 pub fn generate_to_file<P: ?Sized + AsRef<Path>>(&self, output_file: &'b P) -> io::Result<()> {
58 use std::process::Command;
59 let tokens = self.generate();
60 let out = tokens.to_string();
61 std::fs::write(output_file, &out)?;
62 Command::new("rustfmt")
63 .arg(output_file.as_ref().as_os_str())
64 .output()?;
65 Ok(())
66 }
67}
68
69#[derive(Debug, PartialEq)]
70#[must_use]
71pub struct GeneratorBuilder<'a, 'b> {
72 inner: Generator<'a, 'b>,
73}
74
75impl<'a, 'b> Default for GeneratorBuilder<'a, 'b> {
76 fn default() -> Self {
77 Self {
78 inner: Generator {
79 root_name: None,
80 schemafy_path: "::schemafy_core::",
81 input_file: Path::new("schema.json"),
82 },
83 }
84 }
85}
86
87impl<'a, 'b> GeneratorBuilder<'a, 'b> {
88 pub fn with_root_name(mut self, root_name: Option<String>) -> Self {
89 self.inner.root_name = root_name;
90 self
91 }
92 pub fn with_root_name_str(mut self, root_name: &str) -> Self {
93 self.inner.root_name = Some(root_name.to_string());
94 self
95 }
96 pub fn with_input_file<P: ?Sized + AsRef<Path>>(mut self, input_file: &'b P) -> Self {
97 self.inner.input_file = input_file.as_ref();
98 self
99 }
100 pub fn with_schemafy_path(mut self, schemafy_path: &'a str) -> Self {
101 self.inner.schemafy_path = schemafy_path;
102 self
103 }
104 pub fn build(self) -> Generator<'a, 'b> {
105 self.inner
106 }
107}
108
109fn get_crate_root() -> std::io::Result<PathBuf> {
110 if let Ok(path) = std::env::var("CARGO_MANIFEST_DIR") {
111 return Ok(PathBuf::from(path));
112 }
113
114 let current_dir = std::env::current_dir()?;
115
116 for p in current_dir.ancestors() {
117 if std::fs::read_dir(p)?
118 .into_iter()
119 .filter_map(Result::ok)
120 .any(|p| p.file_name().eq("Cargo.toml"))
121 {
122 return Ok(PathBuf::from(p));
123 }
124 }
125
126 Ok(current_dir)
127}