use std::io::{Read, Write};
use ytsaurus_format::DataFormat;
use ytsaurus_skiff::Value;
use crate::{
Event, JobError, JobReader, JobWriter, Result, SkiffJobReader, SkiffJobWriter, SkiffRow,
TableId,
};
#[derive(Debug)]
pub enum WorkerReader<R> {
Yson(JobReader<R>),
Skiff(SkiffJobReader<R>),
}
#[derive(Debug)]
pub enum WorkerEvent<'input> {
Yson(Event<'input>),
Skiff(SkiffRow),
}
impl WorkerReader<std::io::BufReader<std::io::Stdin>> {
pub fn from_stdin(format: DataFormat) -> Result<Self> {
Self::new(
std::io::BufReader::with_capacity(crate::skiff::STDIN_BUFFER_BYTES, std::io::stdin()),
format,
)
}
}
impl<R: Read> WorkerReader<R> {
pub fn new(input: R, format: DataFormat) -> Result<Self> {
match format {
DataFormat::Yson(format) => Ok(Self::Yson(JobReader::with_format(input, format))),
DataFormat::Skiff(format) => Ok(Self::Skiff(SkiffJobReader::new(input, format)?)),
_ => Err(JobError::UnsupportedDataFormat),
}
}
pub fn next_event(&mut self) -> Result<Option<WorkerEvent<'_>>> {
match self {
Self::Yson(reader) => reader
.next_event()
.map(|event| event.map(WorkerEvent::Yson)),
Self::Skiff(reader) => reader.next_row().map(|row| row.map(WorkerEvent::Skiff)),
}
}
}
pub enum WorkerRow<'row> {
YsonRaw(&'row [u8]),
Skiff(&'row Value),
}
pub enum WorkerWriter {
Yson(JobWriter),
Skiff(SkiffJobWriter),
}
impl std::fmt::Debug for WorkerWriter {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Yson(writer) => formatter
.debug_tuple("WorkerWriter::Yson")
.field(writer)
.finish(),
Self::Skiff(writer) => formatter
.debug_tuple("WorkerWriter::Skiff")
.field(writer)
.finish(),
}
}
}
impl WorkerWriter {
#[cfg(unix)]
pub fn descriptors(format: DataFormat, table_count: usize) -> Result<Self> {
match format {
DataFormat::Yson(format) => {
JobWriter::descriptors_with_format(table_count, format).map(Self::Yson)
}
DataFormat::Skiff(format) => {
let schemas = format.table_schemas().len();
if schemas != table_count {
return Err(JobError::SkiffOutputSchemaCount {
sinks: table_count,
schemas,
});
}
SkiffJobWriter::descriptors(format).map(Self::Skiff)
}
_ => Err(JobError::UnsupportedDataFormat),
}
}
pub fn from_writers(tables: Vec<Box<dyn Write>>, format: DataFormat) -> Result<Self> {
match format {
DataFormat::Yson(format) => Ok(Self::Yson(JobWriter::from_writers(tables, format))),
DataFormat::Skiff(format) => {
SkiffJobWriter::from_writers(tables, format).map(Self::Skiff)
}
_ => Err(JobError::UnsupportedDataFormat),
}
}
#[must_use]
pub fn table_count(&self) -> usize {
match self {
Self::Yson(writer) => writer.table_count(),
Self::Skiff(writer) => writer.table_count(),
}
}
pub fn write(&mut self, table: impl Into<TableId>, row: WorkerRow<'_>) -> Result<()> {
match (self, row) {
(Self::Yson(writer), WorkerRow::YsonRaw(row)) => writer.write_raw(table, row),
(Self::Skiff(writer), WorkerRow::Skiff(row)) => writer.write(table, row),
(Self::Yson(_), WorkerRow::Skiff(_)) => Err(JobError::WorkerRowFormatMismatch {
writer: "YSON",
row: "Skiff",
}),
(Self::Skiff(_), WorkerRow::YsonRaw(_)) => Err(JobError::WorkerRowFormatMismatch {
writer: "Skiff",
row: "YSON",
}),
}
}
pub fn flush(&mut self) -> Result<()> {
match self {
Self::Yson(writer) => writer.flush(),
Self::Skiff(writer) => writer.flush(),
}
}
pub fn finish(&mut self) -> Result<()> {
match self {
Self::Yson(writer) => writer.finish(),
Self::Skiff(writer) => writer.finish(),
}
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use ytsaurus_format::SkiffFormat;
use ytsaurus_skiff::{Encoder, Schema, SchemaRef, Value, WireType};
use super::*;
fn skiff_format() -> SkiffFormat {
SkiffFormat::new(vec![SchemaRef::Inline(Schema::tuple([Schema::named(
"value",
WireType::String32,
)]))])
.unwrap()
}
#[test]
fn reader_selects_yson_and_skiff_from_the_same_enum() {
let mut yson =
WorkerReader::new(Cursor::new(b"{value=one};"), DataFormat::text_yson()).unwrap();
assert!(matches!(
yson.next_event().unwrap(),
Some(WorkerEvent::Yson(_))
));
let schema = skiff_format().table_schema(0).unwrap().clone();
let mut encoder = Encoder::new(Vec::new(), schema).unwrap();
encoder
.write(&Value::Tuple(vec![Value::Bytes(b"one".to_vec())]))
.unwrap();
let stream = encoder.into_inner().unwrap();
let mut skiff =
WorkerReader::new(Cursor::new(stream), DataFormat::skiff(skiff_format())).unwrap();
assert!(matches!(
skiff.next_event().unwrap(),
Some(WorkerEvent::Skiff(_))
));
}
#[test]
fn writer_rejects_a_row_from_the_other_format() {
let mut writer =
WorkerWriter::from_writers(vec![Box::new(Vec::new())], DataFormat::binary_yson())
.unwrap();
let error = writer
.write(0, WorkerRow::Skiff(&Value::Tuple(Vec::new())))
.unwrap_err();
assert_eq!(error.kind(), "worker_row_format_mismatch");
}
}