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/traits.rs | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/patches/traits.rs (limited to 'src/patches/traits.rs') diff --git a/src/patches/traits.rs b/src/patches/traits.rs new file mode 100644 index 0000000..ef9ae61 --- /dev/null +++ b/src/patches/traits.rs @@ -0,0 +1,165 @@ +// Copyright © 2022 Kim Altintop +// SPDX-License-Identifier: GPL-2.0-only WITH openvpn-openssl-exception + +use std::{ + io, + path::{ + Path, + PathBuf, + }, +}; + +use super::error; +use crate::git::{ + self, + if_not_found_none, +}; + +pub trait BlobData: Sized { + type Error; + + const MAX_BYTES: usize; + + fn from_blob(data: &[u8]) -> Result; + fn write_blob(&self, writer: W) -> io::Result<()>; +} + +pub trait TreeData: BlobData { + const BLOB_NAME: &'static str; +} + +pub struct Blob { + pub oid: git2::Oid, + pub content: T, +} + +impl Blob +where + T: TreeData, + T::Error: Into, +{ + pub fn from_tree<'a>( + repo: &'a git2::Repository, + tree: &git2::Tree<'a>, + ) -> Result, error::FromTree> { + use error::FromTree::NotFound; + + let entry = tree + .get_name(T::BLOB_NAME) + .ok_or(NotFound { name: T::BLOB_NAME })?; + Self::from_entry(repo, entry) + } + + pub fn from_entry<'a>( + repo: &'a git2::Repository, + entry: git2::TreeEntry<'a>, + ) -> Result { + use error::FromTree::{ + BlobSize, + TypeConversion, + TypeMismatch, + }; + + let blob = entry + .to_object(repo)? + .into_blob() + .map_err(|obj| TypeMismatch { + name: T::BLOB_NAME, + kind: obj.kind(), + })?; + let sz = blob.size(); + if sz > T::MAX_BYTES { + return Err(BlobSize { + max: T::MAX_BYTES, + found: sz, + }); + } + let content = T::from_blob(blob.content()) + .map_err(Into::into) + .map_err(TypeConversion)?; + + Ok(Self { + oid: entry.id(), + content, + }) + } +} + +pub trait Foldable { + fn folded_name(&self) -> String; +} + +pub trait Seen { + fn in_odb(&self, odb: &git2::Odb) -> git::Result; + fn in_tree(&self, tree: &git2::Tree) -> git::Result; +} + +impl Seen for T +where + T: BlobData + Foldable, +{ + fn in_odb(&self, odb: &git2::Odb) -> git::Result { + let hash = blob_hash(self)?; + Ok(odb.exists(hash)) + } + + fn in_tree(&self, tree: &git2::Tree) -> git::Result { + let path = shard_path(&self.folded_name()); + Ok(if_not_found_none(tree.get_path(&path))?.is_some()) + } +} + +pub fn to_tree( + repo: &git2::Repository, + tree: &mut git2::TreeBuilder, + data: &T, +) -> git::Result<()> { + tree.insert( + T::BLOB_NAME, + to_blob(repo, data)?, + git2::FileMode::Blob.into(), + )?; + Ok(()) +} + +pub fn to_blob(repo: &git2::Repository, data: &T) -> git::Result { + let mut writer = repo.blob_writer(None)?; + data.write_blob(&mut writer).map_err(|e| { + git2::Error::new( + git2::ErrorCode::GenericError, + git2::ErrorClass::Object, + e.to_string(), + ) + })?; + writer.commit() +} + +pub fn blob_hash(data: &T) -> git::Result { + let mut buf = Vec::new(); + data.write_blob(&mut buf).unwrap(); + git::blob_hash(&buf) +} + +pub fn write_sharded( + repo: &git2::Repository, + root: &mut git2::TreeBuilder, + item: &F, + blob: git2::Oid, +) -> git::Result<()> { + let name = item.folded_name(); + let (pre, suf) = name.split_at(2); + let shard = root + .get(pre)? + .map(|entry| entry.to_object(repo)) + .transpose()?; + let mut sub = repo.treebuilder(shard.as_ref().and_then(git2::Object::as_tree))?; + sub.insert(suf, blob, git2::FileMode::Blob.into())?; + root.insert(pre, sub.write()?, git2::FileMode::Tree.into())?; + + Ok(()) +} + +pub fn shard_path(name: &str) -> PathBuf { + let (pre, suf) = name.split_at(2); + Path::new(pre).join(suf) +} -- cgit v1.2.3