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