use std::fmt;
use std::fs::{self, File, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use super::error::ConfigurationError;
use super::parameters::ParameterSpace;
use super::paths::ProjectPaths;
const CONFIGURATION_DIRECTORY: &str = "config";
const FIXED_FILE: &str = "fixed.json";
const SWEEP_FILE: &str = "sweep.json";
const PATHS_FILE: &str = "paths.json";
#[derive(Clone)]
pub struct ProjectConfig {
project_root: PathBuf,
parameters: ParameterSpace,
paths: ProjectPaths,
}
impl ProjectConfig {
pub fn load(project_root: impl Into<PathBuf>) -> Result<Self, ConfigurationError> {
let project_root = project_root.into();
let configuration_directory = project_root.join(CONFIGURATION_DIRECTORY);
let parameters = ParameterSpace::load(&configuration_directory)?;
let paths = ProjectPaths::load(&project_root)?;
Ok(Self {
project_root,
parameters,
paths,
})
}
pub fn project_root(&self) -> &Path {
&self.project_root
}
pub fn configuration_directory(&self) -> &Path {
self.parameters.configuration_directory()
}
pub fn parameters(&self) -> &ParameterSpace {
&self.parameters
}
pub fn paths(&self) -> &ProjectPaths {
&self.paths
}
pub fn into_parts(self) -> (ParameterSpace, ProjectPaths) {
(self.parameters, self.paths)
}
pub fn write_source_config(
&self,
destination_project_root: impl AsRef<Path>,
) -> Result<(), ConfigurationError> {
let destination_project_root = destination_project_root.as_ref();
create_destination_root(destination_project_root)?;
let destination = destination_project_root.join(CONFIGURATION_DIRECTORY);
create_configuration_directory(&destination)?;
write_source_file(
&destination.join(FIXED_FILE),
self.parameters.fixed_source_json(),
)?;
write_source_file(
&destination.join(SWEEP_FILE),
self.parameters.sweep_source_json(),
)?;
write_source_file(&destination.join(PATHS_FILE), self.paths.source_json())?;
sync_directory(&destination)?;
sync_directory(destination_project_root)
}
}
impl fmt::Debug for ProjectConfig {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ProjectConfig")
.field("project_root", &self.project_root())
.field("parameters", &self.parameters.parameter_count())
.field("tasks", &self.parameters.task_count())
.field("paths", &self.paths.len())
.finish_non_exhaustive()
}
}
fn create_destination_root(path: &Path) -> Result<(), ConfigurationError> {
match fs::create_dir_all(path) {
Ok(()) => Ok(()),
Err(source) => Err(write_error(path.to_path_buf(), source)),
}
}
fn create_configuration_directory(path: &Path) -> Result<(), ConfigurationError> {
fs::create_dir(path).map_err(|source| write_error(path.to_path_buf(), source))
}
fn write_source_file(path: &Path, source_bytes: &[u8]) -> Result<(), ConfigurationError> {
let mut output = OpenOptions::new()
.write(true)
.create_new(true)
.open(path)
.map_err(|source| write_error(path.to_path_buf(), source))?;
output
.write_all(source_bytes)
.map_err(|source| write_error(path.to_path_buf(), source))?;
output
.sync_all()
.map_err(|source| write_error(path.to_path_buf(), source))
}
fn sync_directory(path: &Path) -> Result<(), ConfigurationError> {
File::open(path)
.and_then(|directory| directory.sync_all())
.map_err(|source| write_error(path.to_path_buf(), source))
}
fn write_error(path: PathBuf, source: io::Error) -> ConfigurationError {
ConfigurationError::WriteConfigurationFile { path, source }
}