pub mod source;
mod build;
mod clean;
mod core;
mod hook;
mod proxy;
mod serve;
mod tools;
mod watch;
pub use build::*;
pub use clean::*;
pub use core::*;
pub use hook::*;
pub use proxy::*;
pub use serve::*;
pub use tools::*;
pub use watch::*;
#[cfg(test)]
mod test;
use anyhow::{bail, Context, Result};
use schemars::JsonSchema;
use serde::Deserialize;
use source::Source;
use std::path::PathBuf;
use tracing::log;
pub trait ConfigModel {
fn migrate(&mut self) -> Result<()> {
Ok(())
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, JsonSchema)]
pub struct Configuration {
#[serde(flatten)]
pub core: Core,
#[serde(default)]
pub build: Build,
#[serde(default)]
pub tools: Tools,
#[serde(default)]
pub hooks: Hooks,
#[serde(default)]
pub watch: Watch,
#[serde(default)]
pub serve: Serve,
#[serde(default)]
pub clean: Clean,
#[serde(default)]
#[serde(alias = "proxy")]
pub proxies: Proxies,
}
impl ConfigModel for Configuration {
#[allow(deprecated)]
fn migrate(&mut self) -> Result<()> {
self.core.migrate()?;
self.tools.migrate()?;
self.hooks.migrate()?;
self.proxies.migrate()?;
self.clean.migrate()?;
self.build.migrate()?;
self.watch.migrate()?;
self.serve.migrate()?;
if let Some(dist) = self.clean.dist.take() {
log::warn!("'clean.dist' is used in the configuration. This is deprecated for the global 'dist' field and will result in an error in a future release.");
self.core.dist = Some(dist);
}
if let Some(backend) = self.serve.proxy_backend.take() {
log::warn!("The proxy fields in the configuration are deprecated and will be removed in a future version. Migrate those settings into an entry of the `proxies` field, which allows adding more than one.");
self.proxies.0.push(Proxy {
backend,
request_headers: Default::default(),
rewrite: self.serve.proxy_rewrite.take(),
ws: self.serve.proxy_ws.unwrap_or_default(),
insecure: self.serve.proxy_insecure.unwrap_or_default(),
no_system_proxy: self.serve.proxy_no_system_proxy.unwrap_or_default(),
no_redirect: self.serve.proxy_no_redirect.unwrap_or_default(),
})
}
Ok(())
}
}
pub async fn load(path: Option<PathBuf>) -> Result<(Configuration, PathBuf)> {
match path {
Some(path) if path.is_file() => {
let path = path.canonicalize().with_context(|| {
format!(
"unable to canonicalize path to configuration: '{}'",
path.display()
)
})?;
let Some(cwd) = path.parent() else {
bail!("unable to get parent directory of '{}'", path.display());
};
let cwd = cwd.to_path_buf();
Ok((Source::File(path).load().await?, cwd))
}
Some(path) if path.is_dir() => Ok((Source::find(&path)?.load().await?, path)),
Some(path) => bail!("{} is neither a file nor a directory", path.display()),
None => {
let cwd = std::env::current_dir().context("unable to get current directory")?;
Ok((Source::find(&cwd)?.load().await?, cwd))
}
}
}