Radish alpha
r
rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt
Git libraries for Radicle
Radicle
Git
Merge remote-tracking branch 'han/remove-object'
Fintan Halpenny committed 3 years ago
commit da2b58b867bcb1f85ef8ef370701d2e75a70963c
parent 640bd9e
7 files changed +399 -425
added radicle-surf/src/blob.rs
@@ -0,0 +1,135 @@
+
// This file is part of radicle-surf
+
// <https://github.com/radicle-dev/radicle-surf>
+
//
+
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
+
//
+
// This program is free software: you can redistribute it and/or modify
+
// it under the terms of the GNU General Public License version 3 or
+
// later as published by the Free Software Foundation.
+
//
+
// This program is distributed in the hope that it will be useful,
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
// GNU General Public License for more details.
+
//
+
// You should have received a copy of the GNU General Public License
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
+

+
//! Represents git object type 'blob', i.e. actual file contents.
+
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
+

+
use std::str;
+

+
use radicle_git_ext::Oid;
+
#[cfg(feature = "serde")]
+
use serde::{
+
    ser::{SerializeStruct as _, Serializer},
+
    Serialize,
+
};
+

+
use crate::Commit;
+

+
/// Represents a git blob object.
+
pub struct Blob {
+
    id: Oid,
+
    content: BlobContent,
+
    commit: Commit,
+
}
+

+
impl Blob {
+
    /// Returns the [`Blob`] for a file at `revision` under `path`.
+
    pub(crate) fn new(id: Oid, content: &[u8], commit: Commit) -> Self {
+
        let content = BlobContent::from(content);
+
        Self {
+
            id,
+
            content,
+
            commit,
+
        }
+
    }
+

+
    /// Indicates if the content of the [`Blob`] is binary.
+
    #[must_use]
+
    pub fn is_binary(&self) -> bool {
+
        matches!(self.content, BlobContent::Binary(_))
+
    }
+

+
    pub fn object_id(&self) -> Oid {
+
        self.id
+
    }
+

+
    pub fn content(&self) -> &BlobContent {
+
        &self.content
+
    }
+

+
    /// Returns the commit that created this blob.
+
    pub fn commit(&self) -> &Commit {
+
        &self.commit
+
    }
+
}
+

+
#[cfg(feature = "serde")]
+
impl Serialize for Blob {
+
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+
    where
+
        S: Serializer,
+
    {
+
        const FIELDS: usize = 5;
+
        let mut state = serializer.serialize_struct("Blob", FIELDS)?;
+
        state.serialize_field("binary", &self.is_binary())?;
+
        state.serialize_field("content", &self.content)?;
+
        state.serialize_field("lastCommit", &self.commit)?;
+
        state.end()
+
    }
+
}
+

+
/// Variants of blob content.
+
#[derive(PartialEq, Eq)]
+
pub enum BlobContent {
+
    /// Content is plain text and can be passed as a string.
+
    Plain(String),
+
    /// Content is binary and needs special treatment.
+
    Binary(Vec<u8>),
+
}
+

+
impl BlobContent {
+
    /// Returns the size of this `BlobContent`.
+
    pub fn size(&self) -> usize {
+
        match self {
+
            Self::Plain(content) => content.len(),
+
            Self::Binary(bytes) => bytes.len(),
+
        }
+
    }
+

+
    /// Returns the content as bytes.
+
    pub fn as_bytes(&self) -> &[u8] {
+
        match self {
+
            Self::Plain(content) => content.as_bytes(),
+
            Self::Binary(bytes) => &bytes[..],
+
        }
+
    }
+
}
+

+
#[cfg(feature = "serde")]
+
impl Serialize for BlobContent {
+
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+
    where
+
        S: Serializer,
+
    {
+
        match self {
+
            Self::Plain(content) => serializer.serialize_str(content),
+
            Self::Binary(bytes) => {
+
                let encoded = base64::encode(bytes);
+
                serializer.serialize_str(&encoded)
+
            },
+
        }
+
    }
+
}
+

