From d2f423521ec76406944ad83098ec33afe20c692b Mon Sep 17 00:00:00 2001 From: Kim Altintop Date: Mon, 9 Jan 2023 13:18:33 +0100 Subject: This is it Squashed commit of all the exploration history. Development starts here. Signed-off-by: Kim Altintop --- src/patches/notes.rs | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 src/patches/notes.rs (limited to 'src/patches/notes.rs') diff --git a/src/patches/notes.rs b/src/patches/notes.rs new file mode 100644 index 0000000..b85ca64 --- /dev/null +++ b/src/patches/notes.rs @@ -0,0 +1,181 @@ +// Copyright © 2022 Kim Altintop +// SPDX-License-Identifier: GPL-2.0-only WITH openvpn-openssl-exception + +use std::{ + cmp, + collections::BTreeMap, + convert::Infallible, + io, + ops::Range, +}; + +use super::{ + error, + traits::{ + Blob, + BlobData, + TreeData, + }, +}; +use crate::{ + bundle::ObjectId, + git::Refname, +}; + +#[derive(serde::Serialize)] +#[serde(untagged)] +pub enum Note { + Simple(Simple), + Automerge(Automerge), +} + +impl Note { + pub fn from_tree<'a>(repo: &'a git2::Repository, tree: &git2::Tree<'a>) -> crate::Result { + Blob::::from_tree(repo, tree) + .map(|Blob { content, .. }| Self::Simple(content)) + .or_else(|e| match e { + error::FromTree::NotFound { .. } => { + let Blob { content, .. } = Blob::::from_tree(repo, tree)?; + Ok(Self::Automerge(content)) + }, + x => Err(x.into()), + }) + } +} + +#[derive(serde::Serialize)] +pub struct Automerge(Vec); + +impl BlobData for Automerge { + type Error = Infallible; + + const MAX_BYTES: usize = 1_000_000; + + fn from_blob(data: &[u8]) -> Result { + Ok(Self(data.to_vec())) + } + + fn write_blob(&self, mut writer: W) -> io::Result<()> { + writer.write_all(&self.0) + } +} + +impl TreeData for Automerge { + const BLOB_NAME: &'static str = "c"; +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum Simple { + Known(Predef), + Unknown(serde_json::Map), +} + +impl Simple { + pub fn new(message: String) -> Self { + Self::basic(message) + } + + pub fn basic(message: String) -> Self { + Self::Known(Predef::Basic { message }) + } + + pub fn checkpoint( + kind: CheckpointKind, + refs: BTreeMap, + message: Option, + ) -> Self { + Self::Known(Predef::Checkpoint { + kind, + refs, + message, + }) + } + + pub fn from_commit(repo: &git2::Repository, commit: &git2::Commit) -> crate::Result { + let tree = commit.tree()?; + let blob = Blob::from_tree(repo, &tree)?; + + Ok(blob.content) + } + + pub fn subject(&self) -> Option<&str> { + match self { + Self::Known(k) => k.subject(), + _ => None, + } + } + + pub fn is_checkpoint(&self) -> bool { + matches!(self, Self::Known(Predef::Checkpoint { .. })) + } + + pub fn checkpoint_kind(&self) -> Option<&CheckpointKind> { + match self { + Self::Known(Predef::Checkpoint { kind, .. }) => Some(kind), + _ => None, + } + } +} + +impl BlobData for Simple { + type Error = serde_json::Error; + + const MAX_BYTES: usize = 1_000_000; + + fn from_blob(data: &[u8]) -> Result { + serde_json::from_slice(data) + } + + fn write_blob(&self, writer: W) -> io::Result<()> { + serde_json::to_writer_pretty(writer, self).map_err(Into::into) + } +} + +impl TreeData for Simple { + const BLOB_NAME: &'static str = "m"; +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[serde(tag = "_type")] +pub enum Predef { + #[serde(rename = "eagain.io/it/notes/basic")] + Basic { message: String }, + #[serde(rename = "eagain.io/it/notes/code-comment")] + CodeComment { loc: SourceLoc, message: String }, + #[serde(rename = "eagain.io/it/notes/checkpoint")] + Checkpoint { + kind: CheckpointKind, + refs: BTreeMap, + #[serde(skip_serializing_if = "Option::is_none")] + message: Option, + }, +} + +impl Predef { + pub fn subject(&self) -> Option<&str> { + let msg = match self { + Self::Basic { message } | Self::CodeComment { message, .. } => Some(message), + Self::Checkpoint { message, .. } => message.as_ref(), + }?; + let line = msg.lines().next()?; + let subj = &line[..cmp::min(72, line.len())]; + + (!subj.is_empty()).then_some(subj) + } +} + +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub struct SourceLoc { + #[serde(with = "crate::git::serde::oid")] + pub file: git2::Oid, + #[serde(skip_serializing_if = "Option::is_none")] + pub line: Option>, +} + +#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum CheckpointKind { + Merge, + Snapshot, +} -- cgit v1.2.3