1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! A bin that produces a fixed set of value rows.
//!
//! ```text
//!   ┌───[directsource]───┐
//!   │          <output 0>│⇒
//!   │          <output 1>│⇒
//!   │          <output 2>│⇒
//!   ┊          …         ┊⇒
//!   │          <output n>│⇒
//!   └────────────────────┘
//! ```

use snafu::{ensure, OptionExt};

use super::{
    BinDescription, GetCalibration, Item, Iteration, Proceed, Result,
    Scope, SourceBin, SourceId, SourceNames, SourceOnlyBin,
    SourceOnlyBinDescription,
};
use crate::error;
use indexmap::IndexSet;
use std::collections::VecDeque;

static BIN_TYPE: &str = "directsource";

/// A bin that produces a fixed set of value rows.
#[derive(Debug)]
pub struct Bin {
    scope: Scope,
    columns: IndexSet<String>,
    rows: VecDeque<Vec<Item>>,

    outputs: Vec<Item>,
}

impl SourceOnlyBin for Bin {
    fn fetch_next(&mut self, _iteration: &Iteration) -> Result<Proceed> {
        if let Some(row) = self.rows.pop_front() {
            self.outputs = row;
            Ok(Proceed::Continue)
        } else {
            Ok(Proceed::Stop)
        }
    }
}

impl SourceBin for Bin {
    fn get_source_data(&self, source: &SourceId) -> Result<Item> {
        let index = self.columns.iter().position(|s| s == &source.id);

        let index = index.context(error::InvalidSourceName {
            scope: self.scope.clone(),
            name: source.id.to_string(),
            bin_type: BIN_TYPE.to_string(),
        })?;
        Ok(self.outputs.get(index).cloned().unwrap())
    }
}

/// Description of the direct source bin.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Description {
    /// The column names.
    pub columns: IndexSet<String>,

    /// The values for each row.
    pub rows: VecDeque<Vec<Item>>,
}

impl BinDescription for Description {
    type Bin = Bin;

    fn check_validity(
        &self,
        _scope: &Scope,
        _get_calibration: &mut dyn GetCalibration,
    ) -> Result<()> {
        ensure!(!self.columns.is_empty(), error::InvalidDirectSourceData);
        ensure!(
            self.columns.iter().collect::<IndexSet<_>>().len()
                == self.columns.len(),
            error::InvalidDirectSourceData
        );

        for row in &self.rows {
            ensure!(
                row.len() == self.columns.len(),
                error::InvalidDirectSourceData
            );
        }
        Ok(())
    }

    fn bin_type(&self) -> &'static str {
        BIN_TYPE
    }
}

impl SourceNames for Description {
    fn source_names(&self) -> Result<IndexSet<String>> {
        Ok(self.columns.clone())
    }
}

impl SourceOnlyBinDescription for Description {
    fn build_bin(&self, scope: &Scope) -> Result<Self::Bin> {
        Ok(Bin {
            scope: scope.clone(),
            columns: self.columns.clone(),
            rows: self.rows.clone(),
            outputs: self.columns.iter().map(|_| Item::Nothing).collect(),
        })
    }
}