use serde_json;
use super::{
BinBuildEnvironment, BinDescription, Calculator, FetchItem,
GetCalibration, Item, Iteration, Result, Scope, SinkBin, SinkNames,
SinkOnlyBin, SinkOnlyBinDescription,
};
use crate::error;
use indexmap::{IndexMap, IndexSet};
use snafu::ResultExt;
use std::fs::File;
use std::io::Stdout;
use std::io::{LineWriter, Write};
static BIN_TYPE: &str = "jsonlsink";
#[derive(Debug)]
pub struct Bin {
scope: Scope,
output: Output,
sources: IndexMap<String, Box<dyn FetchItem>>,
}
#[derive(Debug)]
enum Output {
File(LineWriter<File>),
Stdout(LineWriter<Stdout>),
}
impl Output {
fn writer(&mut self) -> &mut dyn Write {
match self {
Output::File(w) => w,
Output::Stdout(w) => w,
}
}
}
impl SinkBin for Bin {}
impl Calculator for Bin {
fn calculate(&mut self, _iteration: &Iteration) -> Result<()> {
let items = self
.sources
.iter()
.map(|(s, ds)| {
ds.fetch_item(&self.scope).map(|d| (s.to_string(), d))
})
.collect::<Result<IndexMap<String, Item>>>()?;
writeln!(
self.output.writer(),
"{}",
serde_json::to_string(&items.values().collect::<Vec<_>>())
.context(error::SerdeJson)?
)
.context(error::Io)?;
Ok(())
}
}
impl SinkOnlyBin for Bin {}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Description {
pub sinks: IndexSet<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub file_path: Option<String>,
}
impl BinDescription for Description {
type Bin = Bin;
fn check_validity(
&self,
_scope: &Scope,
_get_calibration: &mut dyn GetCalibration,
) -> Result<()> {
Ok(())
}
fn bin_type(&self) -> &'static str {
BIN_TYPE
}
}
impl SinkNames for Description {
fn sink_names(&self) -> IndexSet<String> {
self.sinks.clone()
}
}
impl SinkOnlyBinDescription for Description {
fn build_bin(
&self,
scope: &Scope,
env: &mut dyn BinBuildEnvironment,
) -> Result<Self::Bin> {
let mut output = if let Some(ref path) = self.file_path {
let file =
File::create(&path.to_string()).context(error::Io)?;
Output::File(LineWriter::new(file))
} else {
Output::Stdout(LineWriter::new(std::io::stdout()))
};
writeln!(
output.writer(),
"{}",
serde_json::to_string(&self.sinks)
.context(error::SerdeJson)?
)
.context(error::Io)?;
Ok(Bin {
scope: scope.clone(),
output,
sources: self
.sinks
.iter()
.map(|s| env.resolve(s).map(|ds| (s.to_string(), ds)))
.collect::<Result<IndexMap<_, _>>>()?,
})
}
}