#[cfg(test)]
mod tests;
use std::collections::HashSet;
use thiserror::Error;
use crate::{
alphabet::Alphabet,
scheme::{AlgorithmScheme, SchemeProperties, SubstitutionFormulaDefinitionError},
};
use super::SubstitutionFormula;
#[derive(Clone)]
pub struct AlgorithmSchemeBuilder {
alphabet: Option<Alphabet>,
delimiter: Option<char>,
final_marker: Option<char>,
}
impl AlgorithmSchemeBuilder {
const DEFAULT_DELIMITER: char = '→';
const DEFAULT_FINAL_MARKER: char = '⋅';
pub fn new() -> Self {
Self {
alphabet: None,
delimiter: None,
final_marker: None,
}
}
pub fn with_delimiter(mut self, delimiter: char) -> Self {
_ = self.delimiter.insert(delimiter);
self
}
pub fn with_final_marker(mut self, final_marker: char) -> Self {
_ = self.final_marker.insert(final_marker);
self
}
pub fn with_alphabet(mut self, alphabet: Alphabet) -> Self {
_ = self.alphabet.insert(alphabet);
self
}
pub fn build_with_formula_definitions<'a, I>(
self,
formula_definitions: I,
) -> Result<AlgorithmScheme, AlgorithmSchemeDefinitionError>
where
I: Iterator<Item = &'a str>,
{
let properties = self.finalize_properties();
let assertions = PropertyAssertions::new(&properties);
assertions.assert_all_properties_are_valid()?;
let mut collection_builder = SubstitutionFormulaCollectionBuilder::new(&properties);
for formula_definition in formula_definitions {
assertions.assert_definition_conforms_to_properties(formula_definition)?;
collection_builder.try_add_formula(formula_definition)?;
}
let SubstitutionFormulaCollectionBuilder {
properties: _,
store,
substitution_formulas,
} = collection_builder;
Ok(AlgorithmScheme {
properties,
store,
substitution_formulas,
})
}
fn finalize_properties(self) -> SchemeProperties {
SchemeProperties {
delimiter: self.delimiter.unwrap_or(Self::DEFAULT_DELIMITER),
final_marker: self.final_marker.unwrap_or(Self::DEFAULT_FINAL_MARKER),
alphabet: self.alphabet.unwrap_or_else(Self::create_default_alphabet),
}
}
fn create_default_alphabet() -> Alphabet {
let default_alphabet_set: HashSet<_> = ('a'..='z')
.into_iter()
.chain(('A'..='Z').into_iter())
.chain(('0'..='9').into_iter())
.collect();
Alphabet::try_from(&default_alphabet_set).unwrap()
}
}
impl Default for AlgorithmSchemeBuilder {
fn default() -> Self {
Self::new()
}
}
struct PropertyAssertions<'a> {
properties: &'a SchemeProperties,
}
impl<'a> PropertyAssertions<'a> {
fn new(properties: &'a SchemeProperties) -> Self {
Self { properties }
}
fn assert_definition_conforms_to_properties(
&self,
formula_definition: &str,
) -> Result<(), AlgorithmSchemeDefinitionError> {
let invalid_characters = formula_definition
.matches(|character| {
!self.properties.alphabet.contains_extended(character)
&& character != self.properties.delimiter
&& character != self.properties.final_marker
})
.fold(String::new(), |mut accumulator, character| {
accumulator.push_str(character);
accumulator
});
if invalid_characters.is_empty() {
Ok(())
} else {
Err(AlgorithmSchemeDefinitionError::UnknownCharactersEncountered(invalid_characters))
}
}
fn assert_all_properties_are_valid(&self) -> Result<(), AlgorithmSchemeDefinitionError> {
if self.properties.delimiter == self.properties.final_marker {
Err(
AlgorithmSchemeDefinitionError::DelimiterAndFinalMarkerAreTheSame(
self.properties.delimiter,
),
)
} else if self
.properties
.alphabet
.contains_extended(self.properties.delimiter)
{
Err(
AlgorithmSchemeDefinitionError::DelimiterBelongsToTheAlphabet(
self.properties.delimiter,
),
)
} else if self
.properties
.alphabet
.contains_extended(self.properties.final_marker)
{
Err(
AlgorithmSchemeDefinitionError::FinalMarkerBelongsToTheAlphabet(
self.properties.final_marker,
),
)
} else {
Ok(())
}
}
}
struct SubstitutionFormulaCollectionBuilder<'a> {
store: String,
substitution_formulas: Vec<SubstitutionFormula>,
properties: &'a SchemeProperties,
}
impl<'a> SubstitutionFormulaCollectionBuilder<'a> {
fn new(properties: &'a SchemeProperties) -> Self {
Self {
store: String::new(),
substitution_formulas: Vec::new(),
properties,
}
}
fn try_add_formula(
&mut self,
formula_definition: &str,
) -> Result<(), AlgorithmSchemeDefinitionError> {
let start = self.store.len();
self.store.push_str(formula_definition);
let end = self.store.len();
match SubstitutionFormula::new(self.properties, &self.store, start..end) {
Ok(formula) => self.substitution_formulas.push(formula),
Err(error) => {
return Err(AlgorithmSchemeDefinitionError::FormulaCreationError { source: error })
}
}
Ok(())
}
}
#[derive(Debug, Error, PartialEq, Eq)]
pub enum AlgorithmSchemeDefinitionError {
#[error("the same character '{0}' cannot be used as a delimiter and as a final marker")]
DelimiterAndFinalMarkerAreTheSame(char),
#[error(
"the character '{0}' cannot be used as a delimiter because it belongs to the alphabet"
)]
DelimiterBelongsToTheAlphabet(char),
#[error(
"the character '{0}' cannot be used as a final marker because it belongs to the alphabet"
)]
FinalMarkerBelongsToTheAlphabet(char),
#[error("encountered an issue during the creation of substitution formulas: {source}")]
FormulaCreationError {
source: SubstitutionFormulaDefinitionError,
},
#[error("the definition of the scheme contains the characters that neither belong to the alphabet, \
nor are delimiter or final marker (unknown characters: \"{0}\")")]
UnknownCharactersEncountered(String),
}