gix_merge/blob/platform/
prepare_merge.rs

1use crate::blob::builtin_driver::text::Conflict;
2use crate::blob::platform::{merge, DriverChoice, ResourceRef};
3use crate::blob::{BuiltinDriver, Platform, PlatformRef, ResourceKind};
4use bstr::{BStr, BString, ByteSlice};
5use gix_filter::attributes;
6use std::num::NonZeroU8;
7use std::str::FromStr;
8
9/// The error returned by [Platform::prepare_merge_state()](Platform::prepare_merge()).
10#[derive(Debug, thiserror::Error)]
11#[allow(missing_docs)]
12pub enum Error {
13    #[error("The 'current', 'ancestor' or 'other' resource for the merge operation were not set")]
14    UnsetResource,
15    #[error("Failed to obtain attributes for {kind:?} resource at '{rela_path}'")]
16    Attributes {
17        rela_path: BString,
18        kind: ResourceKind,
19        source: std::io::Error,
20    },
21}
22
23/// Preparation
24impl Platform {
25    /// Prepare all state needed for performing a merge, using all [previously set](Self::set_resource()) resources.
26    /// `objects` is used to possibly lookup attribute files when obtaining merge-related attributes.
27    ///
28    /// `options` are to be used when merging later, and they may be altered to implement correct binary merges
29    /// in the present of [virtual merge bases](merge::Options::is_virtual_ancestor).
30    ///
31    /// Note that no additional validation is performed here to facilitate inspection, which means that
32    /// resource buffers might still be too large to be merged, preventing a successful merge at a later time.
33    pub fn prepare_merge(
34        &mut self,
35        objects: &impl gix_object::Find,
36        mut options: merge::Options,
37    ) -> Result<PlatformRef<'_>, Error> {
38        let current = self.current.as_ref().ok_or(Error::UnsetResource)?;
39        let ancestor = self.ancestor.as_ref().ok_or(Error::UnsetResource)?;
40        let other = self.other.as_ref().ok_or(Error::UnsetResource)?;
41
42        let entry = self
43            .attr_stack
44            .at_entry(current.rela_path.as_bstr(), None, objects)
45            .map_err(|err| Error::Attributes {
46                source: err,
47                kind: ResourceKind::CurrentOrOurs,
48                rela_path: current.rela_path.clone(),
49            })?;
50        entry.matching_attributes(&mut self.attrs);
51        let mut attrs = self.attrs.iter_selected();
52        let merge_attr = attrs.next().expect("pre-initialized with 'merge'");
53        let marker_size_attr = attrs.next().expect("pre-initialized with 'conflict-marker-size'");
54        let mut driver = match merge_attr.assignment.state {
55            attributes::StateRef::Set => DriverChoice::BuiltIn(BuiltinDriver::Text),
56            attributes::StateRef::Unset => DriverChoice::BuiltIn(BuiltinDriver::Binary),
57            attributes::StateRef::Value(_) | attributes::StateRef::Unspecified => {
58                let name = match merge_attr.assignment.state {
59                    attributes::StateRef::Value(name) => Some(name.as_bstr()),
60                    attributes::StateRef::Unspecified => {
61                        self.options.default_driver.as_ref().map(|name| name.as_bstr())
62                    }
63                    _ => unreachable!("only value and unspecified are possible here"),
64                };
65                self.find_driver_by_name(name)
66            }
67        };
68        if let attributes::StateRef::Value(value) = marker_size_attr.assignment.state {
69            if let Some(value) = u8::from_str(value.as_bstr().to_str_lossy().as_ref())
70                .ok()
71                .and_then(NonZeroU8::new)
72            {
73                match &mut options.text.conflict {
74                    Conflict::Keep { marker_size, .. } => *marker_size = value,
75                    Conflict::ResolveWithOurs | Conflict::ResolveWithTheirs | Conflict::ResolveWithUnion => {}
76                }
77            }
78        }
79        if let Some(recursive_driver_name) = match driver {
80            DriverChoice::Index(idx) => self.drivers.get(idx),
81            _ => None,
82        }
83        .and_then(|driver| driver.recursive.as_deref())
84        .filter(|_| options.is_virtual_ancestor)
85        {
86            driver = self.find_driver_by_name(Some(recursive_driver_name.as_bstr()));
87            options.resolve_binary_with = Some(crate::blob::builtin_driver::binary::ResolveWith::Ours);
88        }
89
90        let out = PlatformRef {
91            parent: self,
92            driver,
93            current: ResourceRef::new(current),
94            ancestor: ResourceRef::new(ancestor),
95            other: ResourceRef::new(other),
96            options,
97        };
98        Ok(out)
99    }
100
101    fn find_driver_by_name(&self, name: Option<&BStr>) -> DriverChoice {
102        name.and_then(|name| {
103            self.drivers
104                .binary_search_by(|d| d.name.as_bstr().cmp(name))
105                .ok()
106                .map(DriverChoice::Index)
107                .or_else(|| {
108                    name.to_str()
109                        .ok()
110                        .and_then(BuiltinDriver::by_name)
111                        .map(DriverChoice::BuiltIn)
112                })
113        })
114        .unwrap_or_default()
115    }
116}