cynic_codegen/
registration.rs1use std::{
2 borrow::Cow,
3 io::Write,
4 path::{Path, PathBuf},
5};
6
7use once_cell::unsync::OnceCell;
8
9use crate::schema::{self, Schema, SchemaInput};
10
11pub fn register_schema(name: &str) -> SchemaRegistrationBuilder<'_> {
19 SchemaRegistrationBuilder {
20 name,
21 dry_run: false,
22 }
23}
24
25#[derive(thiserror::Error, Debug)]
26#[error("Could not register schema with cynic")]
27pub enum SchemaRegistrationError {
28 #[error("IOError: {0}")]
29 IoError(#[from] std::io::Error),
30 #[error("Could not find the OUT_DIR environment variable, which should be set by cargo")]
31 OutDirNotSet,
32 #[error("Errors when parsing schema: {0}")]
33 SchemaErrors(String),
34}
35
36#[must_use]
37pub struct SchemaRegistrationBuilder<'a> {
41 name: &'a str,
42 dry_run: bool,
43}
44
45impl<'a> SchemaRegistrationBuilder<'a> {
46 pub fn from_sdl_file(
48 self,
49 path: impl AsRef<std::path::Path>,
50 ) -> Result<SchemaRegistration<'a>, SchemaRegistrationError> {
51 let SchemaRegistrationBuilder { name, dry_run } = self;
52 fn inner<'a>(
53 name: &'a str,
54 path: &Path,
55 dry_run: bool,
56 ) -> Result<SchemaRegistration<'a>, SchemaRegistrationError> {
57 let data = std::fs::read_to_string(path)?;
58 let registration = SchemaRegistration {
59 name,
60 data: Cow::Owned(data),
61 schema: OnceCell::default(),
62 dry_run,
63 };
64 registration.write(registration.filename()?)?;
65 registration.write_schema_module()?;
66 cargo_rerun_if_changed(path.as_os_str().to_str().expect("utf8 paths"));
67 Ok(registration)
68 }
69
70 inner(name, path.as_ref(), dry_run)
71 }
72
73 pub fn from_sdl(self, sdl: &'a str) -> Result<SchemaRegistration<'a>, SchemaRegistrationError> {
75 let SchemaRegistrationBuilder { name, dry_run } = self;
76 let registration = SchemaRegistration {
77 name,
78 data: Cow::Borrowed(sdl),
79 schema: OnceCell::default(),
80 dry_run,
81 };
82 registration.write(registration.filename()?)?;
83 registration.write_schema_module()?;
84 Ok(registration)
85 }
86
87 #[doc(hidden)]
88 pub fn dry_run(mut self) -> Self {
90 self.dry_run = true;
91 self
92 }
93}
94
95pub struct SchemaRegistration<'a> {
99 name: &'a str,
100 data: Cow<'a, str>,
101 schema: OnceCell<Schema<'a, schema::Validated>>,
102 dry_run: bool,
103}
104
105impl SchemaRegistration<'_> {
107 pub fn as_default(self) -> Result<Self, SchemaRegistrationError> {
115 if self.dry_run {
116 return Ok(self);
117 }
118 self.write(default_filename(&out_dir()?))?;
119 Ok(self)
120 }
121}
122
123impl SchemaRegistration<'_> {
125 fn write(&self, mut filename: PathBuf) -> Result<(), SchemaRegistrationError> {
126 if self.dry_run {
127 return Ok(());
128 }
129 std::fs::create_dir_all(filename.parent().expect("filename to have a parent"))?;
130 #[cfg(feature = "rkyv")]
131 {
132 filename.set_extension("rkyv");
133
134 let optimised = self.schema()?.optimise();
135 let bytes = rkyv::to_bytes::<_, 4096>(&optimised).unwrap();
136
137 Ok(std::fs::write(filename, &bytes)?)
138 }
139 #[cfg(not(feature = "rkyv"))]
140 {
141 filename.set_extension("graphql");
142 Ok(std::fs::write(filename, self.data.as_bytes())?)
143 }
144 }
145
146 fn write_schema_module(&self) -> Result<(), SchemaRegistrationError> {
147 use crate::use_schema::use_schema_impl;
148
149 let tokens = use_schema_impl(self.schema()?)
150 .map_err(|errors| SchemaRegistrationError::SchemaErrors(errors.to_string()))?;
151
152 if self.dry_run {
153 return Ok(());
156 }
157
158 let schema_module_filename = schema_module_filename(self.name, &out_dir()?);
159 std::fs::create_dir_all(
160 schema_module_filename
161 .parent()
162 .expect("filename to have a parent"),
163 )?;
164
165 let mut out = std::fs::File::create(&schema_module_filename)?;
166 write!(&mut out, "{}", tokens)?;
167
168 Ok(())
169 }
170
171 fn filename(&self) -> Result<PathBuf, SchemaRegistrationError> {
172 if self.dry_run {
173 return Ok(PathBuf::from(""));
174 }
175 let out_dir = out_dir()?;
176 Ok(registration_filename(self.name, &out_dir))
177 }
178
179 fn schema(&self) -> Result<&Schema<'_, schema::Validated>, SchemaRegistrationError> {
180 self.schema.get_or_try_init(|| {
181 let ast = crate::schema::load_schema(self.data.as_ref())
182 .map_err(|error| SchemaRegistrationError::SchemaErrors(error.to_string()))?;
183
184 let schema = Schema::new(SchemaInput::Document(ast))
185 .validate()
186 .map_err(|errors| SchemaRegistrationError::SchemaErrors(errors.to_string()))?;
187
188 Ok(schema)
189 })
190 }
191}
192
193fn cargo_rerun_if_changed(name: &str) {
194 println!("cargo:rerun-if-changed={name}");
195}
196
197pub(super) fn out_dir() -> Result<String, SchemaRegistrationError> {
198 let out_dir = std::env::var("OUT_DIR").map_err(|_| SchemaRegistrationError::OutDirNotSet)?;
199 Ok(out_dir)
200}
201
202pub(super) fn schema_module_filename(name: &str, out_dir: &str) -> PathBuf {
203 let mut path = PathBuf::from(out_dir);
204 path.push("cynic-schemas");
205 path.push(format!("{name}.rs",));
206
207 path
208}
209
210fn registration_filename(name: &str, out_dir: &str) -> PathBuf {
211 let mut path = PathBuf::from(out_dir);
212 path.push("cynic-schemas");
213 path.push(format!("{name}.graphql",));
214
215 path
216}
217
218fn default_filename(out_dir: &str) -> PathBuf {
219 let mut path = PathBuf::from(out_dir);
220 path.push("cynic-schemas");
221 path.push("default.graphql");
222
223 path
224}