gix_merge/blob/platform/
prepare_merge.rs

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