+
impl From<&[u8]> for BlobContent {
+
    fn from(bytes: &[u8]) -> Self {
+
        match str::from_utf8(bytes) {
+
            Ok(utf8) => BlobContent::Plain(utf8.to_owned()),
+
            Err(_) => BlobContent::Binary(bytes.to_owned()),
+
        }
+
    }
+
}
modified radicle-surf/src/lib.rs
@@ -28,9 +28,10 @@ pub extern crate git_ref_format;

extern crate radicle_git_ext as git_ext;

+
pub mod blob;
pub mod diff;
pub mod fs;
-
pub mod object;
+
pub mod tree;

// Re-export git2 as sub-module
pub use git2::{self, Error as Git2Error, Time};
deleted radicle-surf/src/object.rs
@@ -1,25 +0,0 @@
-
// This file is part of radicle-surf
-
// <https://github.com/radicle-dev/radicle-surf>
-
//
-
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
-
//
-
// This program is free software: you can redistribute it and/or modify
-
// it under the terms of the GNU General Public License version 3 or
-
// later as published by the Free Software Foundation.
-
//
-
// This program is distributed in the hope that it will be useful,
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-
// GNU General Public License for more details.
-
//
-
// You should have received a copy of the GNU General Public License
-
// along with this program. If not, see <https://www.gnu.org/licenses/>.
-

-
//! Common definitions for git objects (blob and tree).
-
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
-

-
pub mod blob;
-
pub use blob::{Blob, BlobContent};
-

-
pub mod tree;
-
pub use tree::{Tree, TreeEntry};
deleted radicle-surf/src/object/blob.rs
@@ -1,135 +0,0 @@
-
// This file is part of radicle-surf
-
// <https://github.com/radicle-dev/radicle-surf>
-
//
-
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
-
//
-
// This program is free software: you can redistribute it and/or modify
-
// it under the terms of the GNU General Public License version 3 or
-
// later as published by the Free Software Foundation.
-
//
-
// This program is distributed in the hope that it will be useful,
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-
// GNU General Public License for more details.
-
//
-
// You should have received a copy of the GNU General Public License
-
// along with this program. If not, see <https://www.gnu.org/licenses/>.
-

-
//! Represents git object type 'blob', i.e. actual file contents.
-
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
-

-
use std::str;
-

-
use radicle_git_ext::Oid;
-
#[cfg(feature = "serde")]
-
use serde::{
-
    ser::{SerializeStruct as _, Serializer},
-
    Serialize,
-
};
-

-
use crate::Commit;
-

-
/// Represents a git blob object.
-
pub struct Blob {
-
    id: Oid,
-
    content: BlobContent,
-
    commit: Commit,
-
}
-

-
impl Blob {
-
    /// Returns the [`Blob`] for a file at `revision` under `path`.
-
    pub(crate) fn new(id: Oid, content: &[u8], commit: Commit) -> Self {
-
        let content = BlobContent::from(content);
-
        Self {
-
            id,
-
            content,
-
            commit,
-
        }
-
    }
-

-
    /// Indicates if the content of the [`Blob`] is binary.
-
    #[must_use]
-
    pub fn is_binary(&self) -> bool {
-
        matches!(self.content, BlobContent::Binary(_))
-
    }
-

-
    pub fn object_id(&self) -> Oid {
-
        self.id
-
    }
-

-
    pub fn content(&self) -> &BlobContent {
-
        &self.content
-
    }
-

-
    /// Returns the commit that created this blob.
-
    pub fn commit(&self) -> &Commit {
-
        &self.commit
-
    }
-
}
-

-
#[cfg(feature = "serde")]
-
impl Serialize for Blob {
-
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-
    where
-
        S: Serializer,
-
    {
-
        const FIELDS: usize = 5;
-
        let mut state = serializer.serialize_struct("Blob", FIELDS)?;
-
        state.serialize_field("binary", &self.is_binary())?;
-
        state.serialize_field("content", &self.content)?;
-
        state.serialize_field("lastCommit", &self.commit)?;
-
        state.end()
-
    }
-
}
-

