radicle_git_ext/tree.rs
1// Copyright © 2019-2020 The Radicle Foundation <hello@radicle.foundation>
2//
3// This file is part of radicle-link, distributed under the GPLv3 with Radicle
4// Linking Exception. For full terms see the included LICENSE file.
5
6use std::{borrow::Cow, collections::BTreeMap, iter::FromIterator};
7
8/// A simplified representation of a git tree, intended mainly to be created
9/// from literals.
10///
11/// # Example
12///
13/// ```
14/// use radicle_git_ext::tree::{Tree, blob};
15///
16/// let my_tree = vec![
17/// ("README", blob(b"awe")),
18/// ("src", vec![("main.rs", blob(b"fn main() {}"))].into_iter().collect()),
19/// ]
20/// .into_iter()
21/// .collect::<Tree>();
22///
23/// assert_eq!(
24/// format!("{:?}", my_tree),
25/// "Tree(\
26/// {\
27/// \"README\": Blob(\
28/// [\
29/// 97, \
30/// 119, \
31/// 101\
32/// ]\
33/// ), \
34/// \"src\": Tree(\
35/// Tree(\
36/// {\
37/// \"main.rs\": Blob(\
38/// [\
39/// 102, \
40/// 110, \
41/// 32, \
42/// 109, \
43/// 97, \
44/// 105, \
45/// 110, \
46/// 40, \
47/// 41, \
48/// 32, \
49/// 123, \
50/// 125\
51/// ]\
52/// )\
53/// }\
54/// )\
55/// )\
56/// }\
57/// )"
58/// )
59/// ```
60#[derive(Clone, Debug)]
61pub struct Tree<'a>(BTreeMap<Cow<'a, str>, Node<'a>>);
62
63impl Tree<'_> {
64 pub fn write(&self, repo: &git2::Repository) -> Result<git2::Oid, git2::Error> {
65 use Node::*;
66
67 let mut builder = repo.treebuilder(None)?;
68 for (name, node) in &self.0 {
69 match node {
70 Blob(data) => {
71 let oid = repo.blob(data)?;
72 builder.insert(name.as_ref(), oid, git2::FileMode::Blob.into())?;
73 }
74 Tree(sub) => {
75 let oid = sub.write(repo)?;
76 builder.insert(name.as_ref(), oid, git2::FileMode::Tree.into())?;
77 }
78 }
79 }
80
81 builder.write()
82 }
83}
84
85impl<'a> From<BTreeMap<Cow<'a, str>, Node<'a>>> for Tree<'a> {
86 fn from(map: BTreeMap<Cow<'a, str>, Node<'a>>) -> Self {
87 Self(map)
88 }
89}
90
91impl<'a, K, N> FromIterator<(K, N)> for Tree<'a>
92where
93 K: Into<Cow<'a, str>>,
94 N: Into<Node<'a>>,
95{
96 fn from_iter<T>(iter: T) -> Self
97 where
98 T: IntoIterator<Item = (K, N)>,
99 {
100 Self(
101 iter.into_iter()
102 .map(|(k, v)| (k.into(), v.into()))
103 .collect(),
104 )
105 }
106}
107
108#[derive(Clone, Debug)]
109pub enum Node<'a> {
110 Blob(Cow<'a, [u8]>),
111 Tree(Tree<'a>),
112}
113
114pub fn blob(slice: &[u8]) -> Node {
115 Node::from(slice)
116}
117
118impl<'a> From<&'a [u8]> for Node<'a> {
119 fn from(slice: &'a [u8]) -> Self {
120 Self::from(Cow::Borrowed(slice))
121 }
122}
123
124impl<'a> From<Cow<'a, [u8]>> for Node<'a> {
125 fn from(bytes: Cow<'a, [u8]>) -> Self {
126 Self::Blob(bytes)
127 }
128}
129
130impl<'a> From<Tree<'a>> for Node<'a> {
131 fn from(tree: Tree<'a>) -> Self {
132 Self::Tree(tree)
133 }
134}
135
136impl<'a, K, N> FromIterator<(K, N)> for Node<'a>
137where
138 K: Into<Cow<'a, str>>,
139 N: Into<Node<'a>>,
140{
141 fn from_iter<T>(iter: T) -> Self
142 where
143 T: IntoIterator<Item = (K, N)>,
144 {
145 Self::Tree(iter.into_iter().collect())
146 }
147}