+TransactionLine
This commit is contained in:
parent
cf4dadd151
commit
04ec2f320e
111
Cargo.lock
generated
111
Cargo.lock
generated
|
@ -1,5 +1,11 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2"
|
||||
|
||||
[[package]]
|
||||
name = "bstr"
|
||||
version = "0.2.8"
|
||||
|
@ -18,6 +24,17 @@ version = "1.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
|
||||
|
||||
[[package]]
|
||||
name = "chrono"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31850b4a4d6bae316f7a09e691c944c28299298837edc0a03f755618c23cbc01"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "csv"
|
||||
version = "1.1.1"
|
||||
|
@ -115,6 +132,15 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569"
|
||||
|
||||
[[package]]
|
||||
name = "fixed"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1343b84a06a392d5475cb9884bf21bd461b7af544bee131f4d954b0fbe56e2a2"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.4"
|
||||
|
@ -152,6 +178,46 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40f005c60db6e03bae699e414c58bf9aa7ea02a2d0b9bfbcf19286cc4c82b30"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4c81ffc11c212fa327657cb19dd85eb7419e163b5b076bede2bdb5c974c07e4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
|
||||
dependencies = [
|
||||
"siphasher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "precomputed-hash"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "0.4.30"
|
||||
|
@ -174,9 +240,12 @@ dependencies = [
|
|||
name = "rdcsv"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"csv",
|
||||
"encoding",
|
||||
"fixed",
|
||||
"readfilez",
|
||||
"string_cache",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -189,6 +258,12 @@ dependencies = [
|
|||
"memmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.1.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.8"
|
||||
|
@ -210,6 +285,25 @@ version = "1.0.104"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449"
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83da420ee8d1a89e640d0948c646c1c088758d3a3c538f943bfa97bdac17929d"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"phf_shared",
|
||||
"precomputed-hash",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "0.15.44"
|
||||
|
@ -221,6 +315,23 @@ dependencies = [
|
|||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.1.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.1.0"
|
||||
|
|
|
@ -6,5 +6,8 @@ edition = "2018"
|
|||
|
||||
[dependencies]
|
||||
csv = "1.1"
|
||||
chrono = "0.4"
|
||||
encoding = "0.2"
|
||||
fixed = "0.5"
|
||||
readfilez = "0.2"
|
||||
string_cache = "0.8"
|
||||
|
|
194
src/main.rs
194
src/main.rs
|
@ -1,3 +1,154 @@
|
|||
use chrono::naive::NaiveDate;
|
||||
use string_cache::DefaultAtom;
|
||||
use std::{collections::HashSet, fmt};
|
||||
|
||||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
|
||||
enum KontoDaten {
|
||||
None,
|
||||
Old {
|
||||
knr: u64,
|
||||
blz: u32,
|
||||
},
|
||||
New {
|
||||
iban: String,
|
||||
bic: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
enum Waehrung {
|
||||
EUR,
|
||||
USD,
|
||||
}
|
||||
|
||||
impl fmt::Display for Waehrung {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", match self {
|
||||
Waehrung::EUR => "EUR",
|
||||
Waehrung::USD => "USD",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
struct WaehrungParseError;
|
||||
|
||||
impl std::error::Error for WaehrungParseError {}
|
||||
|
||||
impl fmt::Display for WaehrungParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "invalid currency")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for Waehrung {
|
||||
type Err = WaehrungParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, WaehrungParseError> {
|
||||
Ok(match s {
|
||||
"EUR" => Waehrung::EUR,
|
||||
"USD" => Waehrung::USD,
|
||||
_ => return Err(WaehrungParseError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type TransactionValue = fixed::types::U21F11;
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
|
||||
enum TransactionDirection {
|
||||
Haben, // H = Gutschrift
|
||||
Soll, // S = Belastung
|
||||
}
|
||||
|
||||
impl fmt::Display for TransactionDirection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", match self {
|
||||
TransactionDirection::Haben => "H",
|
||||
TransactionDirection::Soll => "S",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
struct TransactionDirectionParseError;
|
||||
|
||||
impl std::error::Error for TransactionDirectionParseError {}
|
||||
|
||||
impl fmt::Display for TransactionDirectionParseError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "invalid H/S")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for TransactionDirection {
|
||||
type Err = TransactionDirectionParseError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, TransactionDirectionParseError> {
|
||||
Ok(match s {
|
||||
"H" => TransactionDirection::Haben,
|
||||
"S" => TransactionDirection::Soll,
|
||||
_ => return Err(TransactionDirectionParseError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
struct TransactionLine {
|
||||
d_buchungs: NaiveDate,
|
||||
d_valuta: NaiveDate,
|
||||
// ignore 3. column = "Auftraggeber/Zahlungsempfänger",
|
||||
// because that should be nearly equivalent for every row
|
||||
p_other: DefaultAtom,
|
||||
konto_data: KontoDaten,
|
||||
verwendungszw: String,
|
||||
kref: String,
|
||||
waehrung: Waehrung,
|
||||
umsatz: TransactionValue,
|
||||
direction: TransactionDirection,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
struct Transactions {
|
||||
pothc: HashSet<DefaultAtom>,
|
||||
elems: Vec<TransactionLine>,
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Transactions {
|
||||
type Target = [TransactionLine];
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &[TransactionLine] {
|
||||
&self.elems[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl Transactions {
|
||||
#[inline]
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.elems.is_empty()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.elems.len()
|
||||
}
|
||||
|
||||
pub fn push(&mut self, mut tl: TransactionLine) {
|
||||
if let Some(x) = self.pothc.get(&tl.p_other) {
|
||||
tl.p_other = x.clone();
|
||||
} else {
|
||||
self.pothc.insert(tl.p_other.clone());
|
||||
}
|
||||
self.elems.push(tl);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
use encoding::types::Encoding;
|
||||
|
||||
|
@ -10,15 +161,54 @@ fn main() {
|
|||
.decode(&*fh, encoding::types::DecoderTrap::Replace)
|
||||
.expect("got invalid latin-1 data");
|
||||
|
||||
std::mem::drop(fh);
|
||||
|
||||
let mut rdr = csv::ReaderBuilder::new()
|
||||
.delimiter(b';')
|
||||
.flexible(true)
|
||||
.has_headers(false)
|
||||
.from_reader(dat.as_bytes());
|
||||
|
||||
for result in rdr.records() {
|
||||
let mut recsit = rdr.records().skip(8);
|
||||
|
||||
assert_eq!(recsit.next().unwrap().unwrap(), vec!["Buchungstag", "Valuta", "Auftraggeber/Zahlungsempfänger", "Empfänger/Zahlungspflichtiger", "Konto-Nr.", "IBAN", "BLZ", "BIC", "Vorgang/Verwendungszweck", "Kundenreferenz", "Währung", "Umsatz", " "]);
|
||||
|
||||
println!("\nData:");
|
||||
for result in recsit {
|
||||
let record = result.expect("got invalid line");
|
||||
println!("{:?}", record);
|
||||
assert_eq!(record.len(), 13);
|
||||
if record[1].is_empty() {
|
||||
// finalizer line -> ignore
|
||||
break;
|
||||
}
|
||||
|
||||
// decode KontoDaten
|
||||
let r_e: &[u8] = &[record[4].is_empty() as u8, record[5].is_empty() as u8, record[6].is_empty() as u8, record[7].is_empty() as u8];
|
||||
let konto_data = match &*r_e {
|
||||
&[0, 1, 0, 1] => KontoDaten::Old {
|
||||
knr: record[4].parse().expect("invalid Konto-Nr."),
|
||||
blz: record[6].parse().expect("invalid BLZ"),
|
||||
},
|
||||
&[1, 0, 1, 0] => KontoDaten::New {
|
||||
iban: record[5].to_string(),
|
||||
bic: record[7].to_string(),
|
||||
},
|
||||
&[1, 1, 1, 1] => KontoDaten::None,
|
||||
_ => unimplemented!("unsupported KontoData encoding: {:?} :in: {:?}", &r_e, &record),
|
||||
};
|
||||
|
||||
let tl = TransactionLine {
|
||||
d_buchungs: NaiveDate::parse_from_str(&record[0], "%d.%m.%Y").expect("invalid date format"),
|
||||
d_valuta: NaiveDate::parse_from_str(&record[1], "%d.%m.%Y").expect("invalid date format"),
|
||||
p_other: DefaultAtom::from(&record[3]),
|
||||
konto_data,
|
||||
verwendungszw: record[8].to_string(),
|
||||
kref: record[9].to_string(),
|
||||
waehrung: record[10].parse().unwrap(),
|
||||
umsatz: record[11].replace('.', "").replace(',', ".").parse().expect("invalid Umsatz"),
|
||||
direction: record[12].parse().unwrap(),
|
||||
};
|
||||
println!("{:?}", tl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue