gix_merge/blob/platform/
prepare_merge.rs1use 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#[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
26impl Platform {
28 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}