-
/// Variants of blob content.
-
#[derive(PartialEq, Eq)]
-
pub enum BlobContent {
-
    /// Content is plain text and can be passed as a string.
-
    Plain(String),
-
    /// Content is binary and needs special treatment.
-
    Binary(Vec<u8>),
-
}
-

-
impl BlobContent {
-
    /// Returns the size of this `BlobContent`.
-
    pub fn size(&self) -> usize {
-
        match self {
-
            Self::Plain(content) => content.len(),
-
            Self::Binary(bytes) => bytes.len(),
-
        }
-
    }
-

-
    /// Returns the content as bytes.
-
    pub fn as_bytes(&self) -> &[u8] {
-
        match self {
-
            Self::Plain(content) => content.as_bytes(),
-
            Self::Binary(bytes) => &bytes[..],
-
        }
-
    }
-
}
-

-
#[cfg(feature = "serde")]
-
impl Serialize for BlobContent {
-
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-
    where
-
        S: Serializer,
-
    {
-
        match self {
-
            Self::Plain(content) => serializer.serialize_str(content),
-
            Self::Binary(bytes) => {
-
                let encoded = base64::encode(bytes);
-
                serializer.serialize_str(&encoded)
-
            },
-
        }
-
    }
-
}
-

-
impl From<&[u8]> for BlobContent {
-
    fn from(bytes: &[u8]) -> Self {
-
        match str::from_utf8(bytes) {
-
            Ok(utf8) => BlobContent::Plain(utf8.to_owned()),
-
            Err(_) => BlobContent::Binary(bytes.to_owned()),
-
        }
-
    }
-
}
deleted radicle-surf/src/object/tree.rs
@@ -1,261 +0,0 @@
-
// This file is part of radicle-surf
-
// <https://github.com/radicle-dev/radicle-surf>
-
//
-
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
-
//
-
// This program is free software: you can redistribute it and/or modify
-
// it under the terms of the GNU General Public License version 3 or
-
// later as published by the Free Software Foundation.
-
//
-
// This program is distributed in the hope that it will be useful,
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-
// GNU General Public License for more details.
-
//
-
// You should have received a copy of the GNU General Public License
-
// along with this program. If not, see <https://www.gnu.org/licenses/>.
-

-
//! Represents git object type 'tree', i.e. like directory entries in Unix.
-
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
-

-
use std::cmp::Ordering;
-

-
use radicle_git_ext::Oid;
-
#[cfg(feature = "serde")]
-
use serde::{
-
    ser::{SerializeStruct as _, Serializer},
-
    Serialize,
-
};
-

-
use crate::{fs, Commit};
-

-
/// Represents a tree object as in git. It is essentially the content of
-
/// one directory. Note that multiple directories can have the same content,
-
/// i.e. have the same tree object. Hence this struct does not embed its path.
-
#[derive(Clone, Debug)]
-
pub struct Tree {
-
    /// The object id of this tree.
-
    id: Oid,
-
    /// The first descendant entries for this tree.
-
    entries: Vec<TreeEntry>,
-
    /// The commit object that created this tree object.
-
    commit: Commit,
-
}
-

-
impl Tree {
-
    /// Creates a new tree, ensuring the `entries` are sorted.
-
    pub(crate) fn new(id: Oid, mut entries: Vec<TreeEntry>, commit: Commit) -> Self {
-
        entries.sort();
-
        Self {
-
            id,
-
            entries,
-
            commit,
-
        }
-
    }
-

-
    pub fn object_id(&self) -> Oid {
-
        self.id
-
    }
-

-
    /// Returns the commit that created this tree.
-
    pub fn commit(&self) -> &Commit {
-
        &self.commit
-
    }
-

-
    /// Returns the entries of the tree.
-
    pub fn entries(&self) -> &Vec<TreeEntry> {
-
        &self.entries
-
    }
-
}
-

