From f2ff1229202e7ab26017fb527dac9907d83abb0a Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Fri, 22 Jul 2022 11:52:59 +0200 Subject: [PATCH 01/18] chore(ktf): add serde to deps --- Cargo.lock | 1 + ktf/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 457095d..e80e3af 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -479,6 +479,7 @@ name = "ktf" version = "0.1.0" dependencies = [ "kash", + "serde", ] [[package]] diff --git a/ktf/Cargo.toml b/ktf/Cargo.toml index 01879c9..d609839 100644 --- a/ktf/Cargo.toml +++ b/ktf/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] +serde = { version = "1.0.139" } kash = { path = "../lib" } From 766d1247844710c79687879fbaf2ee4d8c2bda86 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Fri, 22 Jul 2022 11:53:57 +0200 Subject: [PATCH 02/18] feat(ktf): add serde deser skeleton (WIP) --- convert/src/input/ktf.rs | 25 ++++++++----- ktf/src/lib.rs | 2 ++ ktf/src/serde/de.rs | 78 ++++++++++++++++++++++++++++++++++++++++ ktf/src/serde/error.rs | 34 ++++++++++++++++++ ktf/src/serde/mod.rs | 5 +++ 5 files changed, 135 insertions(+), 9 deletions(-) create mode 100644 ktf/src/serde/de.rs create mode 100644 ktf/src/serde/error.rs create mode 100644 ktf/src/serde/mod.rs diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index a1195fd..41b40bf 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -1,7 +1,7 @@ use super::{Input, InputError}; use kash::statements::Statement; -use ktf::Deserializer; -use std::io::{BufRead, BufReader}; +use serde::Deserialize; +use std::io::{BufRead, BufReader, Read}; pub struct KtfInput; @@ -11,12 +11,17 @@ impl KtfInput { } } +#[derive(Debug, Deserialize)] +pub struct KtfInputData { + pub test: f32, +} + impl Input for KtfInput { fn from_read(&self, reader: R) -> Result, InputError> where - R: std::io::Read, + R: Read, { - let mut statements = Vec::new(); + let statements = Vec::new(); let buf = BufReader::new(reader); for ln in buf.lines() { @@ -24,11 +29,13 @@ impl Input for KtfInput { return Err(InputError::Read); } - let statement = Deserializer::from_str(&ln.unwrap()).deserialize(); - match statement { - Ok(s) => statements.push(s), - Err(e) => return Err(InputError::Invalid(e.to_string())), - } + let input_data = ktf::serde::from_str::(&ln.unwrap()).unwrap(); + println!("{:#?}", input_data); + + // match statement { + // Ok(s) => statements.push(s), + // Err(e) => return Err(InputError::Invalid(e.to_string())), + // } } Ok(statements) diff --git a/ktf/src/lib.rs b/ktf/src/lib.rs index a2bbe58..cf3ca50 100644 --- a/ktf/src/lib.rs +++ b/ktf/src/lib.rs @@ -1,3 +1,5 @@ +pub mod serde; + use kash::{ statements::{fixed::FixedExpense, income::Income, Statement}, value::MonthValues, diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs new file mode 100644 index 0000000..f1f4b37 --- /dev/null +++ b/ktf/src/serde/de.rs @@ -0,0 +1,78 @@ +use super::{Error, Result}; +use serde::de::{self, Deserialize, Visitor}; +use serde::forward_to_deserialize_any; + +pub fn from_str<'a, T>(s: &'a str) -> Result +where + T: Deserialize<'a>, +{ + let mut deserializer = Deserializer::from_str(s); + let t = T::deserialize(&mut deserializer)?; + Ok(t) +} + +pub struct Deserializer<'de> { + input: &'de str, +} + +impl<'de> Deserializer<'de> { + pub fn from_str(input: &'de str) -> Self { + Deserializer { input } + } + + fn peek_char(&mut self) -> Result { + self.input.chars().next().ok_or(Error::Eof) + } + + fn next_char(&mut self) -> Result { + let c = self.peek_char()?; + self.input = &self.input[c.len_utf8()..]; + Ok(c) + } + + pub fn parse_f32(&self) -> Result { + match self.input.parse() { + Ok(val) => Ok(val), + Err(_) => Err(Error::ExpectedFloat), + } + } +} + +impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { + type Error = Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self.peek_char()? { + '0'..='9' => self.deserialize_f32(visitor), + '|' => self.deserialize_map(visitor), + _ => Err(Error::Syntax), + } + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_f32(self.parse_f32()?) + } + + fn deserialize_map(self, _visitor: V) -> Result + where + V: Visitor<'de>, + { + if self.next_char()? == '|' { + Err(Error::Message("WIP".into())) + } else { + Err(Error::ExpectedMap) + } + } + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct struct enum identifier ignored_any + } +} diff --git a/ktf/src/serde/error.rs b/ktf/src/serde/error.rs new file mode 100644 index 0000000..71db07f --- /dev/null +++ b/ktf/src/serde/error.rs @@ -0,0 +1,34 @@ +use serde::de; +use std::fmt::Display; +use std::{error, result}; + +pub type Result = result::Result; + +#[derive(Debug)] +pub enum Error { + Message(String), + ExpectedFloat, + ExpectedMap, + Syntax, + Eof, +} + +impl error::Error for Error {} + +impl de::Error for Error { + fn custom(msg: T) -> Self { + Error::Message(msg.to_string()) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::Message(m) => f.write_str(m), + Error::ExpectedFloat => f.write_str("expected float"), + Error::ExpectedMap => f.write_str("expected map"), + Error::Syntax => f.write_str("syntax error"), + Error::Eof => f.write_str("unexpected EOF"), + } + } +} diff --git a/ktf/src/serde/mod.rs b/ktf/src/serde/mod.rs new file mode 100644 index 0000000..982d03d --- /dev/null +++ b/ktf/src/serde/mod.rs @@ -0,0 +1,5 @@ +mod de; +mod error; + +pub use de::{from_str, Deserializer}; +pub use error::*; From 78dba44ea1b1589190b31c026de7597a530eaa51 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Sat, 23 Jul 2022 11:48:56 +0200 Subject: [PATCH 03/18] feat(ktf): add StructMap --- ktf/src/serde/de.rs | 46 +++++++++++++++++++++++++++++++++++++++--- ktf/src/serde/error.rs | 16 ++++++++------- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index f1f4b37..33b1650 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -1,5 +1,5 @@ use super::{Error, Result}; -use serde::de::{self, Deserialize, Visitor}; +use serde::de::{self, Deserialize, MapAccess, Visitor}; use serde::forward_to_deserialize_any; pub fn from_str<'a, T>(s: &'a str) -> Result @@ -59,12 +59,13 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor.visit_f32(self.parse_f32()?) } - fn deserialize_map(self, _visitor: V) -> Result + fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, { if self.next_char()? == '|' { - Err(Error::Message("WIP".into())) + let val = visitor.visit_map(StructMap::new(self)); + Ok(val?) } else { Err(Error::ExpectedMap) } @@ -76,3 +77,42 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { tuple_struct struct enum identifier ignored_any } } + +pub struct StructMap<'a, 'de> { + de: &'a mut Deserializer<'de>, + first: bool, +} + +impl<'a, 'de> StructMap<'a, 'de> { + pub fn new(de: &'a mut Deserializer<'de>) -> Self { + Self { de, first: true } + } +} + +impl<'a, 'de> MapAccess<'de> for StructMap<'a, 'de> { + type Error = Error; + + fn next_key_seed(&mut self, seed: K) -> Result> + where + K: de::DeserializeSeed<'de>, + { + if let Err(Error::Eof) = self.de.peek_char() { + return Ok(None); + } + + if !self.first && self.de.next_char()? != '|' { + return Err(Error::ExpectedMapValue); + } + + self.first = false; + + seed.deserialize(&mut *self.de).map(Some) + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: de::DeserializeSeed<'de>, + { + seed.deserialize(&mut *self.de) + } +} diff --git a/ktf/src/serde/error.rs b/ktf/src/serde/error.rs index 71db07f..0fc84d1 100644 --- a/ktf/src/serde/error.rs +++ b/ktf/src/serde/error.rs @@ -9,6 +9,7 @@ pub enum Error { Message(String), ExpectedFloat, ExpectedMap, + ExpectedMapValue, Syntax, Eof, } @@ -23,12 +24,13 @@ impl de::Error for Error { impl Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Error::Message(m) => f.write_str(m), - Error::ExpectedFloat => f.write_str("expected float"), - Error::ExpectedMap => f.write_str("expected map"), - Error::Syntax => f.write_str("syntax error"), - Error::Eof => f.write_str("unexpected EOF"), - } + f.write_str(match self { + Error::Message(m) => m, + Error::ExpectedFloat => "expected float", + Error::ExpectedMap => "expected map", + Error::ExpectedMapValue => "expected map value", + Error::Syntax => "syntax error", + Error::Eof => "unexpected EOF", + }) } } From 91fc424149599c178dde9f2488b5cc1e9016c5e8 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Mon, 25 Jul 2022 14:56:45 +0200 Subject: [PATCH 04/18] feat(ktf): add ExpectedHeader error variant --- ktf/src/serde/error.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ktf/src/serde/error.rs b/ktf/src/serde/error.rs index 0fc84d1..ff6597f 100644 --- a/ktf/src/serde/error.rs +++ b/ktf/src/serde/error.rs @@ -7,6 +7,7 @@ pub type Result = result::Result; #[derive(Debug)] pub enum Error { Message(String), + ExpectedHeader, ExpectedFloat, ExpectedMap, ExpectedMapValue, @@ -26,6 +27,7 @@ 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", From 1c348bc585dfb769b522731c65c5c94b81ed97f9 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Mon, 25 Jul 2022 17:07:13 +0200 Subject: [PATCH 05/18] refactor(ktf): decouple from serde --- convert/src/input/ktf.rs | 3 +- ktf/Cargo.toml | 6 +++- ktf/src/de.rs | 28 ++++++++++++++++++ ktf/src/error.rs | 31 ++++++++++++++++++++ ktf/src/lib.rs | 61 ++++------------------------------------ ktf/src/serde/de.rs | 32 ++------------------- ktf/src/serde/error.rs | 31 +------------------- ktf/src/serde/mod.rs | 2 +- 8 files changed, 77 insertions(+), 117 deletions(-) create mode 100644 ktf/src/de.rs create mode 100644 ktf/src/error.rs diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 41b40bf..25be66a 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -29,7 +29,8 @@ impl Input for KtfInput { return Err(InputError::Read); } - let input_data = ktf::serde::from_str::(&ln.unwrap()).unwrap(); + let input_data = ktf::from_str::(&ln.unwrap()) + .map_err(|e| InputError::Invalid(e.to_string()))?; println!("{:#?}", input_data); // match statement { diff --git a/ktf/Cargo.toml b/ktf/Cargo.toml index d609839..24d1d7b 100644 --- a/ktf/Cargo.toml +++ b/ktf/Cargo.toml @@ -4,5 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -serde = { version = "1.0.139" } +serde = { version = "1.0.139", optional = true } kash = { path = "../lib" } + +[features] +default = ["serde"] +serde = ["dep:serde"] diff --git a/ktf/src/de.rs b/ktf/src/de.rs new file mode 100644 index 0000000..bfdf605 --- /dev/null +++ b/ktf/src/de.rs @@ -0,0 +1,28 @@ +use super::error::{Error, Result}; + +pub struct Deserializer<'a> { + input: &'a str, +} + +impl<'a> Deserializer<'a> { + pub fn from_str(input: &'a str) -> Self { + Deserializer { input } + } + + pub fn peek_char(&mut self) -> Result { + self.input.chars().next().ok_or(Error::Eof) + } + + pub fn next_char(&mut self) -> Result { + let c = self.peek_char()?; + self.input = &self.input[c.len_utf8()..]; + Ok(c) + } + + pub fn parse_f32(&self) -> Result { + match self.input.parse() { + Ok(val) => Ok(val), + Err(_) => Err(Error::ExpectedFloat), + } + } +} diff --git a/ktf/src/error.rs b/ktf/src/error.rs new file mode 100644 index 0000000..06a6c06 --- /dev/null +++ b/ktf/src/error.rs @@ -0,0 +1,31 @@ +use std::fmt::Display; +use std::{error, result}; + +pub type Result = result::Result; + +#[derive(Debug)] +pub enum Error { + Message(String), + ExpectedHeader, + ExpectedFloat, + ExpectedMap, + ExpectedMapValue, + 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::Syntax => "syntax error", + Error::Eof => "unexpected EOF", + }) + } +} diff --git a/ktf/src/lib.rs b/ktf/src/lib.rs index cf3ca50..6331ded 100644 --- a/ktf/src/lib.rs +++ b/ktf/src/lib.rs @@ -1,57 +1,8 @@ -pub mod serde; +pub mod de; +pub mod error; -use kash::{ - statements::{fixed::FixedExpense, income::Income, Statement}, - value::MonthValues, -}; -use std::fmt::{self, Display}; +#[cfg(feature = "serde")] +mod serde; -#[derive(Debug)] -pub enum ParseError { - NoSuchType, -} - -impl Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::NoSuchType => "no such type", - } - ) - } -} - -pub struct Deserializer<'a> { - input: &'a str, -} - -impl<'a> Deserializer<'a> { - pub fn from_str(input: &'a str) -> Self { - Deserializer { input } - } - - // TODO: refactor this ugly bodged mess, use Serde for this ASAP - pub fn deserialize(&self) -> Result { - let mut cols = self.input.split('|').map(|s| s.trim()); - - match cols.next().unwrap().chars().nth(0).unwrap_or('#') { - 'f' => Ok(Statement::Fixed(FixedExpense { - tag: cols.next().unwrap_or_default().to_string(), - description: cols.next().unwrap_or_default().to_string(), - expenses: self.deserialize_mv(cols.next().unwrap_or_default()), - })), - 'i' => Ok(Statement::Income(Income { - description: cols.next().unwrap_or_default().to_string(), - income: self.deserialize_mv(cols.next().unwrap_or_default()), - })), - _ => Err(ParseError::NoSuchType), - } - } - - // TODO: return Result - fn deserialize_mv(&self, col: &str) -> MonthValues { - MonthValues::from_iter(col.split_whitespace().map(|c| c.parse().unwrap())) - } -} +#[cfg(feature = "serde")] +pub use self::serde::*; diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 33b1650..3c1e5e3 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -1,4 +1,5 @@ -use super::{Error, Result}; +use crate::de::Deserializer; +use crate::error::{Error, Result}; use serde::de::{self, Deserialize, MapAccess, Visitor}; use serde::forward_to_deserialize_any; @@ -11,33 +12,6 @@ where Ok(t) } -pub struct Deserializer<'de> { - input: &'de str, -} - -impl<'de> Deserializer<'de> { - pub fn from_str(input: &'de str) -> Self { - Deserializer { input } - } - - fn peek_char(&mut self) -> Result { - self.input.chars().next().ok_or(Error::Eof) - } - - fn next_char(&mut self) -> Result { - let c = self.peek_char()?; - self.input = &self.input[c.len_utf8()..]; - Ok(c) - } - - pub fn parse_f32(&self) -> Result { - match self.input.parse() { - Ok(val) => Ok(val), - Err(_) => Err(Error::ExpectedFloat), - } - } -} - impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { type Error = Error; @@ -78,7 +52,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } -pub struct StructMap<'a, 'de> { +struct StructMap<'a, 'de> { de: &'a mut Deserializer<'de>, first: bool, } diff --git a/ktf/src/serde/error.rs b/ktf/src/serde/error.rs index ff6597f..5be32e5 100644 --- a/ktf/src/serde/error.rs +++ b/ktf/src/serde/error.rs @@ -1,38 +1,9 @@ +use crate::error::Error; use serde::de; use std::fmt::Display; -use std::{error, result}; - -pub type Result = result::Result; - -#[derive(Debug)] -pub enum Error { - Message(String), - ExpectedHeader, - ExpectedFloat, - ExpectedMap, - ExpectedMapValue, - Syntax, - Eof, -} - -impl error::Error for Error {} impl de::Error for Error { fn custom(msg: T) -> Self { Error::Message(msg.to_string()) } } - -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::Syntax => "syntax error", - Error::Eof => "unexpected EOF", - }) - } -} diff --git a/ktf/src/serde/mod.rs b/ktf/src/serde/mod.rs index 982d03d..fce2d9b 100644 --- a/ktf/src/serde/mod.rs +++ b/ktf/src/serde/mod.rs @@ -1,5 +1,5 @@ mod de; mod error; -pub use de::{from_str, Deserializer}; +pub use de::*; pub use error::*; From 1aacc347b134170000a55137a421221f7e8c1534 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Mon, 25 Jul 2022 19:21:04 +0200 Subject: [PATCH 06/18] feat(ktf): add deserializer methods --- convert/src/input/ktf.rs | 5 ----- ktf/src/de.rs | 26 ++++++++++++++++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 25be66a..50a3306 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -32,11 +32,6 @@ impl Input for KtfInput { let input_data = ktf::from_str::(&ln.unwrap()) .map_err(|e| InputError::Invalid(e.to_string()))?; println!("{:#?}", input_data); - - // match statement { - // Ok(s) => statements.push(s), - // Err(e) => return Err(InputError::Invalid(e.to_string())), - // } } Ok(statements) diff --git a/ktf/src/de.rs b/ktf/src/de.rs index bfdf605..6e23fb4 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -9,16 +9,38 @@ impl<'a> Deserializer<'a> { Deserializer { input } } - pub fn peek_char(&mut self) -> Result { + pub fn advance(&mut self, n: usize) { + self.input = &self.input[n..]; + } + + pub fn peek_char(&self) -> Result { self.input.chars().next().ok_or(Error::Eof) } pub fn next_char(&mut self) -> Result { let c = self.peek_char()?; - self.input = &self.input[c.len_utf8()..]; + self.advance(c.len_utf8()); Ok(c) } + pub fn parse_header(&mut self) -> Result> { + let mut header = Vec::new(); + + if self.next_char()? != '>' { + return Err(Error::ExpectedHeader); + } + + header.push(self.parse_header_col()?); + + Ok(header) + } + + pub fn parse_header_col(&mut self) -> Result { + let col = self.input; + self.advance(col.len()); + Ok(col.to_owned()) + } + pub fn parse_f32(&self) -> Result { match self.input.parse() { Ok(val) => Ok(val), From 757680b6fe95479291e64954665c0c6781713f0b Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Mon, 25 Jul 2022 19:22:27 +0200 Subject: [PATCH 07/18] test(lib): add de_header_single_col test --- ktf/src/lib.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ktf/src/lib.rs b/ktf/src/lib.rs index 6331ded..2e344f9 100644 --- a/ktf/src/lib.rs +++ b/ktf/src/lib.rs @@ -6,3 +6,15 @@ 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"); + Ok(assert_eq!(vec!["col1"], des.parse_header()?)) + } +} From cf525b98e6d39962c83f7abd2395b272198ad54a Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Mon, 25 Jul 2022 20:41:01 +0200 Subject: [PATCH 08/18] feat(ktf): add support for rows and cols --- ktf/src/de.rs | 35 ++++++++++++++++++----------------- ktf/src/lib.rs | 9 +++++++++ ktf/src/serde/de.rs | 9 +-------- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/ktf/src/de.rs b/ktf/src/de.rs index 6e23fb4..008db3e 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -23,28 +23,29 @@ impl<'a> Deserializer<'a> { Ok(c) } - pub fn parse_header(&mut self) -> Result> { - let mut header = Vec::new(); + pub fn next_row(&mut self) -> Result { + let row = self.input.lines().next().ok_or(Error::Eof)?; + self.advance(row.len()); + Ok(Row::new(row)) + } - if self.next_char()? != '>' { - return Err(Error::ExpectedHeader); + pub fn parse_header(&mut self) -> Result> { + match self.next_char()? { + '>' => Ok(self.next_row()?.cols.iter().map(String::to_owned).collect()), + _ => Err(Error::ExpectedHeader), } - - header.push(self.parse_header_col()?); - - Ok(header) } +} - pub fn parse_header_col(&mut self) -> Result { - let col = self.input; - self.advance(col.len()); - Ok(col.to_owned()) - } +#[derive(Debug, Clone)] +pub struct Row { + pub cols: Vec, +} - pub fn parse_f32(&self) -> Result { - match self.input.parse() { - Ok(val) => Ok(val), - Err(_) => Err(Error::ExpectedFloat), +impl Row { + pub fn new(row: &str) -> Self { + Self { + cols: row.split('|').map(str::trim).map(str::to_owned).collect(), } } } diff --git a/ktf/src/lib.rs b/ktf/src/lib.rs index 2e344f9..dea3f8c 100644 --- a/ktf/src/lib.rs +++ b/ktf/src/lib.rs @@ -17,4 +17,13 @@ mod tests { let mut des = Deserializer::from_str(">col1"); Ok(assert_eq!(vec!["col1"], des.parse_header()?)) } + + #[test] + fn de_header_multi_col() -> Result<()> { + let mut des = Deserializer::from_str(">col1|col2|col3"); + Ok(assert_eq!( + vec!["col1", "col2", "col3"], + des.parse_header()? + )) + } } diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 3c1e5e3..6c4d99a 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -26,13 +26,6 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } - fn deserialize_f32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - visitor.visit_f32(self.parse_f32()?) - } - fn deserialize_map(self, visitor: V) -> Result where V: Visitor<'de>, @@ -46,7 +39,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str string + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct struct enum identifier ignored_any } From 907c57aac0d8b9f16435ed4ebe30ac03a7dfb6f2 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Mon, 25 Jul 2022 22:39:26 +0200 Subject: [PATCH 09/18] feat(ktf): start integrating serde --- convert/src/input/ktf.rs | 26 ++++++++++++++---------- ktf/src/de.rs | 13 +++++++++--- ktf/src/serde/de.rs | 43 +++++++++++++++++++++------------------- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 50a3306..77a3cad 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -1,7 +1,7 @@ use super::{Input, InputError}; use kash::statements::Statement; use serde::Deserialize; -use std::io::{BufRead, BufReader, Read}; +use std::io::Read; pub struct KtfInput; @@ -17,22 +17,26 @@ pub struct KtfInputData { } impl Input for KtfInput { - fn from_read(&self, reader: R) -> Result, InputError> + fn from_read(&self, mut reader: R) -> Result, InputError> where R: Read, { let statements = Vec::new(); - let buf = BufReader::new(reader); - for ln in buf.lines() { - if let Err(_) = ln { - return Err(InputError::Read); + // FIXME: actually implement from_read instead of this + // memory-hogging garbage. + let input_data = ktf::from_str::( + { + let mut input = String::new(); + reader + .read_to_string(&mut input) + .map_err(|_| InputError::Read)?; + input } - - let input_data = ktf::from_str::(&ln.unwrap()) - .map_err(|e| InputError::Invalid(e.to_string()))?; - println!("{:#?}", input_data); - } + .as_str(), + ) + .map_err(|e| InputError::Invalid(e.to_string()))?; + println!("{:#?}", input_data); Ok(statements) } diff --git a/ktf/src/de.rs b/ktf/src/de.rs index 008db3e..19c2146 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -2,11 +2,15 @@ use super::error::{Error, Result}; pub struct Deserializer<'a> { input: &'a str, + pub cur_header: Vec, } impl<'a> Deserializer<'a> { pub fn from_str(input: &'a str) -> Self { - Deserializer { input } + Deserializer { + input, + cur_header: vec![], + } } pub fn advance(&mut self, n: usize) { @@ -30,10 +34,13 @@ impl<'a> Deserializer<'a> { } pub fn parse_header(&mut self) -> Result> { - match self.next_char()? { + let header: Vec = match self.next_char()? { '>' => Ok(self.next_row()?.cols.iter().map(String::to_owned).collect()), _ => Err(Error::ExpectedHeader), - } + }?; + + self.cur_header = header.clone(); + Ok(header) } } diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 6c4d99a..da4d5d9 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -20,8 +20,12 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { match self.peek_char()? { - '0'..='9' => self.deserialize_f32(visitor), - '|' => self.deserialize_map(visitor), + '>' => { + self.parse_header()?; + self.deserialize_map(visitor) + } + // '0'..='9' => self.deserialize_f32(visitor), + // '|' => self.deserialize_map(visitor), _ => Err(Error::Syntax), } } @@ -30,12 +34,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.next_char()? == '|' { - let val = visitor.visit_map(StructMap::new(self)); - Ok(val?) - } else { - Err(Error::ExpectedMap) - } + visitor.visit_map(RowMap::new(self)) } forward_to_deserialize_any! { @@ -45,34 +44,38 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } -struct StructMap<'a, 'de> { +struct RowMap<'a, 'de> { de: &'a mut Deserializer<'de>, - first: bool, + col_index: usize, } -impl<'a, 'de> StructMap<'a, 'de> { +impl<'a, 'de> RowMap<'a, 'de> { pub fn new(de: &'a mut Deserializer<'de>) -> Self { - Self { de, first: true } + Self { de, col_index: 0 } + } + + pub fn next_col(&mut self) -> Option { + self.de + .cur_header + .iter() + .nth(self.col_index) + .map(String::to_owned) } } -impl<'a, 'de> MapAccess<'de> for StructMap<'a, 'de> { +impl<'a, 'de> MapAccess<'de> for RowMap<'a, 'de> { type Error = Error; fn next_key_seed(&mut self, seed: K) -> Result> where K: de::DeserializeSeed<'de>, { - if let Err(Error::Eof) = self.de.peek_char() { + if let None = self.next_col() { return Ok(None); } - if !self.first && self.de.next_char()? != '|' { - return Err(Error::ExpectedMapValue); - } - - self.first = false; - + // this currently fails because + // there is no string deserializer implemented. seed.deserialize(&mut *self.de).map(Some) } From 23108c0e0b91ed0f3b39d760c553b9bb56d8f61b Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Tue, 26 Jul 2022 11:04:06 +0200 Subject: [PATCH 10/18] feat(ktf): got maps working, floats are WIP --- ktf/src/de.rs | 15 ++++++++++++- ktf/src/serde/de.rs | 54 +++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/ktf/src/de.rs b/ktf/src/de.rs index 19c2146..887cab1 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -2,6 +2,7 @@ use super::error::{Error, Result}; pub struct Deserializer<'a> { input: &'a str, + col_index: usize, pub cur_header: Vec, } @@ -9,6 +10,7 @@ impl<'a> Deserializer<'a> { pub fn from_str(input: &'a str) -> Self { Deserializer { input, + col_index: 0, cur_header: vec![], } } @@ -29,10 +31,21 @@ impl<'a> Deserializer<'a> { pub fn next_row(&mut self) -> Result { let row = self.input.lines().next().ok_or(Error::Eof)?; - self.advance(row.len()); + self.advance(row.len() + 1); Ok(Row::new(row)) } + pub fn next_col(&mut self) -> Option { + let col = self + .cur_header + .iter() + .nth(self.col_index) + .map(String::to_owned)?; + + self.col_index += 1; + Some(col) + } + pub fn parse_header(&mut self) -> Result> { let header: Vec = match self.next_char()? { '>' => Ok(self.next_row()?.cols.iter().map(String::to_owned).collect()), diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index da4d5d9..bf30ed6 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -1,5 +1,6 @@ use crate::de::Deserializer; use crate::error::{Error, Result}; +use serde::de::value::StrDeserializer; use serde::de::{self, Deserialize, MapAccess, Visitor}; use serde::forward_to_deserialize_any; @@ -7,8 +8,10 @@ pub fn from_str<'a, T>(s: &'a str) -> Result where T: Deserialize<'a>, { - let mut deserializer = Deserializer::from_str(s); - let t = T::deserialize(&mut deserializer)?; + let mut des = Deserializer::from_str(s); + des.parse_header()?; + + let t = T::deserialize(&mut des)?; Ok(t) } @@ -20,12 +23,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { match self.peek_char()? { - '>' => { - self.parse_header()?; - self.deserialize_map(visitor) - } - // '0'..='9' => self.deserialize_f32(visitor), - // '|' => self.deserialize_map(visitor), + '|' => self.deserialize_map(visitor), _ => Err(Error::Syntax), } } @@ -34,11 +32,27 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_map(RowMap::new(self)) + if self.next_char()? != '|' { + Err(Error::ExpectedMap) + } else { + visitor.visit_map(RowMap::new(self)) + } + } + + fn deserialize_f32(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_f32( + self.next_char()? + .to_string() + .parse() + .map_err(|_| Error::ExpectedFloat)?, + ) } forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str string bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct struct enum identifier ignored_any } @@ -46,20 +60,11 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { struct RowMap<'a, 'de> { de: &'a mut Deserializer<'de>, - col_index: usize, } impl<'a, 'de> RowMap<'a, 'de> { pub fn new(de: &'a mut Deserializer<'de>) -> Self { - Self { de, col_index: 0 } - } - - pub fn next_col(&mut self) -> Option { - self.de - .cur_header - .iter() - .nth(self.col_index) - .map(String::to_owned) + Self { de } } } @@ -70,13 +75,10 @@ impl<'a, 'de> MapAccess<'de> for RowMap<'a, 'de> { where K: de::DeserializeSeed<'de>, { - if let None = self.next_col() { - return Ok(None); + match self.de.next_col() { + None => Ok(None), + Some(col) => seed.deserialize(StrDeserializer::new(&col)).map(Some), } - - // this currently fails because - // there is no string deserializer implemented. - seed.deserialize(&mut *self.de).map(Some) } fn next_value_seed(&mut self, seed: V) -> Result From 013ec2436894676304853596d94a57326c415f17 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Tue, 26 Jul 2022 13:21:56 +0200 Subject: [PATCH 11/18] feat(ktf): add support for multiple cols --- convert/src/input/ktf.rs | 4 +++- ktf/src/de.rs | 38 ++++++++++++++++++++++++++++---------- ktf/src/error.rs | 2 ++ ktf/src/serde/de.rs | 25 ++++++++++++++----------- 4 files changed, 47 insertions(+), 22 deletions(-) diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 77a3cad..4e2a349 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -13,7 +13,9 @@ impl KtfInput { #[derive(Debug, Deserialize)] pub struct KtfInputData { - pub test: f32, + pub col1: f32, + pub col2: f32, + pub col3: f32, } impl Input for KtfInput { diff --git a/ktf/src/de.rs b/ktf/src/de.rs index 887cab1..7873280 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -1,9 +1,11 @@ use super::error::{Error, Result}; +#[derive(Debug)] pub struct Deserializer<'a> { input: &'a str, - col_index: usize, - pub cur_header: Vec, + pub col_index: usize, + pub header: Vec, + pub row: Row, } impl<'a> Deserializer<'a> { @@ -11,7 +13,8 @@ impl<'a> Deserializer<'a> { Deserializer { input, col_index: 0, - cur_header: vec![], + header: vec![], + row: Row { cols: vec![] }, } } @@ -32,18 +35,33 @@ impl<'a> Deserializer<'a> { pub fn next_row(&mut self) -> Result { let row = self.input.lines().next().ok_or(Error::Eof)?; self.advance(row.len() + 1); - Ok(Row::new(row)) + + let row = Row::new(row); + self.row = row.clone(); + Ok(row) + } + + pub fn peek_key(&self) -> Result { + self.header + .iter() + .nth(self.col_index) + .map(String::to_owned) + .ok_or(Error::MapEnd) } - pub fn next_col(&mut self) -> Option { - let col = self - .cur_header + pub fn peek_value(&self) -> Result { + self.row + .cols .iter() .nth(self.col_index) - .map(String::to_owned)?; + .map(String::to_owned) + .ok_or(Error::ExpectedMapValue) + } + pub fn next_key(&mut self) -> Result { + let col = self.peek_key()?; self.col_index += 1; - Some(col) + Ok(col) } pub fn parse_header(&mut self) -> Result> { @@ -52,7 +70,7 @@ impl<'a> Deserializer<'a> { _ => Err(Error::ExpectedHeader), }?; - self.cur_header = header.clone(); + self.header = header.clone(); Ok(header) } } diff --git a/ktf/src/error.rs b/ktf/src/error.rs index 06a6c06..23b5d9c 100644 --- a/ktf/src/error.rs +++ b/ktf/src/error.rs @@ -10,6 +10,7 @@ pub enum Error { ExpectedFloat, ExpectedMap, ExpectedMapValue, + MapEnd, Syntax, Eof, } @@ -24,6 +25,7 @@ impl Display for Error { 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", }) diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index bf30ed6..1f41bb0 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -32,11 +32,12 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.next_char()? != '|' { - Err(Error::ExpectedMap) - } else { - visitor.visit_map(RowMap::new(self)) - } + match self.next_char()? { + '|' => self.next_row().map(|_| ()), + _ => Err(Error::ExpectedMap), + }?; + + visitor.visit_map(RowMap::new(self)) } fn deserialize_f32(self, visitor: V) -> Result @@ -44,8 +45,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { visitor.visit_f32( - self.next_char()? - .to_string() + self.peek_value()? .parse() .map_err(|_| Error::ExpectedFloat)?, ) @@ -75,9 +75,10 @@ impl<'a, 'de> MapAccess<'de> for RowMap<'a, 'de> { where K: de::DeserializeSeed<'de>, { - match self.de.next_col() { - None => Ok(None), - Some(col) => seed.deserialize(StrDeserializer::new(&col)).map(Some), + match self.de.peek_key() { + Err(Error::MapEnd) => Ok(None), + Err(e) => Err(e), + Ok(key) => seed.deserialize(StrDeserializer::new(&key)).map(Some), } } @@ -85,6 +86,8 @@ impl<'a, 'de> MapAccess<'de> for RowMap<'a, 'de> { where V: de::DeserializeSeed<'de>, { - seed.deserialize(&mut *self.de) + let des = seed.deserialize(&mut *self.de); + self.de.next_key()?; + des } } From fdf7c47f9fb671abec00650f276cfa0b203c9471 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Tue, 26 Jul 2022 13:25:09 +0200 Subject: [PATCH 12/18] feat(ktf): add support for Strings --- convert/src/input/ktf.rs | 1 + ktf/src/serde/de.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 4e2a349..3e35a33 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -16,6 +16,7 @@ pub struct KtfInputData { pub col1: f32, pub col2: f32, pub col3: f32, + pub col4: String, } impl Input for KtfInput { diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 1f41bb0..a39f6ac 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -51,8 +51,15 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { ) } + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_string(self.peek_value()?) + } + forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str string + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str bytes byte_buf option unit unit_struct newtype_struct seq tuple tuple_struct struct enum identifier ignored_any } From e598131b986b21dec443ec6979167768862284bd Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Tue, 26 Jul 2022 13:44:23 +0200 Subject: [PATCH 13/18] refactor(ktf): move parsing logic to `de` --- ktf/src/de.rs | 10 ++++++++++ ktf/src/serde/de.rs | 8 ++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/ktf/src/de.rs b/ktf/src/de.rs index 7873280..9ceb9e5 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -73,6 +73,16 @@ impl<'a> Deserializer<'a> { self.header = header.clone(); Ok(header) } + + #[inline] + pub fn parse_f32(&self) -> Result { + self.peek_value()?.parse().map_err(|_| Error::ExpectedFloat) + } + + #[inline] + pub fn parse_string(&self) -> Result { + self.peek_value() + } } #[derive(Debug, Clone)] diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index a39f6ac..41d325e 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -44,18 +44,14 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_f32( - self.peek_value()? - .parse() - .map_err(|_| Error::ExpectedFloat)?, - ) + visitor.visit_f32(self.parse_f32()?) } fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_string(self.peek_value()?) + visitor.visit_string(self.parse_string()?) } forward_to_deserialize_any! { From 59ccfcf6808b9b46333a953270cb36b363926883 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Tue, 26 Jul 2022 14:34:15 +0200 Subject: [PATCH 14/18] feat(ktf): add support for sequences --- convert/src/input/ktf.rs | 2 +- ktf/src/de.rs | 19 ++++++++++++++----- ktf/src/lib.rs | 4 ++-- ktf/src/serde/de.rs | 36 ++++++++++++++++++++++++++++++++++-- 4 files changed, 51 insertions(+), 10 deletions(-) diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 3e35a33..1ebed3b 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -28,7 +28,7 @@ impl Input for KtfInput { // FIXME: actually implement from_read instead of this // memory-hogging garbage. - let input_data = ktf::from_str::( + let input_data = ktf::from_str::>( { let mut input = String::new(); reader diff --git a/ktf/src/de.rs b/ktf/src/de.rs index 9ceb9e5..5ae15d5 100644 --- a/ktf/src/de.rs +++ b/ktf/src/de.rs @@ -14,7 +14,10 @@ impl<'a> Deserializer<'a> { input, col_index: 0, header: vec![], - row: Row { cols: vec![] }, + row: Row { + cols: vec![], + len: 0, + }, } } @@ -33,14 +36,18 @@ impl<'a> Deserializer<'a> { } pub fn next_row(&mut self) -> Result { - let row = self.input.lines().next().ok_or(Error::Eof)?; - self.advance(row.len() + 1); - - let row = Row::new(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 { + let row = self.input.lines().next().ok_or(Error::Eof)?; + Ok(Row::new(row)) + } + pub fn peek_key(&self) -> Result { self.header .iter() @@ -88,12 +95,14 @@ impl<'a> Deserializer<'a> { #[derive(Debug, Clone)] pub struct Row { pub cols: Vec, + 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(), } } } diff --git a/ktf/src/lib.rs b/ktf/src/lib.rs index dea3f8c..142235c 100644 --- a/ktf/src/lib.rs +++ b/ktf/src/lib.rs @@ -14,13 +14,13 @@ mod tests { #[test] fn de_header_single_col() -> Result<()> { - let mut des = Deserializer::from_str(">col1"); + 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"); + let mut des = Deserializer::from_str(">col1|col2|col3\n"); Ok(assert_eq!( vec!["col1", "col2", "col3"], des.parse_header()? diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 41d325e..86b9502 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -1,7 +1,7 @@ use crate::de::Deserializer; use crate::error::{Error, Result}; use serde::de::value::StrDeserializer; -use serde::de::{self, Deserialize, MapAccess, Visitor}; +use serde::de::{self, Deserialize, MapAccess, SeqAccess, Visitor}; use serde::forward_to_deserialize_any; pub fn from_str<'a, T>(s: &'a str) -> Result @@ -40,6 +40,13 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor.visit_map(RowMap::new(self)) } + fn deserialize_seq(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_seq(LineSeq::new(self)) + } + fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, @@ -56,7 +63,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str - bytes byte_buf option unit unit_struct newtype_struct seq tuple + bytes byte_buf option unit unit_struct newtype_struct tuple tuple_struct struct enum identifier ignored_any } } @@ -94,3 +101,28 @@ impl<'a, 'de> MapAccess<'de> for RowMap<'a, 'de> { des } } + +struct LineSeq<'a, 'de> { + de: &'a mut Deserializer<'de>, +} + +impl<'a, 'de> LineSeq<'a, 'de> { + pub fn new(de: &'a mut Deserializer<'de>) -> Self { + Self { de } + } +} + +impl<'a, 'de> SeqAccess<'de> for LineSeq<'a, 'de> { + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: de::DeserializeSeed<'de>, + { + match self.de.peek_row() { + Err(Error::Eof) => Ok(None), + Err(e) => Err(e), + _ => seed.deserialize(&mut *self.de).map(Some), + } + } +} From 7c0ae8a4ed7c60802347a3d553d2e12f11932cb9 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Tue, 26 Jul 2022 19:13:15 +0200 Subject: [PATCH 15/18] feat(ktf): add support for enum identifiers --- ktf/src/serde/de.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 86b9502..00085d8 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -47,6 +47,13 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor.visit_seq(LineSeq::new(self)) } + fn deserialize_identifier(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_string(self.parse_string()?) + } + fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, @@ -64,7 +71,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { forward_to_deserialize_any! { bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str bytes byte_buf option unit unit_struct newtype_struct tuple - tuple_struct struct enum identifier ignored_any + tuple_struct struct enum ignored_any } } From 08c303345910747066cd4f9518670a0b3425279c Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Fri, 29 Jul 2022 13:17:52 +0200 Subject: [PATCH 16/18] feat(ktf): implement deserialize_str --- ktf/src/serde/de.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/ktf/src/serde/de.rs b/ktf/src/serde/de.rs index 00085d8..0ab116e 100644 --- a/ktf/src/serde/de.rs +++ b/ktf/src/serde/de.rs @@ -51,7 +51,7 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_string(self.parse_string()?) + self.deserialize_str(visitor) } fn deserialize_f32(self, visitor: V) -> Result @@ -61,15 +61,22 @@ impl<'a, 'de> de::Deserializer<'de> for &'a mut Deserializer<'de> { visitor.visit_f32(self.parse_f32()?) } + fn deserialize_str(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + visitor.visit_str(&self.parse_string()?) + } + fn deserialize_string(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_string(self.parse_string()?) + self.deserialize_str(visitor) } forward_to_deserialize_any! { - bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char str + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f64 char bytes byte_buf option unit unit_struct newtype_struct tuple tuple_struct struct enum ignored_any } From cf2491327e15a4fe4c1e841569c0f70a327ef646 Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Sat, 6 Aug 2022 22:31:55 +0200 Subject: [PATCH 17/18] chore(ktf): use enum for KtfInputData --- convert/src/input/ktf.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/convert/src/input/ktf.rs b/convert/src/input/ktf.rs index 1ebed3b..e104bd1 100644 --- a/convert/src/input/ktf.rs +++ b/convert/src/input/ktf.rs @@ -12,11 +12,15 @@ impl KtfInput { } #[derive(Debug, Deserialize)] -pub struct KtfInputData { - pub col1: f32, - pub col2: f32, - pub col3: f32, - pub col4: String, +#[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 { From abef0d832032e45b50c0217142898e4786ae0a6d Mon Sep 17 00:00:00 2001 From: Kees van Voorthuizen Date: Sun, 4 Sep 2022 13:05:51 +0200 Subject: [PATCH 18/18] feat(ktf): re-add ktf to cli, repo & convert --- Cargo.lock | 9 +++++++++ cli/src/kash/args.rs | 1 + cli/src/kash/main.rs | 2 ++ convert/Cargo.toml | 5 ++++- convert/src/input/mod.rs | 2 ++ repo/src/fs.rs | 3 ++- 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 76df4ec..4ae4da7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -458,6 +458,7 @@ dependencies = [ "camt053", "chrono", "kash", + "ktf", "quick-xml", "serde", "serde_json", @@ -494,6 +495,14 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "ktf" +version = "0.1.0" +dependencies = [ + "kash", + "serde", +] + [[package]] name = "lazy_static" version = "1.4.0" diff --git a/cli/src/kash/args.rs b/cli/src/kash/args.rs index 7b2cc11..c749984 100644 --- a/cli/src/kash/args.rs +++ b/cli/src/kash/args.rs @@ -7,6 +7,7 @@ pub enum InputFormat { Toml, Json, Camt053, + Ktf, } /// command-line interface to kash diff --git a/cli/src/kash/main.rs b/cli/src/kash/main.rs index 99c851a..27106a8 100644 --- a/cli/src/kash/main.rs +++ b/cli/src/kash/main.rs @@ -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; @@ -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(), ); diff --git a/convert/Cargo.toml b/convert/Cargo.toml index e6347bf..d6ce651 100644 --- a/convert/Cargo.toml +++ b/convert/Cargo.toml @@ -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" @@ -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"] diff --git a/convert/src/input/mod.rs b/convert/src/input/mod.rs index e2987d2..29a9ddd 100644 --- a/convert/src/input/mod.rs +++ b/convert/src/input/mod.rs @@ -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; diff --git a/repo/src/fs.rs b/repo/src/fs.rs index 650e500..fb58fb8 100644 --- a/repo/src/fs.rs +++ b/repo/src/fs.rs @@ -6,7 +6,7 @@ use kash::{ }, }; use kash_convert::input::{ - camt053::Camt053Input, json::JsonInput, toml::TomlInput, Input, + camt053::Camt053Input, json::JsonInput, ktf::KtfInput, toml::TomlInput, Input, }; use std::{ fs::{self, File}, @@ -33,6 +33,7 @@ impl FsRepo { Some("json") => Ok(JsonInput::new().from_read(input_file)), Some("toml") => Ok(TomlInput::new().from_read(input_file)), Some("xml") => Ok(Camt053Input::new().from_read(input_file)), + Some("ktf") => Ok(KtfInput::new().from_read(input_file)), Some(ext) => Err(Error::Message(format!("unknown format '{ext}'"))), None => Err(Error::Message("extension parse error".into())), }?