Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/src/kash/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum InputFormat {
Toml,
Json,
Camt053,
Ktf,
}

/// command-line interface to kash
Expand Down
2 changes: 2 additions & 0 deletions cli/src/kash/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod args;
use self::args::{Args, InputFormat};
use clap::Parser;
use kash_cli::output::OutputOptions;
use kash_convert::input::ktf::KtfInput;
use kash_convert::input::toml::TomlInput;
use kash_convert::input::{camt053::Camt053Input, json::JsonInput, Input};
use std::fs::File;
Expand All @@ -29,6 +30,7 @@ fn main() {
InputFormat::Json => JsonInput::new().from_read(reader),
InputFormat::Toml => TomlInput::new().from_read(reader),
InputFormat::Camt053 => Camt053Input::new().from_read(reader),
InputFormat::Ktf => KtfInput::new().from_read(reader),
}
.unwrap(),
);
Expand Down
5 changes: 4 additions & 1 deletion convert/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ serde_json = { version = "1.0.82", optional = true }
toml = { version = "0.5.9", optional = true }
kash = { path = "../lib" }
camt053 = { path = "../camt053", optional = true }
ktf = { path = "../ktf", optional = true }

[dependencies.quick-xml]
version = "0.23.0"
Expand All @@ -27,11 +28,13 @@ all = ["all-inputs", "all-outputs"]
all-inputs = [
"input-json",
"input-toml",
"input-camt053"
"input-camt053",
"input-ktf"
]
all-outputs = ["output-json"]

input-json = ["dep:serde_json"]
input-toml = ["dep:toml"]
input-camt053 = ["dep:camt053", "dep:quick-xml", "dep:chrono"]
input-ktf = ["dep:ktf"]
output-json = ["dep:serde_json"]
50 changes: 50 additions & 0 deletions convert/src/input/ktf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use super::{Input, InputError};
use kash::statements::Statement;
use serde::Deserialize;
use std::io::Read;

pub struct KtfInput;

impl KtfInput {
pub fn new() -> Self {
Self
}
}

#[derive(Debug, Deserialize)]
#[serde(tag = "type", rename_all = "camelCase")]
pub enum KtfInputData {
Test(TestData),
}

#[derive(Debug, Deserialize)]
pub struct TestData {
pub a: i32,
pub b: i32,
}

impl Input for KtfInput {
fn from_read<R>(&self, mut reader: R) -> Result<Vec<Statement>, InputError>
where
R: Read,
{
let statements = Vec::new();

// FIXME: actually implement from_read instead of this
// memory-hogging garbage.
let input_data = ktf::from_str::<Vec<KtfInputData>>(
{
let mut input = String::new();
reader
.read_to_string(&mut input)
.map_err(|_| InputError::Read)?;
input
}
.as_str(),
)
.map_err(|e| InputError::Invalid(e.to_string()))?;
println!("{:#?}", input_data);

Ok(statements)
}
}
2 changes: 2 additions & 0 deletions convert/src/input/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
pub mod camt053;
#[cfg(feature = "input-json")]
pub mod json;
#[cfg(feature = "input-ktf")]
pub mod ktf;
#[cfg(feature = "input-toml")]
pub mod toml;

Expand Down
12 changes: 12 additions & 0 deletions ktf/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "ktf"
version = "0.1.0"
edition = "2021"

[dependencies]
serde = { version = "1.0.139", optional = true }
kash = { path = "../lib" }

[features]
default = ["serde"]
serde = ["dep:serde"]
5 changes: 5 additions & 0 deletions ktf/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# ktf

**k**ash **t**able **f**ormat (_ktf_ for short) is an experimental table-like
data format.
It is designed for but not limited to interacting with kash and has support for Serde.
108 changes: 108 additions & 0 deletions ktf/src/de.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
use super::error::{Error, Result};

#[derive(Debug)]
pub struct Deserializer<'a> {
input: &'a str,
pub col_index: usize,
pub header: Vec<String>,
pub row: Row,
}

impl<'a> Deserializer<'a> {
pub fn from_str(input: &'a str) -> Self {
Deserializer {
input,
col_index: 0,
header: vec![],
row: Row {
cols: vec![],
len: 0,
},
}
}

pub fn advance(&mut self, n: usize) {
self.input = &self.input[n..];
}

pub fn peek_char(&self) -> Result<char> {
self.input.chars().next().ok_or(Error::Eof)
}

pub fn next_char(&mut self) -> Result<char> {
let c = self.peek_char()?;
self.advance(c.len_utf8());
Ok(c)
}

pub fn next_row(&mut self) -> Result<Row> {
let row = self.peek_row()?;
self.advance(row.len + 1);
self.row = row.clone();
self.col_index = 0;
Ok(row)
}

pub fn peek_row(&self) -> Result<Row> {
let row = self.input.lines().next().ok_or(Error::Eof)?;
Ok(Row::new(row))
}

pub fn peek_key(&self) -> Result<String> {
self.header
.iter()
.nth(self.col_index)
.map(String::to_owned)
.ok_or(Error::MapEnd)
}

pub fn peek_value(&self) -> Result<String> {
self.row
.cols
.iter()
.nth(self.col_index)
.map(String::to_owned)
.ok_or(Error::ExpectedMapValue)
}

pub fn next_key(&mut self) -> Result<String> {
let col = self.peek_key()?;
self.col_index += 1;
Ok(col)
}

pub fn parse_header(&mut self) -> Result<Vec<String>> {
let header: Vec<String> = match self.next_char()? {
'>' => Ok(self.next_row()?.cols.iter().map(String::to_owned).collect()),
_ => Err(Error::ExpectedHeader),
}?;

self.header = header.clone();
Ok(header)
}

#[inline]
pub fn parse_f32(&self) -> Result<f32> {
self.peek_value()?.parse().map_err(|_| Error::ExpectedFloat)
}

#[inline]
pub fn parse_string(&self) -> Result<String> {
self.peek_value()
}
}

#[derive(Debug, Clone)]
pub struct Row {
pub cols: Vec<String>,
pub len: usize,
}

impl Row {
pub fn new(row: &str) -> Self {
Self {
cols: row.split('|').map(str::trim).map(str::to_owned).collect(),
len: row.len(),
}
}
}
33 changes: 33 additions & 0 deletions ktf/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use std::fmt::Display;
use std::{error, result};

pub type Result<T> = result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
Message(String),
ExpectedHeader,
ExpectedFloat,
ExpectedMap,
ExpectedMapValue,
MapEnd,
Syntax,
Eof,
}

impl error::Error for Error {}

impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
Error::Message(m) => m,
Error::ExpectedHeader => "expected header",
Error::ExpectedFloat => "expected float",
Error::ExpectedMap => "expected map",
Error::ExpectedMapValue => "expected map value",
Error::MapEnd => "unexpected end of map",
Error::Syntax => "syntax error",
Error::Eof => "unexpected EOF",
})
}
}
29 changes: 29 additions & 0 deletions ktf/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
pub mod de;
pub mod error;

#[cfg(feature = "serde")]
mod serde;

#[cfg(feature = "serde")]
pub use self::serde::*;

#[cfg(test)]
mod tests {
use super::de::Deserializer;
use super::error::Result;

#[test]
fn de_header_single_col() -> Result<()> {
let mut des = Deserializer::from_str(">col1\n");
Ok(assert_eq!(vec!["col1"], des.parse_header()?))
}

#[test]
fn de_header_multi_col() -> Result<()> {
let mut des = Deserializer::from_str(">col1|col2|col3\n");
Ok(assert_eq!(
vec!["col1", "col2", "col3"],
des.parse_header()?
))
}
}
Loading