From 5c9246f5331fe579cd686a987ae83e46f0cc868b Mon Sep 17 00:00:00 2001 From: Bernhard Guillon Date: Wed, 17 Sep 2025 19:42:01 +0200 Subject: future-me: Traverse all logs to be able to print the full log This is how it looks now :) ------------------------------------------------------------------------- 65e8eaa new Bernhard Guillon Hello second bug 2025-09-16 19:44:28 This is the second bug yey - looks nice ------------------------------------------------------------------------- 79708a9 new Bernhard Guillon Hello world 2025-09-16 19:43:17 this is my first bug report --- Cargo.lock | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 184 ++++++++++++++++++++++++++++++++----------- 3 files changed, 395 insertions(+), 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f7be8a3..3de7ead 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,26 +2,150 @@ # It is not intended for manual editing. version = 4 +[[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 = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cc" +version = "1.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "future-me" version = "0.1.0" dependencies = [ + "chrono", "serde", "serde_json", ] +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "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 = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + [[package]] name = "memchr" version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +[[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.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + [[package]] name = "proc-macro2" version = "1.0.101" @@ -40,6 +164,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + [[package]] name = "ryu" version = "1.0.20" @@ -89,6 +219,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "syn" version = "2.0.106" @@ -105,3 +241,121 @@ name = "unicode-ident" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml index ab405c3..97d761a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,6 @@ version = "0.1.0" edition = "2024" [dependencies] +chrono = "0.4.42" serde = { version = "1.0.145", features = ["derive"] } serde_json = "1.0.145" diff --git a/src/main.rs b/src/main.rs index a25a5f7..e070af3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,28 @@ +// SPDX MIT Bernhard Guillon 2025 +// +// As this project is currently in research please just ignore +// all clones and memory copies ^^ As on all my rust projects +// I follow the copy all and fix it later approach. To make +// rust a nice prototyping languague. If you don't like that +// approach you do yours and I do mine ^^ + use serde::{Deserialize, Serialize}; -//use serde_json::Result; use std::time::{SystemTime, UNIX_EPOCH}; use std::{ env::{temp_dir, var}, fs::File, io::Read, process::Command, + process::Stdio, }; use std::fs; +use std::fmt; +use std::num::ParseIntError; +use std::io::Write; +use std::collections::HashMap; +use chrono::{NaiveDateTime, Utc, TimeZone}; -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Default, Clone)] struct BugReport { timestamp: String, status: String, @@ -56,57 +69,140 @@ fn new_bug() { print!("{}", j); } -fn show() { - let reports = get_reports(); - for report in reports { - //println!("{:?}", report); - println!("================================================================="); - println!("{} | {}", report.status, report.title); - println!("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); - for l in report.description { - println!("{}", l); +#[derive(Debug)] +enum GitError { + GitLog(String), + Parse(ParseIntError) +} +impl fmt::Display for GitError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + GitError::GitLog(s) => write!(f, "{}", s), + GitError::Parse(s) => write!(f, "{}", s), } } } -fn get_reports() -> Vec { - let mut ret: Vec = Vec::new(); - let output = Command::new("git") - .arg("ls-tree") - .arg("--full-tree") - .arg("-r") +impl From for GitError { + fn from(err: ParseIntError) -> GitError { + GitError::Parse(err) + } +} + +impl std::error::Error for GitError {} + +#[derive(Debug, Default, Clone)] +struct GitLog{ + timestamp: u64, + hash: String, + author_name: String, + author_email: String, + blob_object: String, +} + +fn collect_reachable_objects() -> Result<(Vec, String), GitError> { + // TODO: until we have the need for archiving old stuff to archive + // we can guarantee that all objects collected exist and are + // unchanged. As soon as we support archives we might need to + // change this approach a bit. + + // As we use the first two chars of a git hash as directory name and + // the other part as file name we are able to regenerate the hash + // from the changed file path. Git log can provide the file path + // as well as all other needed information. + let mut git_logs: Vec = Vec::new(); + let mut blobs = String::default(); + let logs = Command::new("git") + .arg("log") + .arg("--pretty=format:%H#%an#%ae#%at") + .arg("--name-only") .arg("refs/notes/devtools/future-me") .output() - .expect("Error with git ls-tree"); - //println!("{}", output.status); - if output.status.success() { - let lines = String::from_utf8_lossy(&output.stdout); - for line in lines.lines() { - if let Some(after_blob) = line.split_once("blob ") { - if let Some((hash, _)) = after_blob.1.split_once('\t') { - let blob = Command::new("git") - .arg("cat-file") - .arg("-p") - .arg(hash) - .output() - .expect("Error with git cat-file"); - if blob.status.success() { - let lines = String::from_utf8_lossy(&blob.stdout); - // TODO: error handling - let bug_report: BugReport = serde_json::from_str(&lines).unwrap(); - ret.push(bug_report); - } - else { - println!("{}", String::from_utf8_lossy(&blob.stderr)); - } - } - } + .expect("Error with git log"); + if !logs.status.success() { + GitError::GitLog(String::from_utf8_lossy(&logs.stderr).to_string()); + } + let lines = String::from_utf8_lossy(&logs.stdout); + let mut git_log = GitLog::default(); + for (i, line) in lines.lines().enumerate() { + match i%3 { + 0 => { + let parts = line.split("#"); + for (i, part) in parts.enumerate() { + match i%4 { + 0 => git_log.hash=part.to_string(), + 1 => git_log.author_name = part.to_string(), + 2 => git_log.author_email = part.to_string(), + 3 => git_log.timestamp = part.parse::()?, + _ => todo!(), // TODO: why we need this? + } + } + }, + 1 => { + git_log.blob_object = line.replace("/", ""); + if blobs.len() > 0 { + blobs = blobs + "\n" + &git_log.blob_object; + } + else { + blobs = blobs + &git_log.blob_object; + } + }, + 2 => (), // commit seperator + _ => todo!(), // TODO: why we need this? + } + if i>=1 && i%3 ==1 { + git_logs.push(git_log.clone()); + } + } + return Ok((git_logs, blobs)); +} + +fn show() { + // TODO: split this function to collect the reports and to show them + // in the desired format. + // For a short log we don't need to do all this extra work ^^ + let (logs, blobs) = collect_reachable_objects().unwrap(); + let mut files = Command::new("git") + .arg("cat-file") + .arg("--batch") + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Error with git cat-files --batch"); + + let mut stdin = files.stdin.take().expect("Failed to open stdin"); + std::thread::spawn(move || { + stdin.write_all(blobs.as_bytes()).expect("Failed to write to stdin"); + }); + + let output = files.wait_with_output().expect("Failed to write to stdout"); + let objects = String::from_utf8_lossy(&output.stdout); + let mut map: HashMap = HashMap::new(); + let mut bug_report: BugReport = BugReport::default(); + let mut hash: String = String::default(); + for (i, object) in objects.lines().enumerate() { + match i%3 { + 0 => hash = object.split(" ").next().unwrap().to_string(), + 1 => bug_report = serde_json::from_str(&object).unwrap(), + 2 => (), + _ => todo!(), + } + if i>=1 && i%3 ==1 { + map.insert(hash.clone(), bug_report.clone()); } } - else { - println!("{}", String::from_utf8_lossy(&output.stderr)); + for log in logs { + let entry = map[&log.blob_object].clone(); + let datetime = Utc.timestamp_opt(log.timestamp as i64, 0).unwrap(); + // TODO: do we really need to be able to convert times? Or should we just + // collect the git time with the possibilites git gives us? + // I don't like the chrono dependency :/ + println!("-------------------------------------------------------------------------"); + println!{"{} {} {} {}\t{}", &log.hash[0..7], entry.status, log.author_name, entry.title, datetime.format("%Y-%m-%d %H:%M:%S")}; + for line in entry.description { + println!("{}", line); + } } - ret } fn main() { -- cgit v1.2.3