commit 474f2397c6eb7be01ee871e3dee2f35735b708f7 Author: Wataru Otsubo Date: Fri Jul 12 01:08:29 2024 +0900 New: Parsing till position/id assignment diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..cb52621 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,542 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "autocfg" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaff6f8ce506b9773fa786672d63fc7a191ffea1be33f72bbd4aeacefca9ffc8" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets", +] + +[[package]] +name = "clap" +version = "4.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap-verbosity-flag" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb9b20c0dd58e4c2e991c8d203bbeb76c11304d1011659686b5b644bc29aa478" +dependencies = [ + "clap", + "log", +] + +[[package]] +name = "clap_builder" +version = "4.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "env_filter" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psb-qaqc" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "clap-verbosity-flag", + "clap_derive", + "env_logger", + "log", + "regex", + "semver", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + +[[package]] +name = "serde" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.204" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0209b68b3613b093e0ec905354eccaedcfe83b8cb37cbdeae64026c3064c16" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b18c43a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "psb-qaqc" +version = "0.1.0" +edition = "2021" + +[dependencies] +clap = { version = "4.5", features = ["cargo", "derive"] } +clap_derive = "4.5" +log = "0.4" +env_logger = "0.11.3" +clap-verbosity-flag = "2.2" +anyhow = "1.0" +chrono = { version = "0.4", features = ["serde"] } +semver = "1.0" +regex = "1.10" diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9a365bd --- /dev/null +++ b/src/main.rs @@ -0,0 +1,341 @@ +use core::str; +use std::{ + collections::HashMap, + fmt::Display, + fs::File, + io::{BufRead, BufReader}, + path, + str::FromStr, +}; + +use anyhow::{anyhow, Context, Result}; +use chrono::{DateTime, Local, Utc}; +use clap::Parser; +use log::{debug, error, info, trace, warn}; +use regex::Regex; +use semver::Version; + +/// Parse master jathub logfile for PS Board QAQC and write out to CSV. +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// Master log file. + master_log: path::PathBuf, + #[command(flatten)] + verbose: clap_verbosity_flag::Verbosity, +} + +/// Layer +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +enum PositionLayer { + A, + B, +} + +impl Display for PositionLayer { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let c = match self { + PositionLayer::A => "A", + PositionLayer::B => "B", + }; + write!(f, "{}", c) + } +} + +impl FromStr for PositionLayer { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + "A" => Ok(Self::A), + "B" => Ok(Self::B), + _ => Err(anyhow!("Invalid value")), + } + } +} + +/// Where PS Board is placed while QAQC. +/// TODO: name +#[derive(Debug, PartialEq, Eq, Hash, Clone)] +struct Position { + major: PositionLayer, + minor: u8, + patch: u8, +} + +impl Display for Position { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}-{}-{}", self.major, self.minor, self.patch) + } +} + +impl FromStr for Position { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + let mut count = 0; + let mut major_pre = None; + let mut minor_pre = None; + let mut patch_pre = None; + for part in s.split('-') { + count += 1; + match count { + 1 => major_pre = Some(part.parse()?), + 2 => minor_pre = Some(part.parse()?), + 3 => patch_pre = Some(part.parse()?), + _ => (), + } + } + if count > 3 { + return Err(anyhow!("Invalid Position format")); + } + + Ok(Position { + major: major_pre.context("No major")?, + minor: minor_pre.context("No minor")?, + patch: patch_pre.context("No patch")?, + }) + } +} + +/// PSB ID printed on the tape on the board. +#[derive(Debug, PartialEq)] +struct PsbId { + id: u8, +} + +impl PsbId { + /// Without validation. + pub fn new(id: u8) -> Self { + PsbId { id } + } +} + +impl FromStr for PsbId { + type Err = anyhow::Error; + + fn from_str(s: &str) -> std::result::Result { + let num: u8 = s.parse()?; + // TODO: add validation + warn!("No validation implemented"); + Ok(PsbId::new(num)) + } +} + +/// QAQC results for each boards extracted from master log. +/// TODO: specify each filed types.(maybe multi values) +#[derive(Debug)] +struct BoardResult { + id: PsbId, + qspif: bool, + qspip: bool, + recov: bool, + clock: bool, + regac: bool, + asdtp: bool, + done: bool, +} + +/// Full result for a single QAQC run from a log file on JATHub master. +#[derive(Debug)] +struct MasterLogResult { + version: Version, + datetime: DateTime, + shifter: String, + board_results: HashMap, +} + +/// Get version of shift script. +fn extract_version(line: &str) -> Result { + Ok(line + .split_whitespace() + .nth(2) + .context("Invalid log format(version)")? + .parse()?) +} + +/// Get shifters from shifter line +fn extract_shifter_line(line: &str) -> Result { + let re = Regex::new(r"^Shifters: (.+)$").unwrap(); + let caps = re.captures(line).unwrap(); + trace!("Regex {:?}", caps); + Ok(caps.get(1).unwrap().as_str().to_owned()) +} + +/// Get position and PSB ID pair from a master log line. +fn extract_position_id(line: &str) -> Result<(Position, PsbId)> { + let re = Regex::new(r"Position / assigned-ID : (.+) / (.+)").unwrap(); + let caps = re.captures(line).context("No capture")?; + let pos = Position::from_str(caps.get(1).unwrap().into())?; + let id = PsbId::from_str(caps.get(2).unwrap().into())?; + Ok((pos, id)) +} + +impl MasterLogResult { + /// Parse log file on master jathub. + fn parse_file(file: impl BufRead) -> Result { + let mut lines = file.lines(); + + let version = { + let line = lines.next().context("Invalid log format(no versions)")??; + extract_version(&line)? + }; + + let _sep = lines.next().context("Invalid log format(separator)")??; + + let datetime: DateTime = { + let line = lines + .next() + .context("Invalid log format(no datetime line)")??; + DateTime::parse_from_str(&line, "Date: %+") + .context("Invalid datetime format (must be ISO 8601)")? + .to_utc() + }; + + let shifter = { + let line = lines + .next() + .context("Invalid log format(no shifter line)")??; + extract_shifter_line(&line)? + }; + + let _sep = lines.next().context("Invalid log format")?; + + if !lines + .next() + .context("Invalid log format")?? + .starts_with("PBS Assignment:") + { + return Err(anyhow!("Invalid log format")); + } + + let mut assignments = HashMap::new(); + for i in 0..18 { + let line = lines.next().context("Unexpected EOF")??; + if line.starts_with("=========") { + debug!("End of assignments"); + break; + } + let (pos, id) = extract_position_id(&line)?; + match assignments.insert(pos.clone(), id) { + None => (), + Some(old_id) => { + return Err(anyhow!( + "Value already exists on row {}: {:?} => {:?}", + i, + pos, + old_id + )); + } + }; + } + trace!("Read all PBS assignments"); + info!("{:?}", assignments); + + todo!() + } +} + +fn main() -> Result<()> { + let args = Args::parse(); + env_logger::Builder::new() + .filter_level(args.verbose.log_level_filter()) + .init(); + debug!("Args: {:?}", args); + println!("Hello, world!"); + + let file = File::open(args.master_log)?; + let mut reader = BufReader::new(file); + let result = MasterLogResult::parse_file(reader)?; + + Ok(()) +} + +#[cfg(test)] +mod test { + use std::str::FromStr; + + use chrono::{DateTime, Utc}; + + use crate::{extract_position_id, extract_shifter_line, Position, PositionLayer, PsbId}; + + #[test] + fn parse_position() { + assert_eq!( + Position::from_str("A-1-4").unwrap(), + Position { + major: PositionLayer::A, + minor: 1, + patch: 4 + } + ); + assert_eq!( + Position::from_str("A-0-9").unwrap(), + Position { + major: PositionLayer::A, + minor: 0, + patch: 9 + } + ); + assert_ne!( + Position::from_str("A-1-4").unwrap(), + Position { + major: PositionLayer::A, + minor: 0, + patch: 2 + } + ); + } + + #[test] + fn parse_datetime() { + assert!(DateTime::parse_from_str("2024-06-20T08:42:01+0000", "%+").is_ok()); + assert!(DateTime::parse_from_str("Date: 2024-06-20T08:42:01+0000", "Date: %+").is_ok()); + } + + #[test] + fn parse_shifter_line() { + assert_eq!(extract_shifter_line("Shifters: foo").unwrap(), "foo"); + assert_eq!( + extract_shifter_line("Shifters: foo bar").unwrap(), + "foo bar" + ); + } + + #[test] + fn parse_pos_id_line() { + assert_eq!( + extract_position_id("Position / assigned-ID : A-0-0 / 000004").unwrap(), + ( + Position { + major: PositionLayer::A, + minor: 0, + patch: 0, + }, + PsbId::new(4) + ) + ); + assert_eq!( + extract_position_id("Position / assigned-ID : A-1-7 / 000108").unwrap(), + ( + Position { + major: PositionLayer::A, + minor: 1, + patch: 7, + }, + PsbId::new(108) + ) + ); + assert_ne!( + extract_position_id("Position / assigned-ID : A-1-7 / 000108").unwrap(), + ( + Position { + major: PositionLayer::A, + minor: 0, + patch: 7, + }, + PsbId::new(106) + ) + ); + } +}