From 4c9515ceb43f90700d1e376233a6c44ce8a15f11 Mon Sep 17 00:00:00 2001 From: Profpatsch Date: Tue, 18 May 2021 19:38:38 +0200 Subject: pkgs/profpatsch: init text-letter A simple text letter formatter (A4) for printing. --- pkgs/profpatsch/default.nix | 2 + pkgs/profpatsch/text-letter.nix | 25 ++++++++ pkgs/profpatsch/text-letter.rs | 131 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 pkgs/profpatsch/text-letter.nix create mode 100644 pkgs/profpatsch/text-letter.rs (limited to 'pkgs/profpatsch') diff --git a/pkgs/profpatsch/default.nix b/pkgs/profpatsch/default.nix index 61530859..732eabc6 100644 --- a/pkgs/profpatsch/default.nix +++ b/pkgs/profpatsch/default.nix @@ -260,6 +260,8 @@ in rec { printenv ; + text-letter = import ./text-letter.nix { inherit pkgs rust-deps writeRustSimple writeExecline getBins; }; + inherit (import ./netencode { inherit pkgs writeRustSimpleLib writeRustSimple el-semicolon el-exec; }) netencode-rs netencode-rs-tests diff --git a/pkgs/profpatsch/text-letter.nix b/pkgs/profpatsch/text-letter.nix new file mode 100644 index 00000000..d9ee9ef5 --- /dev/null +++ b/pkgs/profpatsch/text-letter.nix @@ -0,0 +1,25 @@ +{ pkgs, rust-deps, writeRustSimple, writeExecline, getBins }: + +let + bins = getBins pkgs.coreutils [ "date" "cat" ]; + + mustache-interpol = writeRustSimple "mustache-interpol" { + dependencies = [ + rust-deps.mustache + rust-deps.toml + rust-deps.regex + rust-deps.lazy_static + ]; + } (./text-letter.rs); + + text-letter = writeExecline "write-letter" {} [ + "pipeline" [ bins.cat "/home/philip/kot/work/pa/krankenkasse/2021-05-18-tk-anschreiben-assurance-maladie.toml" ] + mustache-interpol + ]; + +in { + inherit + mustache-interpol + text-letter + ; +} diff --git a/pkgs/profpatsch/text-letter.rs b/pkgs/profpatsch/text-letter.rs new file mode 100644 index 00000000..55921adc --- /dev/null +++ b/pkgs/profpatsch/text-letter.rs @@ -0,0 +1,131 @@ +//! Reads a toml file on stdin describing a simple letter format, +//! outputs a text formatting of the file, which can be piped to lp +//! and should be viable to use on A4 paper with one of these windowed envelopes. +//! Might depend on the printer. +//! +//! ``` +//! cat letter.toml | ./text-letter | lp -h localhost -d printer-name - +//! ``` +extern crate mustache; +extern crate toml; +extern crate regex; +extern crate lazy_static; + + +use std::io::{Read, Write}; + +struct Letter { + current_city: String, + from_address: String, + to_address: String, + content: String, + date: Date +} + +struct Date { + year: String, + month: String, + day: String, +} + + +lazy_static::lazy_static!{ + static ref date_regex: regex::bytes::Regex = regex::bytes::Regex::new(r"^([0-9]{4})-([0-9]{2})-([0-9]{2})$").unwrap(); +} + +fn parse_date(s: &str) -> Option { + date_regex.captures(s.as_bytes()).map(|c| Date { + year: String::from_utf8(c.get(1).unwrap().as_bytes().to_vec()).unwrap(), + month: String::from_utf8(c.get(2).unwrap().as_bytes().to_vec()).unwrap(), + day: String::from_utf8(c.get(3).unwrap().as_bytes().to_vec()).unwrap(), + }) +} + +fn parse_letter_toml(input: &str) -> Letter { + let v : toml::Value = toml::from_str(input).expect("could not parse letter TOML"); + let get = |field: &str| -> &str { + v.get(field).expect(&format!("field {} is missing in letter TOML", field)) + .as_str().expect(&format!("field {} must be a string", field)) + }; + assert_eq!(get("type"), "letter", "type must be `letter`"); + assert_eq!(get("version"), "0.0.1", "version must be `0.0.1`"); + + Letter { + current_city: get("current-city").to_owned(), + from_address: get("from-address").trim_right().to_owned(), + to_address: get("to-address").trim_right().to_owned(), + content: get("content").trim_right().to_owned(), + date: parse_date(get("date")).expect("field `date` needs to be a date like yyyy-mm-dd"), + } +} + +fn main() { + let mut letter = String::new(); + std::io::stdin().read_to_string(&mut letter).expect("stdin has to be an utf-8 string"); + let letter = parse_letter_toml(&letter); + let from_address_one_line = letter.from_address.replace("\n", ", "); + + fn indent4(s: String) -> String { + let mut res = String::from(" "); + res.push_str(&s.replace("\n", "\n ")); + res + } + fn indent2(s: String) -> String { + let mut res = String::from(" "); + res.push_str(&s.replace("\n", "\n ")); + res + } + + let template = mustache::compile_str(r###" +{{{date}}} + + + + + + + + + +{{{from_address_one_line}}} + +{{{an}}} +{{{to_address}}} + + + + + + + + + +{{content}} +"### + ).expect("the template is malformed"); + + + let data : std::collections::HashMap = + [("an", indent4("An:".to_string())), + ("from_address_one_line", indent4(from_address_one_line)), + ("to_address", indent4(letter.to_address)), + ("content", letter.content), + ("date", format!( + "{}, den {}.{}.{}", + letter.current_city, + letter.date.day, + letter.date.month, + letter.date.year + )) + ][..].into_iter().cloned() + .map(|(k,v)| (k.to_owned(), mustache::Data::String(v))).collect(); + let data = mustache::Data::Map(data); + let res = template.render_data_to_string( + &data + ).expect("could not render template"); + + // give a slight margin + let res = indent2(res); + + std::io::stdout().write_all(&res.as_bytes()).unwrap() +} -- cgit 1.4.1