diff --git a/Cargo.lock b/Cargo.lock index 2b78c0f..92f3eff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,132 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-executor" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa3dc5f2a8564f07759c008b9109dc0d39de92a88d5588b8a5036d286383afb" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand 1.9.0", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.23", + "slab", + "socket2 0.4.9", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-process" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a9d28b1d97e08915212e2e45310d47854eafa69600756fc735fb788f75199c9" +dependencies = [ + "async-io", + "async-lock", + "autocfg", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 0.37.23", + "signal-hook", + "windows-sys", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc7ab41815b3c653ccd2978ec3255c81349336702dfdf62ee6f7069b12a3aae" + +[[package]] +name = "atomic-waker" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3" + [[package]] name = "autocfg" version = "1.1.0" @@ -128,6 +254,21 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "blocking" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77231a1c8f801696fc0123ec6150ce92cffb8e164a02afb9c8ddee0e9b65ad65" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "atomic-waker", + "fastrand 1.9.0", + "futures-lite", + "log", +] + [[package]] name = "bumpalo" version = "3.13.0" @@ -237,6 +378,15 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "concurrent-queue" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62ec6771ecfa0762d24683ee5a32ad78487a3d3afdc0fb8cae19d2c5deb50b7c" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -253,12 +403,58 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "csv" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "626ae34994d3d8d668f4269922248239db4ae42d538b14c398b74a52208e8086" +dependencies = [ + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "deranged" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" +[[package]] +name = "email-encoding" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75" +dependencies = [ + "base64", + "memchr", +] + +[[package]] +name = "email_address" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -295,6 +491,21 @@ dependencies = [ "libc", ] +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.0.0" @@ -352,6 +563,21 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-sink" version = "0.3.28" @@ -385,6 +611,18 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "h2" version = "0.3.21" @@ -428,6 +666,17 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.9" @@ -522,6 +771,16 @@ dependencies = [ "cc", ] +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.4.0" @@ -552,6 +811,26 @@ dependencies = [ "hashbrown 0.14.0", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + [[package]] name = "ipnet" version = "2.8.0" @@ -573,18 +852,57 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lettre" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76bd09637ae3ec7bd605b8e135e757980b3968430ff2b1a4a94fb7769e50166d" +dependencies = [ + "async-std", + "base64", + "email-encoding", + "email_address", + "fastrand 1.9.0", + "futures-util", + "hostname", + "httpdate", + "idna 0.3.0", + "mime", + "native-tls", + "nom", + "once_cell", + "quoted_printable", + "socket2 0.4.9", + "tokio", +] + [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +[[package]] +name = "linux-raw-sys" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + [[package]] name = "linux-raw-sys" version = "0.4.7" @@ -596,6 +914,15 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] + +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" [[package]] name = "memchr" @@ -609,6 +936,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -647,6 +980,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.16" @@ -734,6 +1077,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f2252c834a40ed9bb5422029649578e63aa341ac401f74e719dd1afda8394e" + [[package]] name = "parse-zoneinfo" version = "0.3.0" @@ -805,6 +1154,22 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys", +] + [[package]] name = "proc-macro2" version = "1.0.66" @@ -823,6 +1188,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "quoted_printable" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a3866219251662ec3b26fc217e3e05bf9c4f84325234dfb96bf0bf840889e49" + [[package]] name = "rand" version = "0.8.5" @@ -919,6 +1290,20 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +[[package]] +name = "rustix" +version = "0.37.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys", +] + [[package]] name = "rustix" version = "0.38.12" @@ -928,7 +1313,7 @@ dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.7", "windows-sys", ] @@ -1022,6 +1407,25 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "simplelog" version = "0.12.1" @@ -1106,9 +1510,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.0", "redox_syscall", - "rustix", + "rustix 0.38.12", "windows-sys", ] @@ -1158,6 +1562,8 @@ dependencies = [ "chrono", "chrono-tz", "clap", + "csv", + "lettre", "log", "reqwest", "serde", @@ -1193,6 +1599,7 @@ dependencies = [ "mio", "num_cpus", "pin-project-lite", + "signal-hook-registry", "socket2 0.5.3", "windows-sys", ] @@ -1315,7 +1722,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", - "idna", + "idna 0.4.0", "percent-encoding", ] @@ -1325,12 +1732,24 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "value-bag" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" + [[package]] name = "vcpkg" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "want" version = "0.3.1" diff --git a/flake.nix b/flake.nix index 68e3c0e..06c1cfc 100644 --- a/flake.nix +++ b/flake.nix @@ -73,7 +73,7 @@ rust-project TODO: write shell script for automatically updating `cargoHash` pname = "time_tracker"; version = "0.1.0"; buildAndTestSubdir = "time_tracker"; - cargoHash = "sha256-b8qbOh7ATsQnswgFkbeES8VHGhs1OCYCcuyJ4m0bGMs="; + cargoHash = "sha256-5Wy2+ef2BriKmvNhllDVh/clAZGV7myDc281ygj05vI="; meta = meta // { description = "Using nextcloud notes to track time usage."; }; diff --git a/time_tracker/Cargo.toml b/time_tracker/Cargo.toml index dc464fe..cac90bc 100644 --- a/time_tracker/Cargo.toml +++ b/time_tracker/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" chrono = "0.4.26" chrono-tz = "0.8.3" clap = { version = "4.4.0", features = ["derive"] } +csv = "1.2.2" +lettre = { version = "0.10.4", features = ["sendmail-transport", "builder"] } log = "0.4.20" reqwest = { version = "0.11.18", features = ["blocking", "json"] } serde = { version = "1.0.184", features = ["serde_derive"] } diff --git a/time_tracker/src/main.rs b/time_tracker/src/main.rs index b3a40f4..6feb113 100644 --- a/time_tracker/src/main.rs +++ b/time_tracker/src/main.rs @@ -1,6 +1,10 @@ use chrono::{DateTime, Datelike, Utc}; use chrono_tz::US::Central; use clap::Parser; +use lettre::{ + message::{Message, header::ContentType, Attachment, MultiPart, SinglePart}, + SendmailTransport, Transport, +}; use log::{debug, error}; use reqwest::blocking::Client; use serde::{Deserialize, Serialize}; @@ -41,6 +45,8 @@ fn main() -> Result<(), Box> { }; // --- END Loading and setup --- + let mut do_email_summary: bool = false; + // --- Get checked and unchecked --- let primary_note = Client::new() .get(format!( @@ -59,6 +65,13 @@ fn main() -> Result<(), Box> { let mut checked: Vec = vec![]; for line in primary_note_lines.iter() { + if line.starts_with("- [x] Send Summary") { + do_email_summary = true; + continue; + } + if line.starts_with("- [ ] Send Summary") { + continue; + } // if line is a checkbox if line.starts_with("- [") { if line.starts_with("- [ ] ") { @@ -140,13 +153,13 @@ fn main() -> Result<(), Box> { body_content.push(format!( "### {item} | {}h {}m | {}/{}/{}", elapsed_time.num_hours(), - elapsed_time.num_minutes(), + elapsed_time.num_minutes() - elapsed_time.num_hours() * 60, // yes, I'm lazy now.month(), now.day(), now.year() )); } else if !line.is_empty() { - body_content.push(line.to_string()); + body_content.push(line.to_string()); } checked.retain(|val| val != &item); } @@ -161,8 +174,7 @@ fn main() -> Result<(), Box> { }) .collect(); - debug!("checked: {:#?}", checked); - debug!("unchecked: {:#?}", unchecked); + debug!("Creating new logs for: {:#?}", checked); body_content.splice(2..2, checked); @@ -181,6 +193,12 @@ fn main() -> Result<(), Box> { })?) .send()?; + if do_email_summary { + debug!("Send email selected"); + reset_email_checkbox(&cfg); + send_email_summary(&cfg, body_content); + } + Ok(()) } @@ -205,6 +223,7 @@ struct NoteUpdate { struct Config { user: String, pswd: String, + email_addr: String, primary_note_id: String, logging_note_id: String, server_url: String, @@ -220,3 +239,149 @@ struct CliArgs { #[arg(short, long)] debug: bool, } + +// All unwraps are unrecoverable errors +// All calls after depend on the success of the one before, and cannot run +// without. So it would be too much work to make an error type to handle an error +// that would cause a crash anyways +fn reset_email_checkbox(config: &Config) { + let primary_note = Client::new() + .get(format!( + "https://{}/index.php/apps/notes/api/v1/notes/{}", + &config.server_url, &config.primary_note_id + )) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .basic_auth(&config.user, Some(&config.pswd)) + .send() + .unwrap() + .json::() + .unwrap(); + + let primary_note_lines: Vec<&str> = primary_note.content.lines().collect(); + + let mut body_content: Vec<&str> = vec![]; + + for line in primary_note_lines.iter() { + if line.starts_with("- [x] Send Summary") { + body_content.push("- [ ] Send Summary"); + } else { + body_content.push(line); + } + } + + Client::new() + .put(format!( + "https://{}/index.php/apps/notes/api/v1/notes/{}", + &config.server_url, &config.primary_note_id + )) + .header("Accept", "application/json") + .header("Content-Type", "application/json") + .basic_auth(&config.user, Some(&config.pswd)) + .body( + serde_json::to_string(&NoteUpdate { + content: body_content.join("\n"), + }) + .unwrap(), + ) + .send() + .unwrap(); +} + +#[derive(Serialize, Deserialize, Debug)] +struct SummaryRow { + date: String, + total_time: i64, + task_name: String, +} + +// All unwraps are unrecoverable errors +// All calls after depend on the success of the one before, and cannot run +// without. So it would be too much work to make an error type to handle an error +// that would cause a crash anyways +fn send_email_summary(config: &Config, body_content: Vec) { + let mut wtr = csv::Writer::from_writer(vec![]); + + for line in body_content { + if line.starts_with("### ") { + let mut split: Vec<&str> = line.split('|').collect(); + if split.len() != 3 { + error!("There was an issue with this line: {line}"); + continue; + } + let date: String = match split.pop() { + Some(val) => val, + None => continue, + } + .to_string(); + let time: i64 = { + // This should never error as the len should always be 3 + let data = match split.pop() { + Some(val) => val, + None => continue, + } + .trim(); + + // There should always be an h and an m in the second item + let h_index = match data.chars().position(|c| c == 'h') { + Some(val) => val, + None => continue, + }; + + let m_index = match data.chars().position(|c| c == 'm') { + Some(val) => val, + None => continue, + }; + + // this should always be the "10" in "10h" + let hours = match data[0..h_index].parse::() { + Ok(val) => val, + Err(_) => continue, + }; + + // this should always be the "20" in "10h 20m" + let minutes = match data[(h_index + 2)..m_index].parse::() { + Ok(val) => val, + Err(_) => continue, + }; + + hours * 60 + minutes + }; + + let task_name: String = match split.pop() { + Some(val) => val.trim(), + None => continue, + } + .to_string() + .replace("### ", ""); + + wtr.serialize(SummaryRow { date: date.trim().to_string(), total_time: time, task_name }).unwrap(); + } + } + + let data = String::from_utf8(wtr.into_inner().unwrap()).unwrap(); + + debug!("{:#?}", data); + + let attachment = Attachment::new("TimeSummary.csv".to_string()).body(data, ContentType::parse("text/csv").unwrap()); + + let email = Message::builder() + .from("NoReplay ".parse().unwrap()) + .to("Nicholas Young ".parse().unwrap()) + .subject("Testing email") + .header(ContentType::TEXT_PLAIN) + .multipart( + MultiPart::mixed() + .singlepart(SinglePart::plain("Hello World".to_string())) + .singlepart(attachment) + ).unwrap(); + + + let mailer = SendmailTransport::new(); + + match mailer.send(&email) { + Ok(val) => debug!("email sent: {:?}", val), + Err(e) => debug!("Couldn't send email {}", e) + }; + +}