pub mod add;
pub mod blocker;
pub mod calibration;
pub mod cborsink;
pub mod channelsink;
pub mod cosinus;
pub mod cumulation;
pub mod derivation;
pub mod directsource;
pub mod divide;
pub mod fifo;
pub mod first_value;
pub mod fixedvalues;
pub mod greater_than;
pub mod invert;
pub mod jsonlsink;
pub mod jsonlsource;
pub mod last_calm_point;
pub mod linear_regression;
pub mod maximum;
pub mod mean;
pub mod minimum;
pub mod multiplex;
pub mod multiply;
pub mod not;
pub mod passthrough;
pub mod pipeline;
pub mod single_not_null;
pub mod sinus;
pub mod storage;
pub mod subtract;
pub mod verificationsink;
#[cfg(feature = "xio")]
pub mod xiosource;
use crate::{
error, CalibrationSource, GetCalibration, Item, Proceed, Result, Scope,
};
use indexmap::IndexSet;
use snafu::{ensure, OptionExt, ResultExt};
use std::fmt::Debug;
use std::io::Write;
use std::rc::Weak;
use std::sync::RwLock;
pub trait SourceBin: Debug {
fn get_source_data(&self, source: &SourceId) -> Result<Item>;
}
impl<S: SourceBin + ?Sized> SourceBin for Box<S> {
fn get_source_data(&self, source: &SourceId) -> Result<Item> {
(**self).get_source_data(source)
}
}
pub trait SinkBin {}
impl<S: SinkBin + ?Sized> SinkBin for Box<S> {}
pub trait SourceOnlyBin: SourceBin + Debug {
fn fetch_next(&mut self, iteration: &Iteration) -> Result<Proceed>;
}
pub trait SourceSinkBin: SinkBin + SourceBin + Calculator {}
impl<T: SinkBin + SourceBin + Calculator> SourceSinkBin for T {}
pub trait SinkOnlyBin: SinkBin + Calculator + Debug {}
#[derive(Debug)]
pub struct SourceOnlyBinProcessor {
bin: Box<dyn SourceOnlyBin>,
previous_iteration: Option<Iteration>,
proceed: Proceed,
}
impl SourceOnlyBinProcessor {
pub fn new(bin: Box<dyn SourceOnlyBin>) -> Self {
SourceOnlyBinProcessor {
bin,
previous_iteration: None,
proceed: Proceed::Continue,
}
}
}
#[derive(Debug)]
pub struct SourceSinkBinProcessor {
bin: Box<dyn SourceSinkBin>,
previous_iteration: Option<Iteration>,
}
impl SinkBin for SourceSinkBinProcessor {}
impl Calculator for SourceSinkBinProcessor {
fn calculate(&mut self, iteration: &Iteration) -> Result<()> {
if self.previous_iteration != Some(iteration.clone()) {
self.bin.calculate(iteration)?;
}
self.previous_iteration = Some(iteration.clone());
Ok(())
}
}
impl SourceBin for SourceSinkBinProcessor {
fn get_source_data(&self, source: &SourceId) -> Result<Item> {
self.bin.get_source_data(source)
}
}
#[derive(Debug)]
pub struct SinkOnlyBinProcessor {
bin: Box<dyn SinkOnlyBin>,
previous_iteration: Option<Iteration>,
}
impl Calculator for SinkOnlyBinProcessor {
fn calculate(&mut self, iteration: &Iteration) -> Result<()> {
if self.previous_iteration != Some(iteration.clone()) {
self.bin.calculate(iteration)?;
}
self.previous_iteration = Some(iteration.clone());
Ok(())
}
}
impl SinkOnlyBinProcessor {
pub fn new(bin: Box<dyn SinkOnlyBin>) -> Self {
SinkOnlyBinProcessor {
bin,
previous_iteration: None,
}
}
}
impl SourceBin for SourceOnlyBinProcessor {
fn get_source_data(&self, source: &SourceId) -> Result<Item> {
self.bin.get_source_data(source)
}
}
impl SourceSinkBinProcessor {
pub fn new(bin: Box<dyn SourceSinkBin>) -> Self {
SourceSinkBinProcessor {
bin,
previous_iteration: None,
}
}
}
impl SourceOnlyBin for SourceOnlyBinProcessor {
fn fetch_next(&mut self, iteration: &Iteration) -> Result<Proceed> {
if self.proceed == Proceed::Stop {
Ok(Proceed::Stop)
} else {
self.previous_iteration = match self.previous_iteration {
None => {
self.proceed = self.bin.fetch_next(iteration)?;
Some(iteration.clone())
}
Some(ref it) if it == iteration => {
self.previous_iteration.clone()
}
Some(_) => {
self.proceed = self.bin.fetch_next(iteration)?;
Some(iteration.clone())
}
};
Ok(self.proceed.clone())
}
}
}
pub trait Calculator {
fn calculate(&mut self, iteration: &Iteration) -> Result<()>;
fn initialize(&mut self) -> Result<()> {
Ok(())
}
}
impl<T: Calculator + ?Sized> Calculator for Box<T> {
fn calculate(&mut self, iteration: &Iteration) -> Result<()> {
(**self).calculate(iteration)
}
fn initialize(&mut self) -> Result<()> {
(**self).initialize()
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum SourceOnlyDescription {
JsonLSource(jsonlsource::Description),
DirectSource(directsource::Description),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum SourceSinkDescription {
Add(add::Description),
Blocker(blocker::Description),
Calibration(calibration::Description),
Cosinus(cosinus::Description),
Cumulation(cumulation::Description),
Derivation(derivation::Description),
Divide(divide::Description),
Fifo(fifo::Description),
FirstValue(first_value::Description),
FixedValues(fixedvalues::Description),
GreaterThan(greater_than::Description),
Invert(invert::Description),
LastCalmPoint(last_calm_point::Description),
LinearRegression(linear_regression::Description),
Maximum(maximum::Description),
Mean(mean::Description),
Minimum(minimum::Description),
Multiplex(multiplex::Description),
Multiply(multiply::Description),
Not(not::Description),
Pipeline(pipeline::Description),
SingleNotNull(single_not_null::Description),
Sinus(sinus::Description),
Subtract(subtract::Description),
Storage(storage::Description),
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum SinkOnlyDescription {
JsonLSink(jsonlsink::Description),
CborSink(cborsink::Description),
VerificationSink(verificationsink::Description),
}
impl BinDescription for SinkOnlyDescription {
type Bin = Box<dyn SinkOnlyBin>;
fn check_validity(
&self,
scope: &Scope,
getcal: &mut dyn GetCalibration,
) -> Result<()> {
use self::SinkOnlyDescription::*;
match *self {
JsonLSink(ref d) => d.check_validity(scope, getcal),
CborSink(ref d) => d.check_validity(scope, getcal),
VerificationSink(ref d) => d.check_validity(scope, getcal),
}
}
fn bin_type(&self) -> &'static str {
use self::SinkOnlyDescription::*;
match *self {
JsonLSink(ref d) => d.bin_type(),
CborSink(ref d) => d.bin_type(),
VerificationSink(ref d) => d.bin_type(),
}
}
}
impl SinkNames for SinkOnlyDescription {
fn sink_names(&self) -> IndexSet<String> {
use self::SinkOnlyDescription::*;
match *self {
JsonLSink(ref d) => d.sink_names(),
CborSink(ref d) => d.sink_names(),
VerificationSink(ref d) => d.sink_names(),
}
}
}
impl SinkOnlyBinDescription for SinkOnlyDescription {
fn build_bin(
&self,
scope: &Scope,
env: &mut dyn BinBuildEnvironment,
) -> Result<Self::Bin> {
use self::SinkOnlyDescription::*;
let b: Box<dyn SinkOnlyBin> = match *self {
JsonLSink(ref d) => Box::new(d.build_bin(scope, env)?),
CborSink(ref d) => Box::new(d.build_bin(scope, env)?),
VerificationSink(ref d) => Box::new(d.build_bin(scope, env)?),
};
Ok(b)
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", untagged)]
pub enum Description {
SourceOnly(SourceOnlyDescription),
SourceSink(SourceSinkDescription),
SinkOnly(SinkOnlyDescription),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct SourceReference {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub bin: Option<String>,
pub source: String,
}
impl BinDescription for SourceSinkDescription {
type Bin = Box<dyn SourceSinkBin>;
fn check_validity(
&self,
scope: &Scope,
getcal: &mut dyn GetCalibration,
) -> Result<()> {
use self::SourceSinkDescription::*;
match *self {
Add(ref d) => d.check_validity(scope, getcal),
Blocker(ref d) => d.check_validity(scope, getcal),
Calibration(ref d) => d.check_validity(scope, getcal),
Cosinus(ref d) => d.check_validity(scope, getcal),
Cumulation(ref d) => d.check_validity(scope, getcal),
Derivation(ref d) => d.check_validity(scope, getcal),
Divide(ref d) => d.check_validity(scope, getcal),
Fifo(ref d) => d.check_validity(scope, getcal),
FirstValue(ref d) => d.check_validity(scope, getcal),
FixedValues(ref d) => d.check_validity(scope, getcal),
GreaterThan(ref d) => d.check_validity(scope, getcal),
Invert(ref d) => d.check_validity(scope, getcal),
LastCalmPoint(ref d) => d.check_validity(scope, getcal),
LinearRegression(ref d) => d.check_validity(scope, getcal),
Maximum(ref d) => d.check_validity(scope, getcal),
Mean(ref d) => d.check_validity(scope, getcal),
Minimum(ref d) => d.check_validity(scope, getcal),
Multiplex(ref d) => d.check_validity(scope, getcal),
Multiply(ref d) => d.check_validity(scope, getcal),
Not(ref d) => d.check_validity(scope, getcal),
Pipeline(ref d) => d.check_validity(scope, getcal),
SingleNotNull(ref d) => d.check_validity(scope, getcal),
Sinus(ref d) => d.check_validity(scope, getcal),
Subtract(ref d) => d.check_validity(scope, getcal),
Storage(ref d) => d.check_validity(scope, getcal),
}
}
fn bin_type(&self) -> &'static str {
use self::SourceSinkDescription::*;
match *self {
Add(ref d) => d.bin_type(),
Blocker(ref d) => d.bin_type(),
Calibration(ref d) => d.bin_type(),
Cosinus(ref d) => d.bin_type(),
Cumulation(ref d) => d.bin_type(),
Derivation(ref d) => d.bin_type(),
Divide(ref d) => d.bin_type(),
Fifo(ref d) => d.bin_type(),
FirstValue(ref d) => d.bin_type(),
FixedValues(ref d) => d.bin_type(),
GreaterThan(ref d) => d.bin_type(),
Invert(ref d) => d.bin_type(),
LastCalmPoint(ref d) => d.bin_type(),
LinearRegression(ref d) => d.bin_type(),
Maximum(ref d) => d.bin_type(),
Mean(ref d) => d.bin_type(),
Minimum(ref d) => d.bin_type(),
Multiplex(ref d) => d.bin_type(),
Multiply(ref d) => d.bin_type(),
Not(ref d) => d.bin_type(),
Pipeline(ref d) => d.bin_type(),
SingleNotNull(ref d) => d.bin_type(),
Sinus(ref d) => d.bin_type(),
Subtract(ref d) => d.bin_type(),
Storage(ref d) => d.bin_type(),
}
}
}
impl WriteDot for SourceSinkDescription {
fn write_dot<W: Write>(&self, w: &mut W, name: &str) -> Result<()> {
use self::SourceSinkDescription::*;
match *self {
Add(ref d) => d.write_dot(w, name),
Blocker(ref d) => d.write_dot(w, name),
Calibration(ref d) => d.write_dot(w, name),
Cosinus(ref d) => d.write_dot(w, name),
Cumulation(ref d) => d.write_dot(w, name),
Derivation(ref d) => d.write_dot(w, name),
Divide(ref d) => d.write_dot(w, name),
Fifo(ref d) => d.write_dot(w, name),
FirstValue(ref d) => d.write_dot(w, name),
FixedValues(ref d) => d.write_dot(w, name),
GreaterThan(ref d) => d.write_dot(w, name),
Invert(ref d) => d.write_dot(w, name),
LastCalmPoint(ref d) => d.write_dot(w, name),
LinearRegression(ref d) => d.write_dot(w, name),
Maximum(ref d) => d.write_dot(w, name),
Mean(ref d) => d.write_dot(w, name),
Minimum(ref d) => d.write_dot(w, name),
Multiplex(ref d) => d.write_dot(w, name),
Multiply(ref d) => d.write_dot(w, name),
Not(ref d) => d.write_dot(w, name),
Pipeline(ref d) => d.write_dot(w, name),
SingleNotNull(ref d) => d.write_dot(w, name),
Sinus(ref d) => d.write_dot(w, name),
Subtract(ref d) => d.write_dot(w, name),
Storage(ref d) => d.write_dot(w, name),
}
}
}
impl SourceSinkBinDescription for SourceSinkDescription {
fn build_bin(
&self,
s: &Scope,
e: &mut dyn BinBuildEnvironment,
) -> Result<Self::Bin> {
use self::SourceSinkDescription::*;
Ok(match *self {
Add(ref d) => Box::new(d.build_bin(s, e)?),
Blocker(ref d) => Box::new(d.build_bin(s, e)?),
Calibration(ref d) => Box::new(d.build_bin(s, e)?),
Cosinus(ref d) => Box::new(d.build_bin(s, e)?),
Cumulation(ref d) => Box::new(d.build_bin(s, e)?),
Derivation(ref d) => Box::new(d.build_bin(s, e)?),
Divide(ref d) => Box::new(d.build_bin(s, e)?),
Fifo(ref d) => Box::new(d.build_bin(s, e)?),
FirstValue(ref d) => Box::new(d.build_bin(s, e)?),
FixedValues(ref d) => Box::new(d.build_bin(s, e)?),
GreaterThan(ref d) => Box::new(d.build_bin(s, e)?),
Invert(ref d) => Box::new(d.build_bin(s, e)?),
LastCalmPoint(ref d) => Box::new(d.build_bin(s, e)?),
LinearRegression(ref d) => Box::new(d.build_bin(s, e)?),
Maximum(ref d) => Box::new(d.build_bin(s, e)?),
Mean(ref d) => Box::new(d.build_bin(s, e)?),
Minimum(ref d) => Box::new(d.build_bin(s, e)?),
Multiplex(ref d) => Box::new(d.build_bin(s, e)?),
Multiply(ref d) => Box::new(d.build_bin(s, e)?),
Not(ref d) => Box::new(d.build_bin(s, e)?),
Pipeline(ref d) => Box::new(d.build_bin(s, e)?),
SingleNotNull(ref d) => Box::new(d.build_bin(s, e)?),
Sinus(ref d) => Box::new(d.build_bin(s, e)?),
Subtract(ref d) => Box::new(d.build_bin(s, e)?),
Storage(ref d) => Box::new(d.build_bin(s, e)?),
})
}
}
impl SinkNames for SourceSinkDescription {
fn sink_names(&self) -> IndexSet<String> {
use self::SourceSinkDescription::*;
match *self {
Add(ref d) => d.sink_names(),
Blocker(ref d) => d.sink_names(),
Calibration(ref d) => d.sink_names(),
Cosinus(ref d) => d.sink_names(),
Cumulation(ref d) => d.sink_names(),
Derivation(ref d) => d.sink_names(),
Divide(ref d) => d.sink_names(),
Fifo(ref d) => d.sink_names(),
FirstValue(ref d) => d.sink_names(),
FixedValues(ref d) => d.sink_names(),
GreaterThan(ref d) => d.sink_names(),
Invert(ref d) => d.sink_names(),
LastCalmPoint(ref d) => d.sink_names(),
LinearRegression(ref d) => d.sink_names(),
Maximum(ref d) => d.sink_names(),
Mean(ref d) => d.sink_names(),
Minimum(ref d) => d.sink_names(),
Multiplex(ref d) => d.sink_names(),
Multiply(ref d) => d.sink_names(),
Not(ref d) => d.sink_names(),
Pipeline(ref d) => d.sink_names(),
SingleNotNull(ref d) => d.sink_names(),
Sinus(ref d) => d.sink_names(),
Subtract(ref d) => d.sink_names(),
Storage(ref d) => d.sink_names(),
}
}
}
impl SourceNames for SourceSinkDescription {
fn source_names(&self) -> Result<IndexSet<String>> {
use self::SourceSinkDescription::*;
match *self {
Add(ref d) => d.source_names(),
Blocker(ref d) => d.source_names(),
Calibration(ref d) => d.source_names(),
Cosinus(ref d) => d.source_names(),
Cumulation(ref d) => d.source_names(),
Derivation(ref d) => d.source_names(),
Divide(ref d) => d.source_names(),
Fifo(ref d) => d.source_names(),
FirstValue(ref d) => d.source_names(),
FixedValues(ref d) => d.source_names(),
GreaterThan(ref d) => d.source_names(),
Invert(ref d) => d.source_names(),
LastCalmPoint(ref d) => d.source_names(),
LinearRegression(ref d) => d.source_names(),
Maximum(ref d) => d.source_names(),
Mean(ref d) => d.source_names(),
Minimum(ref d) => d.source_names(),
Multiplex(ref d) => d.source_names(),
Multiply(ref d) => d.source_names(),
Not(ref d) => d.source_names(),
Pipeline(ref d) => d.source_names(),
SingleNotNull(ref d) => d.source_names(),
Sinus(ref d) => d.source_names(),
Subtract(ref d) => d.source_names(),
Storage(ref d) => d.source_names(),
}
}
}
pub trait SourceOnlyBinDescription: SourceNames + BinDescription {
fn build_bin(&self, scope: &Scope) -> Result<Self::Bin>;
}
impl BinDescription for SourceOnlyDescription {
type Bin = Box<dyn SourceOnlyBin>;
fn check_validity(
&self,
scope: &Scope,
getcal: &mut dyn GetCalibration,
) -> Result<()> {
use self::SourceOnlyDescription::*;
match *self {
JsonLSource(ref d) => d.check_validity(scope, getcal),
DirectSource(ref d) => d.check_validity(scope, getcal),
}
}
fn bin_type(&self) -> &'static str {
use self::SourceOnlyDescription::*;
match *self {
JsonLSource(ref d) => d.bin_type(),
DirectSource(ref d) => d.bin_type(),
}
}
}
impl SourceNames for SourceOnlyDescription {
fn source_names(&self) -> Result<IndexSet<String>> {
use self::SourceOnlyDescription::*;
match *self {
JsonLSource(ref d) => d.source_names(),
DirectSource(ref d) => d.source_names(),
}
}
}
impl SourceOnlyBinDescription for SourceOnlyDescription {
fn build_bin(&self, scope: &Scope) -> Result<Self::Bin> {
use self::SourceOnlyDescription::*;
let b: Box<dyn SourceOnlyBin> = match *self {
JsonLSource(ref d) => Box::new(d.build_bin(scope)?),
DirectSource(ref d) => Box::new(d.build_bin(scope)?),
};
Ok(b)
}
}
pub trait SourceSinkBinDescription:
SourceNames + SinkNames + BinDescription
{
fn build_bin(
&self,
scope: &Scope,
env: &mut dyn BinBuildEnvironment,
) -> Result<Self::Bin>;
}
pub trait SinkOnlyBinDescription: SinkNames + BinDescription {
fn build_bin(
&self,
scope: &Scope,
env: &mut dyn BinBuildEnvironment,
) -> Result<Self::Bin>;
}
pub trait SinkNames {
fn sink_names(&self) -> IndexSet<String>;
fn check_sink_names(
&self,
scope: &Scope,
sink_names: &IndexSet<String>,
) -> Result<()> {
ensure!(
sink_names == &self.sink_names(),
error::InvalidSinkNames {
scope: scope.clone(),
expected: self.sink_names().clone(),
received: sink_names.clone(),
}
);
Ok(())
}
}
pub trait SinkBinDescription: BinDescription + SinkNames {}
impl<T: BinDescription + SinkNames> SinkBinDescription for T {}
pub trait SourceBinDescription: BinDescription + SourceNames {}
impl<T: BinDescription + SourceNames> SourceBinDescription for T {}
pub trait SourceNames {
fn source_names(&self) -> Result<IndexSet<String>>;
}
pub trait BinDescription {
type Bin;
fn check_validity(
&self,
scope: &Scope,
get_calibration: &mut dyn GetCalibration,
) -> Result<()>;
fn bin_type(&self) -> &'static str;
}
pub trait WriteDot {
fn write_dot<W: Write>(&self, w: &mut W, name: &str) -> Result<()>;
}
pub trait WriteDotSimple {}
impl<T: SourceNames + SinkNames + BinDescription + WriteDotSimple> WriteDot
for T
{
fn write_dot<W: Write>(&self, w: &mut W, name: &str) -> Result<()> {
write_dot_bin(
w,
name,
self.bin_type(),
&self.sink_names(),
&self.source_names()?,
)
}
}
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Iteration {
pub index: usize,
}
impl Iteration {
pub fn iterate(&mut self) {
self.index = self.index.checked_add(1usize).unwrap();
}
}
#[derive(Debug, PartialEq)]
pub struct SourceId {
pub id: String,
}
impl SourceId {
pub fn new(id: &str) -> Self {
SourceId { id: id.to_string() }
}
}
pub trait FetchItem: Debug {
fn fetch_item(&self, scope: &Scope) -> Result<Item>;
}
#[derive(Debug)]
pub struct DataSource<T: SourceBin> {
pub data_provider: Weak<RwLock<T>>,
pub source: SourceId,
}
impl<T: SourceBin> FetchItem for DataSource<T> {
fn fetch_item(&self, scope: &Scope) -> Result<Item> {
let provider_mut = self.data_provider.upgrade().context(
error::DataProviderGone {
scope: scope.clone(),
},
)?;
let provider = provider_mut.write().unwrap();
provider.get_source_data(&self.source)
}
}
trait CheckContains {
fn check_contains(
&self,
scope: &Scope,
bin_type: &str,
value: &str,
) -> Result<()>;
}
impl CheckContains for IndexSet<String> {
fn check_contains(
&self,
scope: &Scope,
bin_type: &str,
value: &str,
) -> Result<()> {
ensure!(
self.contains(&value.to_string()),
error::InvalidSourceName {
bin_type: bin_type.to_string(),
name: format!("{:?}", value),
scope: scope.clone(),
}
);
Ok(())
}
}
fn write_dot_bin<W: Write>(
w: &mut W,
name: &str,
bin_type: &str,
sinks: &IndexSet<String>,
sources: &IndexSet<String>,
) -> Result<()> {
writeln!(w, "{} [label=<", name).context(error::Io)?;
write_dot_bin_table(w, name, bin_type, sinks, sources)?;
writeln!(w, ">];").context(error::Io)?;
Ok(())
}
fn write_dot_bin_table<W: Write>(
w: &mut W,
name: &str,
bin_type: &str,
sinks: &IndexSet<String>,
sources: &IndexSet<String>,
) -> Result<()> {
let cols = match (sinks.is_empty(), sources.is_empty()) {
(false, false) => 2,
(true, false) | (false, true) => 1,
(true, true) => 0,
};
writeln!(w, "<table border=\"0\" cellborder=\"1\" cellspacing=\"0\">")
.context(error::Io)?;
writeln!(w, "<tr>").context(error::Io)?;
writeln!(w, "<td colspan=\"{}\">", cols).context(error::Io)?;
writeln!(
w,
"<font point-size=\"12\">{}</font><br/>{}",
name, bin_type
)
.context(error::Io)?;
writeln!(w, "</td>").context(error::Io)?;
writeln!(w, "</tr>").context(error::Io)?;
if cols > 0 {
writeln!(w, "<tr>").context(error::Io)?;
if !sinks.is_empty() {
writeln!(w, "<td>").context(error::Io)?;
write_dot_socket_table(w, sinks, "_sink")?;
writeln!(w, "</td>").context(error::Io)?;
}
if !sources.is_empty() {
writeln!(w, "<td>").context(error::Io)?;
write_dot_socket_table(w, sources, "_source")?;
writeln!(w, "</td>").context(error::Io)?;
}
writeln!(w, "</tr>").context(error::Io)?;
}
writeln!(w, "</table>").context(error::Io)?;
Ok(())
}
fn write_dot_socket_table<W: Write>(
w: &mut W,
items: &IndexSet<String>,
suffix: &str,
) -> Result<()> {
writeln!(w, "<table border=\"0\" cellborder=\"0\" cellspacing=\"0\">")
.context(error::Io)?;
for item in items {
writeln!(w, "<tr>").context(error::Io)?;
writeln!(w, "<td port=\"{}{}\">{}</td>", item, suffix, item)
.context(error::Io)?;
writeln!(w, "</tr>").context(error::Io)?;
}
writeln!(w, "</table>").context(error::Io)?;
Ok(())
}
pub trait BinBuildEnvironment: GetCalibration {
fn resolve(&mut self, id: &str) -> Result<Box<dyn FetchItem>>;
}
static SINK_A: &str = "a";
static SINK_B: &str = "b";
static SINK_INPUT: &str = "input";
static SINK_SELECT: &str = "select";
static SINK_TRIGGER: &str = "trigger";
static SINK_X: &str = "x";
static SINK_Y: &str = "y";
static SOURCE_OUTPUT: &str = "output";
static SOURCE_CHANGED: &str = "changed";
fn build_names<S: ToString>(names: &[S]) -> IndexSet<String> {
names.iter().map(S::to_string).collect()
}
fn sink_names_a_b() -> IndexSet<String> {
build_names(&[SINK_A, SINK_B])
}
fn sink_names_input_trigger() -> IndexSet<String> {
build_names(&[SINK_INPUT, SINK_TRIGGER])
}
fn sink_names_trigger() -> IndexSet<String> {
build_names(&[SINK_TRIGGER])
}
fn sink_names_input_select() -> IndexSet<String> {
build_names(&[SINK_INPUT, SINK_SELECT])
}
fn sink_names_input() -> IndexSet<String> {
build_names(&[SINK_INPUT])
}
fn source_names_output() -> IndexSet<String> {
build_names(&[SOURCE_OUTPUT])
}
fn source_names_output_changed() -> IndexSet<String> {
build_names(&[SOURCE_OUTPUT, &SOURCE_CHANGED])
}