-
#[cfg(feature = "serde")]
-
impl Serialize for Tree {
-
    /// Sample output:
-
    /// (for `<entry_1>` and `<entry_2>` sample output, see [`TreeEntry`])
-
    /// ```
-
    /// {
-
    ///   "entries": [
-
    ///     { <entry_1> },
-
    ///     { <entry_2> },
-
    ///   ],
-
    ///   "lastCommit": {
-
    ///     "author": {
-
    ///       "email": "foobar@gmail.com",
-
    ///       "name": "Foo Bar"
-
    ///     },
-
    ///     "committer": {
-
    ///       "email": "noreply@github.com",
-
    ///       "name": "GitHub"
-
    ///     },
-
    ///     "committerTime": 1582198877,
-
    ///     "description": "A sample commit.",
-
    ///     "sha1": "b57846bbc8ced6587bf8329fc4bce970eb7b757e",
-
    ///     "summary": "Add a new sample"
-
    ///   },
-
    ///   "oid": "dd52e9f8dfe1d8b374b2a118c25235349a743dd2"
-
    /// }
-
    /// ```
-
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-
    where
-
        S: Serializer,
-
    {
-
        const FIELDS: usize = 4;
-
        let mut state = serializer.serialize_struct("Tree", FIELDS)?;
-
        state.serialize_field("oid", &self.id)?;
-
        state.serialize_field("entries", &self.entries)?;
-
        state.serialize_field("lastCommit", &self.commit)?;
-
        state.end()
-
    }
-
}
-

-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-
pub enum Entry {
-
    Tree(Oid),
-
    Blob(Oid),
-
}
-

-
impl PartialOrd for Entry {
-
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-
        Some(self.cmp(other))
-
    }
-
}
-

-
impl Ord for Entry {
-
    fn cmp(&self, other: &Self) -> Ordering {
-
        match (self, other) {
-
            (Entry::Tree(_), Entry::Tree(_)) => Ordering::Equal,
-
            (Entry::Tree(_), Entry::Blob(_)) => Ordering::Less,
-
            (Entry::Blob(_), Entry::Tree(_)) => Ordering::Greater,
-
            (Entry::Blob(_), Entry::Blob(_)) => Ordering::Equal,
-
        }
-
    }
-
}
-

-
/// An entry that can be found in a tree.
-
///
-
/// Each entry consists of a [`TreeEntry::name`], its kind -- given as [`Entry`]
-
/// -- and the header of its associated [`TreeEntry::commit`].
-
///
-
/// # Ordering
-
///
-
/// The ordering of a `TreeEntry` is first by its [`Entry`] kind where
-
/// [`Entry::Tree`]s come before [`Entry::Blob`]. If both kinds are
-
/// equal then they are next compared by the lexicographical ordering
-
/// of their `name`s.
-
#[derive(Clone, Debug)]
-
pub struct TreeEntry {
-
    name: String,
-
    entry: Entry,
-

-
    /// The commit object that created this entry object.
-
    commit: Commit,
-
}
-

-
impl TreeEntry {
-
    pub(crate) fn new(name: String, entry: Entry, commit: Commit) -> Self {
-
        Self {
-
            name,
-
            entry,
-
            commit,
-
        }
-
    }
-

-
    pub fn name(&self) -> &str {
-
        &self.name
-
    }
-

-
    pub fn entry(&self) -> &Entry {
-
        &self.entry
-
    }
-

-
    pub fn is_tree(&self) -> bool {
-
        matches!(self.entry, Entry::Tree(_))
-
    }
-

-
    pub fn commit(&self) -> &Commit {
-
        &self.commit
-
    }
-

-
    pub fn object_id(&self) -> Oid {
-
        match self.entry {
-
            Entry::Blob(id) => id,
-
            Entry::Tree(id) => id,
-
        }
-
    }
-
}
-

