gix_merge/blob/platform/
set_resource.rs

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