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,
#[serde(default = "default_backend_src_dir")]
pub(crate) backend_src_dir: PathBuf,
}
fn default_backend_src_dir() -> PathBuf {
PathBuf::from("src")
}
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> {
validate_relative_dir(&self.frontend_dir, "frontend_dir")?;
validate_relative_dir(&self.backend_src_dir, "backend_src_dir")?;
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 {
return Err(ProjectError::InvalidField {
field: "backend_port",
value: self.backend_port.to_string(),
});
}
Ok(())
}
}
fn validate_relative_dir(value: &Path, field: &'static str) -> Result<(), ProjectError> {
if value.as_os_str().is_empty()
|| value.is_absolute()
|| value
.components()
.any(|part| !matches!(part, Component::Normal(_)))
{
return Err(ProjectError::InvalidField {
field,
value: value.display().to_string(),
});
}
Ok(())
}