-
// To support `sort`.
-
impl Ord for TreeEntry {
-
    fn cmp(&self, other: &Self) -> Ordering {
-
        self.entry
-
            .cmp(&other.entry)
-
            .then(self.name.cmp(&other.name))
-
    }
-
}
-

-
impl PartialOrd for TreeEntry {
-
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-
        Some(self.cmp(other))
-
    }
-
}
-

-
impl PartialEq for TreeEntry {
-
    fn eq(&self, other: &Self) -> bool {
-
        self.entry == other.entry && self.name == other.name
-
    }
-
}
-

-
impl Eq for TreeEntry {}
-

-
impl From<fs::Entry> for Entry {
-
    fn from(entry: fs::Entry) -> Self {
-
        match entry {
-
            fs::Entry::File(f) => Entry::Blob(f.id()),
-
            fs::Entry::Directory(d) => Entry::Tree(d.id()),
-
        }
-
    }
-
}
-

-
#[cfg(feature = "serde")]
-
impl Serialize for TreeEntry {
-
    /// Sample output:
-
    /// ```json
-
    ///  {
-
    ///     "kind": "blob",
-
    ///     "lastCommit": {
-
    ///       "author": {
-
    ///         "email": "foobar@gmail.com",
-
    ///         "name": "Foo Bar"
-
    ///       },
-
    ///       "committer": {
-
    ///         "email": "noreply@github.com",
-
    ///         "name": "GitHub"
-
    ///       },
-
    ///       "committerTime": 1578309972,
-
    ///       "description": "This is a sample file",
-
    ///       "sha1": "2873745c8f6ffb45c990eb23b491d4b4b6182f95",
-
    ///       "summary": "Add a new sample"
-
    ///     },
-
    ///     "name": "Sample.rs",
-
    ///     "oid": "6d6240123a8d8ea8a8376610168a0a4bcb96afd0"
-
    ///   },
-
    /// ```
-
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-
    where
-
        S: Serializer,
-
    {
-
        const FIELDS: usize = 4;
-
        let mut state = serializer.serialize_struct("TreeEntry", FIELDS)?;
-
        state.serialize_field("name", &self.name)?;
-
        state.serialize_field(
-
            "kind",
-
            match self.entry {
-
                Entry::Blob(_) => "blob",
-
                Entry::Tree(_) => "tree",
-
            },
-
        )?;
-
        state.serialize_field("oid", &self.object_id())?;
-
        state.serialize_field("lastCommit", &self.commit)?;
-
        state.end()
-
    }
-
}
modified radicle-surf/src/repo.rs
@@ -27,13 +27,14 @@ use radicle_git_ext::Oid;
use thiserror::Error;

use crate::{
+
    blob::Blob,
    commit,
    diff::{self, *},
    fs::{self, Directory, File, FileContent},
    glob,
    namespace,
-
    object::{Blob, Tree, TreeEntry},
    refs::{self, BranchNames, Branches, Categories, Namespaces, TagNames, Tags},
+
    tree::{Entry, Tree},
    Branch,
    Commit,
    Glob,
@@ -281,9 +282,9 @@ impl Repository {
                let commit = self
                    .last_commit(&path, commit.id)?
                    .ok_or(Error::PathNotFound(path))?;
-
                Ok(TreeEntry::new(name, en.into(), commit))
+
                Ok(Entry::new(name, en.into(), commit))
            })
-
            .collect::<Result<Vec<TreeEntry>, Error>>()?;
+
            .collect::<Result<Vec<Entry>, Error>>()?;
        entries.sort();

        let last_commit = self
added radicle-surf/src/tree.rs
@@ -0,0 +1,258 @@
+
// This file is part of radicle-surf
+
// <https://github.com/radicle-dev/radicle-surf>
+
//
+
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
+
//
+
// This program is free software: you can redistribute it and/or modify
+
// it under the terms of the GNU General Public License version 3 or
+
// later as published by the Free Software Foundation.
+
//
+
// This program is distributed in the hope that it will be useful,
+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+
// GNU General Public License for more details.
+
//
+
// You should have received a copy of the GNU General Public License
+
// along with this program. If not, see <https://www.gnu.org/licenses/>.
+

