use crate::context::SproutContext;
use crate::entries::EntryDeclaration;
use crate::generators::bls::BlsConfiguration;
use crate::generators::matrix::MatrixConfiguration;
use anyhow::Result;
use anyhow::bail;
use serde::{Deserialize, Serialize};
use std::rc::Rc;

pub mod bls;
pub mod matrix;

/// Declares a generator configuration.
/// Generators allow generating entries at runtime based on a set of data.
#[derive(Serialize, Deserialize, Default, Clone)]
pub struct GeneratorDeclaration {
    /// Matrix generator configuration.
    /// Matrix allows you to specify multiple value-key values as arrays.
    /// This allows multiplying the number of entries by any number of possible
    /// configuration options. For example,
    /// data.x = ["a", "b"]
    /// data.y = ["c", "d"]
    /// would generate an entry for each of these combinations:
    /// x = a, y = c
    /// x = a, y = d
    /// x = b, y = c
    /// x = b, y = d
    #[serde(default)]
    pub matrix: Option<MatrixConfiguration>,
    /// BLS generator configuration.
    /// BLS allows you to pass a filesystem path that contains a set of BLS entries.
    /// It will generate a sprout entry for every supported BLS entry.
    #[serde(default)]
    pub bls: Option<BlsConfiguration>,
}

/// Runs the generator specified by the `generator` option.
/// It uses the specified `context` as the parent context for
/// the generated entries, injecting more values if needed.
pub fn generate(
    context: Rc<SproutContext>,
    generator: &GeneratorDeclaration,
) -> Result<Vec<(Rc<SproutContext>, EntryDeclaration)>> {
    if let Some(matrix) = &generator.matrix {
        matrix::generate(context, matrix)
    } else if let Some(bls) = &generator.bls {
        bls::generate(context, bls)
    } else {
        bail!("unknown generator configuration");
    }
}
