gix_merge/blob/platform/
set_resource.rs

1use bstr::{BStr, BString};
2
3use crate::blob::{pipeline, platform::Resource, Platform, ResourceKind};
4
5/// The error returned by [Platform::set_resource](Platform::set_resource).
6#[derive(Debug, thiserror::Error)]
7#[allow(missing_docs)]
8pub enum Error {
9    #[error("Can only diff blobs, not {mode:?}")]
10    InvalidMode { mode: gix_object::tree::EntryKind },
11    #[error("Failed to read {kind:?} worktree data from '{rela_path}'")]
12    Io {
13        rela_path: BString,
14        kind: ResourceKind,
15        source: std::io::Error,
16    },
17    #[error("Failed to obtain attributes for {kind:?} resource at '{rela_path}'")]
18    Attributes {
19        rela_path: BString,
20        kind: ResourceKind,
21        source: std::io::Error,
22    },
23    #[error(transparent)]
24    ConvertToMergeable(#[from] pipeline::convert_to_mergeable::Error),
25}
26
27/// Preparation
28impl Platform {
29    /// Store enough information about a resource to eventually use it in a merge, where…
30    ///
31    /// * `id` is the hash of the resource. If it [is null](gix_hash::ObjectId::is_null()), it should either
32    ///   be a resource in the worktree, or it's considered a non-existing, deleted object.
33    ///   If an `id` is known, as the hash of the object as (would) be stored in `git`, then it should be provided
34    ///   for completeness. Note that it's not expected to be in `objects` if `rela_path` is set and a worktree-root
35    ///   is available for `kind`.
36    /// * `mode` is the kind of object (only blobs and executables are allowed)
37    /// * `rela_path` is the relative path as seen from the (work)tree root.
38    /// * `kind` identifies the side of the merge this resource will be used for.
39    /// * `objects` provides access to the object database in case the resource can't be read from a worktree.
40    pub fn set_resource(
41        &mut self,
42        id: gix_hash::ObjectId,
43        mode: gix_object::tree::EntryKind,
44        rela_path: &BStr,
45        kind: ResourceKind,
46        objects: &impl gix_object::FindObjectOrHeader,
47    ) -> Result<(), Error> {
48        if !matches!(
49            mode,
50            gix_object::tree::EntryKind::Blob | gix_object::tree::EntryKind::BlobExecutable
51        ) {
52            return Err(Error::InvalidMode { mode });
53        }
54        let entry = self
55            .attr_stack
56            .at_entry(rela_path, None, objects)
57            .map_err(|err| Error::Attributes {
58                source: err,
59                kind,
60                rela_path: rela_path.to_owned(),
61            })?;
62
63        let storage = match kind {
64            ResourceKind::OtherOrTheirs => &mut self.other,
65            ResourceKind::CommonAncestorOrBase => &mut self.ancestor,
66            ResourceKind::CurrentOrOurs => &mut self.current,
67        };
68
69        let mut buf_storage = Vec::new();
70        let out = self.filter.convert_to_mergeable(
71            &id,
72            mode,
73            rela_path,
74            kind,
75            &mut |_, out| {
76                let _ = entry.matching_attributes(out);
77            },
78            objects,
79            self.filter_mode,
80            storage.as_mut().map_or(&mut buf_storage, |s| &mut s.buffer),
81        )?;
82
83        match storage {
84            None => {
85                *storage = Some(Resource {
86                    id,
87                    rela_path: rela_path.to_owned(),
88                    data: out,
89                    mode,
90                    buffer: buf_storage,
91                });
92            }
93            Some(storage) => {
94                storage.id = id;
95                storage.rela_path = rela_path.to_owned();
96                storage.data = out;
97                storage.mode = mode;
98            }
99        }
100        Ok(())
101    }
102}