+
//! Represents git object type 'tree', i.e. like directory entries in Unix.
+
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.
+

+
use std::cmp::Ordering;
+

+
use radicle_git_ext::Oid;
+
#[cfg(feature = "serde")]
+
use serde::{
+
    ser::{SerializeStruct as _, Serializer},
+
    Serialize,
+
};
+

+
use crate::{fs, Commit};
+

+
/// Represents a tree object as in git. It is essentially the content of
+
/// one directory. Note that multiple directories can have the same content,
+
/// i.e. have the same tree object. Hence this struct does not embed its path.
+
#[derive(Clone, Debug)]
+
pub struct Tree {
+
    /// The object id of this tree.
+
    id: Oid,
+
    /// The first descendant entries for this tree.
+
    entries: Vec<Entry>,
+
    /// The commit object that created this tree object.
+
    commit: Commit,
+
}
+

+
impl Tree {
+
    /// Creates a new tree, ensuring the `entries` are sorted.
+
    pub(crate) fn new(id: Oid, mut entries: Vec<Entry>, commit: Commit) -> Self {
+
        entries.sort();
+
        Self {
+
            id,
+
            entries,
+
            commit,
+
        }
+
    }
+

+
    pub fn object_id(&self) -> Oid {
+
        self.id
+
    }
+

+
    /// Returns the commit that created this tree.
+
    pub fn commit(&self) -> &Commit {
+
        &self.commit
+
    }
+

+
    /// Returns the entries of the tree.
+
    pub fn entries(&self) -> &Vec<Entry> {
+
        &self.entries
+
    }
+
}
+

+
#[cfg(feature = "serde")]
+
impl Serialize for Tree {
+
    /// Sample output:
+
    /// (for `<entry_1>` and `<entry_2>` sample output, see [`Entry`])
+
    /// ```
+
    /// {
+
    ///   "entries": [
+
    ///     { <entry_1> },
+
    ///     { <entry_2> },
+
    ///   ],
+
    ///   "lastCommit": {
+
    ///     "author": {
+
    ///       "email": "foobar@gmail.com",
+
    ///       "name": "Foo Bar"
+
    ///     },
+
    ///     "committer": {
+
    ///       "email": "noreply@github.com",
+
    ///       "name": "GitHub"
+
    ///     },
+
    ///     "committerTime": 1582198877,
+
    ///     "description": "A sample commit.",
+
    ///     "sha1": "b57846bbc8ced6587bf8329fc4bce970eb7b757e",
+
    ///     "summary": "Add a new sample"
+
    ///   },
+
    ///   "oid": "dd52e9f8dfe1d8b374b2a118c25235349a743dd2"
+
    /// }
+
    /// ```
+
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+
    where
+
        S: Serializer,
+
    {
+
        const FIELDS: usize = 4;
+
        let mut state = serializer.serialize_struct("Tree", FIELDS)?;
+
        state.serialize_field("oid", &self.id)?;
+
        state.serialize_field("entries", &self.entries)?;
+
        state.serialize_field("lastCommit", &self.commit)?;
+
        state.end()
+
    }
+
}
+

+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+
pub enum EntryKind {
+
    Tree(Oid),
+
    Blob(Oid),
+
}
+

+
impl PartialOrd for EntryKind {
+
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+
        Some(self.cmp(other))
+
    }
+
}
+

+
impl Ord for EntryKind {
+
    fn cmp(&self, other: &Self) -> Ordering {
+
        match (self, other) {
+
            (EntryKind::Tree(_), EntryKind::Tree(_)) => Ordering::Equal,
+
            (EntryKind::Tree(_), EntryKind::Blob(_)) => Ordering::Less,
+
            (EntryKind::Blob(_), EntryKind::Tree(_)) => Ordering::Greater,
+
            (EntryKind::Blob(_), EntryKind::Blob(_)) => Ordering::Equal,
+
        }
+
    }
+
}
+

