Skip to main content

sley_ref_filter/
contents.rs

1//! Parsed object contents shared by the for-each-ref atom model.
2
3use sley_core::{GitError, ObjectFormat, ObjectId, Result};
4use sley_object::{Commit, EncodedObject, ObjectType, Tag};
5use std::borrow::Cow;
6use std::io::Write;
7
8#[derive(Clone)]
9pub struct ForEachRefContents<'a> {
10    pub message: Cow<'a, [u8]>,
11    pub tree: Option<ObjectId>,
12    pub parents: Vec<ObjectId>,
13    pub tag: Option<Cow<'a, [u8]>>,
14    pub tag_object_type: Option<ObjectType>,
15    pub tag_object: Option<ObjectId>,
16    pub author: Option<Cow<'a, [u8]>>,
17    pub committer: Option<Cow<'a, [u8]>>,
18    pub tagger: Option<Cow<'a, [u8]>>,
19    pub creator: Option<Cow<'a, [u8]>>,
20}
21
22impl ForEachRefContents<'_> {
23    pub fn into_owned(self) -> ForEachRefContents<'static> {
24        ForEachRefContents {
25            message: Cow::Owned(self.message.into_owned()),
26            tree: self.tree,
27            parents: self.parents,
28            tag: self.tag.map(|tag| Cow::Owned(tag.into_owned())),
29            tag_object_type: self.tag_object_type,
30            tag_object: self.tag_object,
31            author: self.author.map(|author| Cow::Owned(author.into_owned())),
32            committer: self
33                .committer
34                .map(|committer| Cow::Owned(committer.into_owned())),
35            tagger: self.tagger.map(|tagger| Cow::Owned(tagger.into_owned())),
36            creator: self.creator.map(|creator| Cow::Owned(creator.into_owned())),
37        }
38    }
39}
40
41pub fn for_each_ref_contents<'a>(
42    format: ObjectFormat,
43    object: &'a EncodedObject,
44) -> Result<Option<ForEachRefContents<'a>>> {
45    let contents = match object.object_type {
46        ObjectType::Commit => {
47            let commit = Commit::parse_ref(format, &object.body)?;
48            ForEachRefContents {
49                message: Cow::Borrowed(commit.message),
50                tree: Some(commit.tree),
51                parents: commit.parents,
52                tag: None,
53                tag_object_type: None,
54                tag_object: None,
55                author: Some(Cow::Borrowed(commit.author)),
56                committer: Some(Cow::Borrowed(commit.committer)),
57                tagger: None,
58                creator: Some(Cow::Borrowed(commit.committer)),
59            }
60        }
61        ObjectType::Tag => {
62            let tag = Tag::parse_ref(format, &object.body)?;
63            ForEachRefContents {
64                message: Cow::Borrowed(tag.message),
65                tree: None,
66                parents: Vec::new(),
67                tag: Some(Cow::Borrowed(tag.name)),
68                tag_object_type: Some(tag.object_type),
69                tag_object: Some(tag.object),
70                author: None,
71                committer: None,
72                tagger: tag.tagger.map(Cow::Borrowed),
73                creator: tag.tagger.map(Cow::Borrowed),
74            }
75        }
76        _ => return Ok(None),
77    };
78    Ok(Some(contents))
79}
80
81pub fn for_each_ref_validate_tag_pointer(
82    tag_oid: &ObjectId,
83    contents: &ForEachRefContents<'_>,
84    target_oid: &ObjectId,
85    target: &EncodedObject,
86) -> Result<()> {
87    if contents
88        .tag_object_type
89        .is_some_and(|object_type| object_type != target.object_type)
90    {
91        eprintln!("error: bad tag pointer to {target_oid} in {tag_oid}");
92        return Err(GitError::Exit(128));
93    }
94    Ok(())
95}
96
97pub struct ForEachRefPeeledObject<'a> {
98    pub oid: ObjectId,
99    pub object_type: ObjectType,
100    pub object_body: Cow<'a, [u8]>,
101    pub object_size: usize,
102    pub object_disk_size: Option<u64>,
103    pub tree: Option<ObjectId>,
104    pub parents: Vec<ObjectId>,
105    pub message: Option<Cow<'a, [u8]>>,
106    pub author: Option<Cow<'a, [u8]>>,
107    pub committer: Option<Cow<'a, [u8]>>,
108    pub creator: Option<Cow<'a, [u8]>>,
109}
110
111pub fn write_for_each_ref_contents_lines(
112    stdout: &mut impl Write,
113    message: &[u8],
114    count: usize,
115) -> Result<()> {
116    let mut lines = message.split(|byte| *byte == b'\n').collect::<Vec<_>>();
117    if lines.last().is_some_and(|line| line.is_empty()) {
118        lines.pop();
119    }
120    for (idx, line) in lines.into_iter().take(count).enumerate() {
121        if idx > 0 {
122            stdout.write_all(b"\n    ")?;
123        }
124        stdout.write_all(line)?;
125    }
126    Ok(())
127}