aboutsummaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs561
1 files changed, 0 insertions, 561 deletions
diff --git a/src/main.rs b/src/main.rs
deleted file mode 100644
index c64f6fe..0000000
--- a/src/main.rs
+++ /dev/null
@@ -1,561 +0,0 @@
-// 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 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::{Utc, TimeZone};
-use std::env::args;
-
-#[derive(Serialize, Deserialize, Debug, Default, Clone)]
-struct BugReport {
- timestamp: String,
- status: String,
- title: String,
- description: Vec<String>,
- tags: Option<String>,
- version: String
-}
-
-fn new_bug() -> String {
- let editor = var("EDITOR").unwrap();
- let mut file_path = temp_dir(); // TODO: figgure out how to get .git dir in a save manner
- file_path.push("NEW_BUG_REPORT");
- File::create(&file_path).expect("Could not create file");
-
- Command::new(editor)
- .arg(&file_path)
- .status()
- .expect("Something went wrong");
-
- let mut new_bug_report = String::new();
- let _ = File::open(&file_path)
- .expect("Could not open file")
- .read_to_string(&mut new_bug_report);
- let _ = fs::remove_file(&file_path);
- let mut header = "";
- let mut description: Vec::<String> = Vec::new();
- for (i, line) in new_bug_report.lines().enumerate() {
- match i {
- 0 => header = line,
- 1 => (),
- _ => description.push(line.to_string()),
- }
- }
- let report = BugReport {
- timestamp: SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs().to_string(),
- title: header.to_owned(),
- description: description.to_owned(),
- status: "new".to_owned(),
- tags: None,
- version: "v1".to_owned(),
- };
- serde_json::to_string(&report).unwrap()
-}
-
-#[derive(Debug)]
-enum GitError {
- GitLog(String),
- Parse(ParseIntError),
- UnknownRef,
-}
-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),
- GitError::UnknownRef => write!(f, "Unknown reference"),
- }
- }
-}
-
-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 log");
- if !logs.status.success() {
- GitError::GitLog(String::from_utf8_lossy(&logs.stderr).to_string());
- //return 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();
- //println!("logs {:?}", logs);
- //println!("blobs {:?}", blobs);
- 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() {
- //println!("object |{}|", object);
- match i%2 {
- 0 => hash = object.split(" ").next().unwrap().to_string(),
- 1 => {
- //println!("|{:?}|", object);
- bug_report = serde_json::from_str(&object).unwrap(); map.insert(hash.clone(), bug_report.clone());
- //println!("INSERT")
- },
- //2 => (),
- _ => todo!(),
- }
-// if i>=1 && i%3 ==1 {
-// println!("insert bug report");
-// map.insert(hash.clone(), bug_report.clone());
-// }
- }
- for log in logs {
- //println!("{:?}", log);
- 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);
- }
- }
-}
-
-
-//create-new-bug() {
-// readonly bug="$1"
-// last_ref=$(git show-ref refs/notes/devtools/future-me | cut -d ' ' -f 1)
-// echo $last_ref
-// if [ -n "$last_ref" ]
-// then
-// git read-tree $last_ref
-// else
-// git read-tree --empty
-// fi
-//
-// object_id=$(echo "$1" | git hash-object -w --stdin)
-// echo $object_id
-// file_path=${object_id:0:2}/${object_id:2}
-// git update-index --add --cacheinfo 100644 $object_id $file_path
-// tree_id=$(git write-tree)
-// files_to_unstage=$(git update-index --refresh | cut -d ' ' -f 1 | cut -d ':' -f 1)
-// git update-index --remove $files_to_unstage
-// if [ -n "$last_ref" ]
-// then
-// add_parent="$last_ref"
-// commit_id=$(echo 'future-me: created a new bug for you' | git commit-tree $tree_id -p $add_parent)
-// else
-// commit_id=$(echo 'future-me: created a new bug for you' | git commit-tree $tree_id)
-// fi
-// git update-ref refs/notes/devtools/future-me $commit_id
-//}
-//
-// new)
-// echo create new bug "$payload" #"${*:2}"
-// check-git-status
-// last_tree="$(git --no-pager log -1 --format="%H" | tr --delete '\n')"
-// echo $last_tree
-// create-new-bug "$payload"
-// git read-tree "$last_tree"
-// ;;
-
-fn hash_to_path(hash: &str) -> String {
- format!("{}/{}", &hash[..2], &hash[2..])
-}
-
-fn create_new_bug() {
- // first of all check if there is anything staged as we mess with the trees and staging area
- // use check_status for that
- // after that get the current tree get_current_tree and save it. We need to guarantee that
- // we switch back to that as best as we can if anything went bad.
- //
- // get_last_ref (show-ref ref/notes/devtools/future-me)
- // if empty
- // call init future-me which creates a new tree
- // with git read-tree --empty
- // otherwise read the current tree with git read-tree future-me-hash
- //
- // let the user enter the bug report and creat the json string
- // then
- // create an git object with git hash-object -w --stdin
- // collect the output as object hash
- // create a file path string with dd/fffff
- // update the index
- // with git update-index --add --cacheinfo 100644 hash dd/fffff
- // write a new tree object and collect the hash from the output
- // git write-tree
- // reset our changes to the staging area with
- // git update-index --remove
- // files_to_unstage=$(git update-index --refresh | cut -d ' ' -f 1 | cut -d ':' -f 1)
- // git update-index --remove $files_to_unstage // TODO: check if we can just unstage the
- // file as we know the path?
- // If we are the first and there is no parent aka future-me ref was empty
- // commit_id=$(echo 'future-me: created a new bug for you' | git commit-tree $tree_id)
- // else add the parent
- // commit_id=$(echo 'future-me: created a new bug for you' | git commit-tree $tree_id -p $add_parent)
- // cool now update the ref
- //
- // git update-ref refs/notes/devtools/future-me $commit_id
- //
- // switch back to what ever was the working tree we got from get_current_tree
-
-
- check_status();
- let save_current_tree = get_current_tree().unwrap();
- let future_me_ref = match get_last_ref() {
- Ok(hash) => {read_tree(&hash); Some(hash)},
- Err(GitError::UnknownRef) => {create_new_tree(); None},
- Err(_) => panic!("fixme"),
- };
- let bug_report = new_bug();
- let bug_object = create_object(bug_report.clone()).unwrap();
- let path = hash_to_path(&bug_object);
- stage_object(&bug_object, &path);
- let tree_object = write_tree();
- let commit_object = match future_me_ref {
- Some(parent) => commit(tree_object, Some(parent)).unwrap(),
- None => commit(tree_object, None).unwrap(),
- };
- let files_to_unstage = get_files_to_unstage();
- unstage_object(&files_to_unstage);
- update_ref(&commit_object);
- read_tree(&save_current_tree);
- println!("hello new bug");
-}
-
-fn get_files_to_unstage() -> String {
- let cmd = Command::new("git")
- .arg("update-index")
- .arg("--refresh")
- .output()
- .expect("Error with update-index");
- match cmd.status.code() {
- Some(1) => (),
- Some(0) => (),
- Some(s) => panic!("Fixme status code: {}",s),
- None => panic!("Fixme git update-index"),
- }
- let lines = String::from_utf8_lossy(&cmd.stdout).to_string();
- let mut files = String::default();
- for line in lines.lines() {
- files = files + " " +line.split(":").next().unwrap();
- }
- files[1..].to_string()
-}
-
-fn unstage_object(path: &str) {
- let cmd = Command::new("git")
- .arg("update-index")
- .arg("--remove")
- .arg(path)
- .output()
- .expect("Error with update-index");
- if !cmd.status.success() {
- panic!("FIXME unstage_object failed");
- }
-}
-
-fn update_ref(object: &str) {
- let cmd = Command::new("git")
- .arg("update-ref")
- .arg("refs/notes/devtools/future-me")
- .arg(object)
- .output()
- .expect("Error with update-index");
- if !cmd.status.success() {
- panic!("FIXME: update ref failed");
- }
-}
-
-fn stage_object(hash: &str, path: &str) {
- // with git update-index --add --cacheinfo 100644 hash dd/fffff
- let cmd = Command::new("git")
- .arg("update-index")
- .arg("--add")
- .arg("--cacheinfo")
- .arg("100644")
- .arg(hash)
- .arg(path)
- .output()
- .expect("Error with update-index");
- if !cmd.status.success() {
- panic!("FIXME");
- }
-}
-
-fn create_object(object: String) -> Result<String, GitError> {
-
- // create an git object with git hash-object -w --stdin
- let mut files = Command::new("git")
- .arg("hash-object")
- .arg("-w")
- .arg("--stdin")
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .expect("Error with git hash-ojbect");
-
- let mut stdin = files.stdin.take().expect("Failed to open stdin");
- std::thread::spawn(move || {
- stdin.write_all(object.as_bytes()).expect("Failed to write to stdin");
- });
-
- let output = files.wait_with_output().expect("Failed to write to stdout");
- let lines = String::from_utf8_lossy(&output.stdout).to_string();
- Ok(lines.split_whitespace().next().unwrap().to_string())
-}
-
-fn commit(object: String, parent: Option<String>) -> Result<String, GitError> {
- // commit_id=$(echo 'future-me: created a new bug for you' | git commit-tree $tree_id)
- let mut files = match parent {
- Some(parent) =>
- Command::new("git")
- .arg("commit-tree")
- .arg(object)
- .arg("-p")
- .arg(parent)
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .expect("Error with git commit-tree"),
- None =>
- Command::new("git")
- .arg("commit-tree")
- .arg(object)
- .stdin(Stdio::piped())
- .stdout(Stdio::piped())
- .spawn()
- .expect("Error with git commit-tree"),
- };
-
- let mut stdin = files.stdin.take().expect("Failed to open stdin");
- std::thread::spawn(move || {
- stdin.write_all("future-me: created a new bug for you".as_bytes()).expect("Failed to write to stdin");
- });
-
- let output = files.wait_with_output().expect("Failed to write to stdout");
- let lines = String::from_utf8_lossy(&output.stdout).to_string();
- Ok(lines.split_whitespace().next().unwrap().to_string())
-}
-
-fn get_last_ref() -> Result<String, GitError> {
- let cmd = Command::new("git")
- .arg("show-ref")
- .arg("refs/notes/devtools/future-me")
- .output()
- .expect("Error with git show-ref");
- if !cmd.status.success() {
- //return GitError::GitLog(String::from_utf8_lossy(&cmd.stderr).to_string());
- GitError::GitLog(String::from_utf8_lossy(&cmd.stderr).to_string());
- }
- let lines = String::from_utf8_lossy(&cmd.stdout);
-
- match lines.split_whitespace().next() {
- Some(line) => Ok(line.to_string()),
- None => Err(GitError::UnknownRef),
- }
-}
-
-fn get_current_tree() -> Result<String, GitError>{
- let cmd = Command::new("git")
- .arg("log")
- .arg("-1")
- .arg("--format=%H")
- .output()
- .expect("Error with git log");
- if !cmd.status.success() {
- GitError::GitLog(String::from_utf8_lossy(&cmd.stderr).to_string());
- }
- let lines = String::from_utf8_lossy(&cmd.stdout);
- Ok(lines.trim().to_string())
-}
-
-fn write_tree() -> String {
- let cmd = Command::new("git")
- .arg("write-tree")
- .output()
- .expect("Error with git write-tree");
- if !cmd.status.success() {
- panic!("{}", String::from_utf8_lossy(&cmd.stderr));
- }
- let lines = String::from_utf8_lossy(&cmd.stdout);
- lines.trim().to_string()
-}
-
-fn create_new_tree() {
- let cmd = Command::new("git")
- .arg("read-tree")
- .arg("--empty")
- .output()
- .expect("Error with git read-tree");
- if !cmd.status.success() {
- panic!("{}", String::from_utf8_lossy(&cmd.stderr));
- }
-}
-
-fn read_tree(tree: &str) {
- let cmd = Command::new("git")
- .arg("read-tree")
- .arg(tree)
- .output()
- .expect("Error with git read-tree");
- if !cmd.status.success() {
- panic!("{}", String::from_utf8_lossy(&cmd.stderr));
- }
-}
-
-fn check_status() {
- let logs = Command::new("git")
- .arg("status")
- .arg("--porcelain")
- .output()
- .expect("Error with git status");
- if !logs.status.success() {
- GitError::GitLog(String::from_utf8_lossy(&logs.stderr).to_string());
- }
- let lines = String::from_utf8_lossy(&logs.stdout);
- for line in lines.lines() {
- println!("{}", line);
- if line.starts_with(['M', 'A']) {
- panic!("You first need to clean you git staging status to use future-me");
- }
- }
-}
-
-fn print_usage() {
- println!("usage: future-me <command>\n");
- println!("commands:");
- println!("show - shows the log");
- println!("new - enter new bug");
- println!("help - get this help");
-}
-
-enum Cmd{
- Show,
- New,
- Check,
-}
-
-fn process_args() -> Cmd {
- let myargs: Vec<String> = args().collect();
- println!("{:?}", myargs);
- if myargs.len() != 2 {
- print_usage();
- }
- let unwraped = myargs.get(1).unwrap();
- match unwraped.as_str() {
- "show" => Cmd::Show,
- "new" => Cmd::New,
- "check" => Cmd::Check,
- _ => {print_usage(); todo!()},
- }
-}
-
-fn main() {
- match process_args() {
- Cmd::Show => show(),
- Cmd::New => create_new_bug(),
- Cmd::Check=> {
- println!("{}", get_files_to_unstage());
- //let tree = get_current_tree().unwrap();
- //println!("{}", get_last_ref().unwrap());
- //read_tree(&tree);
- }
- }
-}