aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
authorBernhard Guillon <Bernhard.Guillon@begu.org>2025-09-17 19:42:01 +0200
committerBernhard Guillon <Bernhard.Guillon@begu.org>2025-09-17 19:42:01 +0200
commit5c9246f5331fe579cd686a987ae83e46f0cc868b (patch)
tree05e6cd42fc8bed15bcfde53497a4e066987d2c0c /src/main.rs
parent99f8dcce0276dbc7df2689e8f8b101ab31b4746d (diff)
downloadfuture-me-5c9246f5331fe579cd686a987ae83e46f0cc868b.tar.gz
future-me-5c9246f5331fe579cd686a987ae83e46f0cc868b.zip
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
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs184
1 files changed, 140 insertions, 44 deletions
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<BugReport> {
- let mut ret: Vec<BugReport> = Vec::new();
- let output = Command::new("git")
- .arg("ls-tree")
- .arg("--full-tree")
- .arg("-r")
+impl From<ParseIntError> 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<GitLog>, 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<GitLog> = 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::<u64>()?,
+ _ => 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<String, BugReport> = 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() {