sqlx_core/config/mod.rs
1//! (Exported for documentation only) Guide and reference for `sqlx.toml` files.
2//!
3//! To use, create a `sqlx.toml` file in your crate root (the same directory as your `Cargo.toml`).
4//! The configuration in a `sqlx.toml` configures SQLx *only* for the current crate.
5//!
6//! Requires the `sqlx-toml` feature (not enabled by default).
7//!
8//! `sqlx-cli` will also read `sqlx.toml` when running migrations.
9//!
10//! See the [`Config`] type and its fields for individual configuration options.
11//!
12//! See the [reference][`_reference`] for the full `sqlx.toml` file.
13
14use std::error::Error;
15use std::fmt::Debug;
16use std::io;
17use std::path::{Path, PathBuf};
18
19/// Configuration shared by multiple components.
20///
21/// See [`common::Config`] for details.
22pub mod common;
23
24pub mod drivers;
25
26/// Configuration for the `query!()` family of macros.
27///
28/// See [`macros::Config`] for details.
29pub mod macros;
30
31/// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
32///
33/// See [`migrate::Config`] for details.
34pub mod migrate;
35
36/// Reference for `sqlx.toml` files
37///
38/// Source: `sqlx-core/src/config/reference.toml`
39///
40/// ```toml
41#[doc = include_str!("reference.toml")]
42/// ```
43pub mod _reference {}
44
45#[cfg(all(test, feature = "sqlx-toml"))]
46mod tests;
47
48/// The parsed structure of a `sqlx.toml` file.
49#[derive(Debug, Default)]
50#[cfg_attr(
51 feature = "sqlx-toml",
52 derive(serde::Deserialize),
53 serde(default, rename_all = "kebab-case", deny_unknown_fields)
54)]
55pub struct Config {
56 /// Configuration shared by multiple components.
57 ///
58 /// See [`common::Config`] for details.
59 pub common: common::Config,
60
61 /// Configuration for database drivers.
62 ///
63 /// See [`drivers::Config`] for details.
64 pub drivers: drivers::Config,
65
66 /// Configuration for the `query!()` family of macros.
67 ///
68 /// See [`macros::Config`] for details.
69 pub macros: macros::Config,
70
71 /// Configuration for migrations when executed using `sqlx::migrate!()` or through `sqlx-cli`.
72 ///
73 /// See [`migrate::Config`] for details.
74 pub migrate: migrate::Config,
75}
76
77/// Error returned from various methods of [`Config`].
78#[derive(thiserror::Error, Debug)]
79pub enum ConfigError {
80 /// The loading method expected `CARGO_MANIFEST_DIR` to be set and it wasn't.
81 ///
82 /// This is necessary to locate the root of the crate currently being compiled.
83 ///
84 /// See [the "Environment Variables" page of the Cargo Book][cargo-env] for details.
85 ///
86 /// [cargo-env]: https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates
87 #[error("environment variable `CARGO_MANIFEST_DIR` must be set and valid")]
88 Env(
89 #[from]
90 #[source]
91 std::env::VarError,
92 ),
93
94 /// No configuration file was found. Not necessarily fatal.
95 #[error("config file {path:?} not found")]
96 NotFound { path: PathBuf },
97
98 /// An I/O error occurred while attempting to read the config file at `path`.
99 ///
100 /// If the error is [`io::ErrorKind::NotFound`], [`Self::NotFound`] is returned instead.
101 #[error("error reading config file {path:?}")]
102 Io {
103 path: PathBuf,
104 #[source]
105 error: io::Error,
106 },
107
108 /// An error in the TOML was encountered while parsing the config file at `path`.
109 ///
110 /// The error gives line numbers and context when printed with `Display`/`ToString`.
111 ///
112 /// Only returned if the `sqlx-toml` feature is enabled.
113 #[error("error parsing config file {path:?}")]
114 Parse {
115 path: PathBuf,
116 /// Type-erased [`toml::de::Error`].
117 #[source]
118 error: Box<dyn Error + Send + Sync + 'static>,
119 },
120
121 /// A `sqlx.toml` file was found or specified, but the `sqlx-toml` feature is not enabled.
122 #[error("SQLx found config file at {path:?} but the `sqlx-toml` feature was not enabled")]
123 ParseDisabled { path: PathBuf },
124}
125
126impl ConfigError {
127 /// Create a [`ConfigError`] from a [`std::io::Error`].
128 ///
129 /// Maps to either `NotFound` or `Io`.
130 pub fn from_io(path: impl Into<PathBuf>, error: io::Error) -> Self {
131 if error.kind() == io::ErrorKind::NotFound {
132 Self::NotFound { path: path.into() }
133 } else {
134 Self::Io {
135 path: path.into(),
136 error,
137 }
138 }
139 }
140
141 /// If this error means the file was not found, return the path that was attempted.
142 pub fn not_found_path(&self) -> Option<&Path> {
143 if let Self::NotFound { path } = self {
144 Some(path)
145 } else {
146 None
147 }
148 }
149}
150
151/// Internal methods for loading a `Config`.
152#[allow(clippy::result_large_err)]
153impl Config {
154 /// Read `$CARGO_MANIFEST_DIR/sqlx.toml` or return `Config::default()` if it does not exist.
155 ///
156 /// # Errors
157 /// * If `CARGO_MANIFEST_DIR` is not set.
158 /// * If the file exists but could not be read or parsed.
159 /// * If the file exists but the `sqlx-toml` feature is disabled.
160 pub fn try_from_crate_or_default() -> Result<Self, ConfigError> {
161 Self::try_from_path_or_default(get_crate_path()?)
162 }
163
164 /// Attempt to read `Config` from the path given, or return `Config::default()` if it does not exist.
165 ///
166 /// # Errors
167 /// * If the file exists but could not be read or parsed.
168 /// * If the file exists but the `sqlx-toml` feature is disabled.
169 pub fn try_from_path_or_default(path: PathBuf) -> Result<Self, ConfigError> {
170 Self::read_from(path).or_else(|e| {
171 if let ConfigError::NotFound { .. } = e {
172 Ok(Config::default())
173 } else {
174 Err(e)
175 }
176 })
177 }
178
179 /// Attempt to read `Config` from the path given.
180 ///
181 /// # Errors
182 /// * If the file does not exist.
183 /// * If the file exists but could not be read or parsed.
184 /// * If the file exists but the `sqlx-toml` feature is disabled.
185 pub fn try_from_path(path: PathBuf) -> Result<Self, ConfigError> {
186 Self::read_from(path)
187 }
188
189 #[cfg(feature = "sqlx-toml")]
190 fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
191 // The `toml` crate doesn't provide an incremental reader.
192 let toml_s = match std::fs::read_to_string(&path) {
193 Ok(toml) => toml,
194 Err(error) => {
195 return Err(ConfigError::from_io(path, error));
196 }
197 };
198
199 // TODO: parse and lint TOML structure before deserializing
200 // Motivation: https://github.com/toml-rs/toml/issues/761
201 tracing::debug!("read config TOML from {path:?}:\n{toml_s}");
202
203 toml::from_str(&toml_s).map_err(|error| ConfigError::Parse {
204 path,
205 error: Box::new(error),
206 })
207 }
208
209 #[cfg(not(feature = "sqlx-toml"))]
210 fn read_from(path: PathBuf) -> Result<Self, ConfigError> {
211 match path.try_exists() {
212 Ok(true) => Err(ConfigError::ParseDisabled { path }),
213 Ok(false) => Err(ConfigError::NotFound { path }),
214 Err(e) => Err(ConfigError::from_io(path, e)),
215 }
216 }
217}
218
219fn get_crate_path() -> Result<PathBuf, ConfigError> {
220 let mut path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR")?);
221 path.push("sqlx.toml");
222 Ok(path)
223}