+
/// An entry that can be found in a tree.
+
///
+
/// # Ordering
+
///
+
/// The ordering of a [`Entry`] is first by its `entry` where
+
/// [`EntryKind::Tree`]s come before [`EntryKind::Blob`]. If both kinds
+
/// are equal then they are next compared by the lexicographical ordering
+
/// of their `name`s.
+
#[derive(Clone, Debug)]
+
pub struct Entry {
+
    name: String,
+
    entry: EntryKind,
+

+
    /// The commit object that created this entry object.
+
    commit: Commit,
+
}
+

+
impl Entry {
+
    pub(crate) fn new(name: String, entry: EntryKind, commit: Commit) -> Self {
+
        Self {
+
            name,
+
            entry,
+
            commit,
+
        }
+
    }
+

+
    pub fn name(&self) -> &str {
+
        &self.name
+
    }
+

+
    pub fn entry(&self) -> &EntryKind {
+
        &self.entry
+
    }
+

+
    pub fn is_tree(&self) -> bool {
+
        matches!(self.entry, EntryKind::Tree(_))
+
    }
+

+
    pub fn commit(&self) -> &Commit {
+
        &self.commit
+
    }
+

+
    pub fn object_id(&self) -> Oid {
+
        match self.entry {
+
            EntryKind::Blob(id) => id,
+
            EntryKind::Tree(id) => id,
+
        }
+
    }
+
}
+

+
// To support `sort`.
+
impl Ord for Entry {
+
    fn cmp(&self, other: &Self) -> Ordering {
+
        self.entry
+
            .cmp(&other.entry)
+
            .then(self.name.cmp(&other.name))
+
    }
+
}
+

+
impl PartialOrd for Entry {
+
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+
        Some(self.cmp(other))
+
    }
+
}
+

+
impl PartialEq for Entry {
+
    fn eq(&self, other: &Self) -> bool {
+
        self.entry == other.entry && self.name == other.name
+
    }
+
}
+

+
impl Eq for Entry {}
+

+
impl From<fs::Entry> for EntryKind {
+
    fn from(entry: fs::Entry) -> Self {
+
        match entry {
+
            fs::Entry::File(f) => EntryKind::Blob(f.id()),
+
            fs::Entry::Directory(d) => EntryKind::Tree(d.id()),
+
        }
+
    }
+
}
+

+
#[cfg(feature = "serde")]
+
impl Serialize for Entry {
+
    /// Sample output:
+
    /// ```json
+
    ///  {
+
    ///     "kind": "blob",
+
    ///     "lastCommit": {
+
    ///       "author": {
+
    ///         "email": "foobar@gmail.com",
+
    ///         "name": "Foo Bar"
+
    ///       },
+
    ///       "committer": {
+
    ///         "email": "noreply@github.com",
+
    ///         "name": "GitHub"
+
    ///       },
+
    ///       "committerTime": 1578309972,
+
    ///       "description": "This is a sample file",
+
    ///       "sha1": "2873745c8f6ffb45c990eb23b491d4b4b6182f95",
+
    ///       "summary": "Add a new sample"
+
    ///     },
+
    ///     "name": "Sample.rs",
+
    ///     "oid": "6d6240123a8d8ea8a8376610168a0a4bcb96afd0"
+
    ///   },
+
    /// ```
+
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+
    where
+
        S: Serializer,
+
    {
+
        const FIELDS: usize = 4;
+
        let mut state = serializer.serialize_struct("TreeEntry", FIELDS)?;
+
        state.serialize_field("name", &self.name)?;
+
        state.serialize_field(
+
            "kind",
+
            match self.entry {
+
                EntryKind::Blob(_) => "blob",
+
                EntryKind::Tree(_) => "tree",
+
            },
+
        )?;
+
        state.serialize_field("oid", &self.object_id())?;
+
        state.serialize_field("lastCommit", &self.commit)?;
+
        state.end()
+
    }
+
}