summaryrefslogtreecommitdiffstats
path: root/pkgs/by-name/ro/rollup/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'pkgs/by-name/ro/rollup/src/parse.rs')
-rw-r--r--pkgs/by-name/ro/rollup/src/parse.rs289
1 files changed, 289 insertions, 0 deletions
diff --git a/pkgs/by-name/ro/rollup/src/parse.rs b/pkgs/by-name/ro/rollup/src/parse.rs
new file mode 100644
index 00000000..ceb9d44f
--- /dev/null
+++ b/pkgs/by-name/ro/rollup/src/parse.rs
@@ -0,0 +1,289 @@
+use core::fmt;
+use std::{
+ fs,
+ path::{Path, PathBuf},
+ process::Command,
+};
+
+use anyhow::{Context, Result};
+use pretty_assertions::assert_eq;
+
+pub(crate) struct Patch {
+ pub(crate) files: Vec<FileDiff>,
+ pub(crate) commit_message: String,
+}
+
+impl fmt::Display for Patch {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ for file in &self.files {
+ write!(f, "{}", file)?;
+ }
+
+ Ok(())
+ }
+}
+
+#[derive(Clone)]
+pub(crate) enum FileDiff {
+ Add {
+ path: PathBuf,
+ diff: String,
+ },
+ Remove {
+ path: PathBuf,
+ diff: String,
+ index: String,
+ deleted: String,
+ },
+ Change {
+ from: PathBuf,
+ to: PathBuf,
+ diff: String,
+ index: String,
+ },
+}
+impl FileDiff {
+ fn from_parts(
+ from: PathBuf,
+ to: PathBuf,
+ diff: String,
+ index: Option<String>,
+ deleted: Option<String>,
+ ) -> Self {
+ let diff = diff.trim().to_owned();
+
+ if from.display().to_string() == "/dev/null" {
+ Self::Add { path: to, diff }
+ } else if to.display().to_string() == "/dev/null" {
+ Self::Remove {
+ path: from,
+ diff,
+ index: index.unwrap(),
+ deleted: deleted.unwrap(),
+ }
+ } else {
+ Self::Change {
+ from,
+ to,
+ diff,
+ index: index.unwrap(),
+ }
+ }
+ }
+}
+
+impl fmt::Display for FileDiff {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ FileDiff::Add { path, diff } => {
+ // writeln!(f, "diff --git /dev/null b/{}", path.display())?;
+ writeln!(f, "--- /dev/null")?;
+ writeln!(f, "+++ b/{}", path.display())?;
+ writeln!(f, "{}", diff)?;
+ }
+ FileDiff::Remove {
+ path,
+ diff,
+ index,
+ deleted,
+ } => {
+ writeln!(f, "diff --git a/{} b/{}", path.display(), path.display())?;
+ writeln!(f, "deleted {}", deleted)?;
+ writeln!(f, "index {}", index)?;
+ writeln!(f, "+++ /dev/null")?;
+ writeln!(f, "{}", diff)?;
+ }
+ FileDiff::Change {
+ from,
+ to,
+ diff,
+ index,
+ } => {
+ writeln!(f, "diff --git a/{} b/{}", from.display(), to.display())?;
+ writeln!(f, "index {}", index)?;
+ writeln!(f, "--- a/{}", from.display())?;
+ writeln!(f, "+++ b/{}", to.display())?;
+ writeln!(f, "{}", diff)?;
+ }
+ }
+
+ Ok(())
+ }
+}
+
+macro_rules! expect {
+ (if $gate:ident -> $prefix:literal from $line:expr; assign |$strp_line:ident| $callback:block) => {
+ expect!(@base continue; if $gate -> $prefix from $line; assign |$strp_line| $callback)
+ };
+ (@no_wrap if $gate:ident -> $prefix:literal from $line:expr; assign |$strp_line:ident| $callback:block) => {
+ expect!(@base (); if $gate -> $prefix from $line; assign |$strp_line| $callback)
+ };
+ (@base $continue:expr; if $gate:ident -> $prefix:literal from $line:expr; assign |$strp_line:ident| $callback:block) => {
+ if $gate.is_none() {
+ let $strp_line: &str = $line
+ .strip_prefix($prefix)
+ .expect(concat!(
+ "Expectected '",
+ $prefix,
+ "' prefix, while trying to fill '",
+ stringify!($gate),
+ "'"
+ ))
+ .trim();
+ $gate = Some((|$strp_line: &str| $callback)($strp_line));
+ $continue;
+ }
+ };
+}
+
+pub(crate) fn parse_file(file: &Path, debug: bool) -> Result<Patch> {
+ let data = Command::new("filterdiff")
+ .arg(&file)
+ .output()
+ .context("Failed to run `filterdiff` for patch analysis")?;
+
+ let filterdiff_output = String::from_utf8(data.stdout).unwrap();
+ if debug {
+ eprintln!("{}", filterdiff_output);
+ eprintln!("****************************");
+ }
+
+ let commit_message = {
+ let raw_message = fs::read_to_string(file)?;
+
+ let mut commit = None;
+ let mut date = None;
+ let mut author = None;
+ let mut final_message = None;
+ for line in raw_message.lines() {
+ expect!(if commit -> "From " from line; assign |strp_line| {
+ let (id, fake_date) = strp_line.split_once(' ').unwrap();
+ assert_eq!(fake_date, "Mon Sep 17 00:00:00 2001");
+ id.to_owned()
+ });
+ expect!(if author -> "From: " from line; assign |strp_line| {
+ strp_line.to_owned()
+ });
+ expect!(if date -> "Date: " from line; assign |strp_line| {
+ strp_line.to_owned()
+ });
+ expect!(if final_message -> "Subject: " from line; assign |strp_line| {
+ if strp_line.starts_with('[') {
+ let chars = strp_line.chars().take_while(|ch| *ch != ']').collect::<String>();
+ assert_eq!(&chars[1..=5], "PATCH");
+ strp_line[chars.len()+1..].to_owned()
+ } else {
+ unreachable!("Email subjects always start with [PATCH x/y]");
+ }
+ });
+ if line == "---" {
+ break;
+ }
+ final_message.as_mut().unwrap().push_str(line);
+ final_message.as_mut().unwrap().push('\n');
+ }
+
+ if debug {
+ eprintln!("Patch data:");
+ eprintln!(" Author: {}", author.unwrap());
+ eprintln!(" Commit: {}", commit.unwrap());
+ eprintln!(" Date: {}", date.unwrap());
+ for line in final_message.as_ref().unwrap().lines() {
+ eprintln!(" {}", line);
+ }
+ eprintln!("****************************");
+ }
+
+ final_message.unwrap().trim().to_owned()
+ };
+
+ let mut diff = Patch {
+ files: vec![],
+ commit_message,
+ };
+ let mut fd_from = None;
+ let mut fd_to = None;
+ let mut fd_diff: Option<String> = None;
+ let mut fd_index: Option<String> = None;
+ let mut fd_deleted: Option<String> = None;
+ let mut git_diff = None;
+ for (idx, line) in filterdiff_output.lines().enumerate() {
+ if debug {
+ eprintln!("{}", line);
+ }
+
+ if line.starts_with("diff --git") {
+ if idx != 0 {
+ diff.files.push(FileDiff::from_parts(
+ fd_from.take().unwrap(),
+ fd_to.take().unwrap(),
+ fd_diff.take().unwrap(),
+ fd_index.take(),
+ fd_deleted.take(),
+ ));
+ }
+ git_diff = Some(line.to_owned());
+ continue;
+ }
+
+ if line.starts_with("deleted") {
+ expect!(if fd_deleted -> "deleted" from line; assign |strp_line| {
+ strp_line.to_owned()
+ });
+ }
+
+ if line.starts_with("index") {
+ expect!(if fd_index -> "index" from line; assign |strp_line| {
+ strp_line.to_owned()
+ });
+ }
+
+ if fd_deleted.is_some() {
+ let git_diff = git_diff.clone().unwrap();
+ expect!(@no_wrap if fd_from -> "diff --git a/" from git_diff; assign |strp_line| {
+ let (from, _to) = strp_line.split_once(' ').unwrap();
+ PathBuf::from(from)
+ });
+ } else {
+ expect!(if fd_from -> "---" from line; assign |strp_line| {
+ if strp_line != "/dev/null" {
+ PathBuf::from(strp_line.strip_prefix("a/").unwrap())
+ } else {
+ PathBuf::from(strp_line)
+ }
+ });
+ }
+ expect!(if fd_to -> "+++" from line; assign |strp_line| {
+ if strp_line != "/dev/null" {
+ PathBuf::from(strp_line.strip_prefix("b/").unwrap())
+ } else {
+ PathBuf::from(strp_line)
+ }
+ });
+
+ match fd_diff.as_mut() {
+ Some(str) => {
+ str.push_str(line);
+ str.push('\n');
+ }
+ None => {
+ let mut base = line.to_owned();
+ base.push('\n');
+ fd_diff = Some(base);
+ }
+ }
+ }
+ diff.files.push(FileDiff::from_parts(
+ fd_from.take().unwrap(),
+ fd_to.take().unwrap(),
+ fd_diff.take().unwrap(),
+ fd_index.take(),
+ fd_deleted.take(),
+ ));
+
+ let check_content = diff.to_string();
+
+ assert_eq!(filterdiff_output, check_content);
+
+ Ok(diff)
+}