use std::fs;
use std::path::{Component, Path, PathBuf};
use serde::Deserialize;
use super::{Frontend, ProjectError};
#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub(crate) struct ProjectConfig {
#[serde(skip)]
root: PathBuf,
pub(crate) frontend: Frontend,
pub(crate) frontend_dir: PathBuf,
pub(crate) backend_package: String,
pub(crate) backend_binary: String,
pub(crate) backend_port: u16,
pub(crate) frontend_port: u16,
}
impl ProjectConfig {
pub(crate) fn load(root: &Path) -> Result<Self, ProjectError> {
let path = root.join("arcature.toml");
let source = fs::read_to_string(&path).map_err(|source| ProjectError::ReadConfig {
path: path.clone(),
source,
})?;
let mut config: Self = toml::from_str(&source)
.map_err(|source| ProjectError::InvalidConfig { path, source })?;
config.validate()?;
config.root = root.to_path_buf();
Ok(config)
}
pub(crate) fn root(&self) -> &Path {
&self.root
}
pub(crate) fn frontend_root(&self) -> PathBuf {
self.root.join(&self.frontend_dir)
}
fn validate(&self) -> Result<(), ProjectError> {
if self.frontend_dir.as_os_str().is_empty()
|| self.frontend_dir.is_absolute()
|| self
.frontend_dir
.components()
.any(|part| !matches!(part, Component::Normal(_)))
{
return Err(ProjectError::InvalidField {
field: "frontend_dir",
value: self.frontend_dir.display().to_string(),
});
}
super::ProjectName::parse(&self.backend_package).map_err(|_| {
ProjectError::InvalidField {
field: "backend_package",
value: self.backend_package.clone(),
}
})?;
super::ProjectName::parse(&self.backend_binary).map_err(|_| {
ProjectError::InvalidField {
field: "backend_binary",
value: self.backend_binary.clone(),
}
})?;
if self.backend_port == 0
|| self.frontend_port == 0
|| self.backend_port == self.frontend_port
{
return Err(ProjectError::InvalidField {
field: "ports",
value: format!("{}/{}", self.backend_port, self.frontend_port),
});
}
Ok(())
}
}