use std::str::FromStr;
use serde::Serialize;
use thiserror::Error;
use crate::erlang_mk::MakeVariables;
use crate::model::app_manifest::ManifestUnreadable;
use crate::model::names::{ApplicationName, MakeVariableName, RelativePath};
use crate::model::non_empty::NonEmpty;
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct MakefileVariable {
pub file_path: RelativePath,
pub variable: MakeVariableName,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum ReleaseRoots {
Apps(NonEmpty<ApplicationName>),
Variable(MakefileVariable),
Both {
apps: NonEmpty<ApplicationName>,
variable: MakefileVariable,
},
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum ReleaseRootsError {
#[error("{file_path} is not present at the tag")]
FileMissing { file_path: RelativePath },
#[error("{variable} in {file_path} cannot be read: {reason:?}")]
VariableUnreadable {
file_path: RelativePath,
variable: MakeVariableName,
reason: ManifestUnreadable,
},
#[error("{variable} in {file_path} lists no apps and no literal roots are configured")]
NoRoots {
file_path: RelativePath,
variable: MakeVariableName,
},
#[error("{word:?} in {variable} is not an app name")]
NotAnApp {
variable: MakeVariableName,
word: String,
},
}
impl ReleaseRoots {
pub fn read(
&self,
read_file: impl Fn(&RelativePath) -> Option<String>,
) -> Result<NonEmpty<ApplicationName>, ReleaseRootsError> {
match self {
Self::Apps(apps) => Ok(apps.clone()),
Self::Variable(variable) => {
let words = variable_roots(variable, &read_file)?;
NonEmpty::try_from(words).map_err(|_| ReleaseRootsError::NoRoots {
file_path: variable.file_path.clone(),
variable: variable.variable.clone(),
})
}
Self::Both { apps, variable } => {
let more = variable_roots(variable, &read_file)?;
Ok(NonEmpty::new(
apps.first().clone(),
apps.iter().skip(1).cloned().chain(more),
))
}
}
}
}
fn variable_roots(
variable: &MakefileVariable,
read_file: &impl Fn(&RelativePath) -> Option<String>,
) -> Result<Vec<ApplicationName>, ReleaseRootsError> {
let text = read_file(&variable.file_path).ok_or_else(|| ReleaseRootsError::FileMissing {
file_path: variable.file_path.clone(),
})?;
let variables = MakeVariables::parse(&text);
let words = variables.words(&variable.variable).map_err(|reason| {
ReleaseRootsError::VariableUnreadable {
file_path: variable.file_path.clone(),
variable: variable.variable.clone(),
reason,
}
})?;
words
.iter()
.map(|word| {
ApplicationName::from_str(word).map_err(|_| ReleaseRootsError::NotAnApp {
variable: variable.variable.clone(),
word: word.clone(),
})
})
.collect()
}