separate hunks type

This commit is contained in:
Nikita Galaiko 2023-06-27 12:22:30 +02:00
parent f6c6b6c51d
commit daad336602
10 changed files with 487 additions and 437 deletions

View File

@ -0,0 +1,94 @@
use std::{fmt::Display, ops::RangeInclusive};
use anyhow::{anyhow, Context, Result};
static CONTEXT: usize = 3; // default git diff context
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Hunk {
start: usize,
end: usize,
}
impl From<RangeInclusive<usize>> for Hunk {
fn from(range: RangeInclusive<usize>) -> Self {
Hunk {
start: *range.start(),
end: *range.end(),
}
}
}
impl TryFrom<&str> for Hunk {
type Error = anyhow::Error;
fn try_from(s: &str) -> Result<Self, Self::Error> {
let mut range = s.split('-');
if range.clone().count() != 2 {
return Err(anyhow!("invalid range: {}", s));
}
let start = range
.next()
.unwrap()
.parse::<usize>()
.context(format!("failed to parse start of range: {}", s))?;
let end = range
.next()
.unwrap()
.parse::<usize>()
.context(format!("failed to parse end of range: {}", s))?;
if start > end {
Err(anyhow!("invalid range: {}", s))
} else {
Ok(Hunk { start, end })
}
}
}
impl Display for Hunk {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}", self.start, self.end,)
}
}
impl Hunk {
pub fn start(&self) -> &usize {
&self.start
}
pub fn contains(&self, line: &usize) -> bool {
self.start <= *line && self.end >= *line
}
pub fn distance(&self, another: &Hunk) -> usize {
if self.start > another.end {
self.start - another.end
} else if another.start > self.end {
another.start - self.end
} else {
0
}
}
pub fn touches(&self, another: &Hunk) -> bool {
self.distance(another) <= CONTEXT * 2
}
pub fn intersects(&self, another: &Hunk) -> bool {
self.contains(&another.start)
|| self.contains(&another.end)
|| another.contains(&self.start)
|| another.contains(&self.end)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_from_string() {
let hunk = Hunk::from(1..=2);
assert_eq!(hunk, Hunk::try_from(hunk.to_string().as_str()).unwrap());
}
}

View File

@ -1,3 +1,5 @@
mod hunk;
mod ownership;
mod reader;
mod writer;
@ -5,404 +7,10 @@ pub use reader::BranchReader as Reader;
pub use writer::BranchWriter as Writer;
use serde::{Deserialize, Serialize};
use std::{fmt, ops, path, vec};
use anyhow::{anyhow, Context, Result};
use anyhow::Result;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Ownership {
pub file_path: path::PathBuf,
pub ranges: Vec<ops::RangeInclusive<usize>>,
}
impl TryFrom<&String> for Ownership {
type Error = anyhow::Error;
fn try_from(value: &String) -> std::result::Result<Self, Self::Error> {
Self::parse_string(value)
}
}
impl TryFrom<&str> for Ownership {
type Error = anyhow::Error;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
Self::parse_string(value)
}
}
impl TryFrom<String> for Ownership {
type Error = anyhow::Error;
fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
Self::parse_string(&value)
}
}
impl Ownership {
pub fn normalize(&self) -> Ownership {
let mut ranges = self.ranges.clone();
ranges.sort_by(|a, b| a.start().cmp(b.start()));
ranges.dedup();
Ownership {
file_path: self.file_path.clone(),
ranges,
}
}
// return a copy of self, with another ranges added
pub fn plus(&self, another: &Ownership) -> Ownership {
if !self.file_path.eq(&another.file_path) {
return self.clone();
}
if self.ranges.is_empty() {
// full ownership + partial ownership = full ownership
return self.clone();
}
if another.ranges.is_empty() {
// partial ownership + full ownership = full ownership
return another.clone();
}
let mut ranges = self.ranges.clone();
ranges.extend(another.ranges.clone());
Ownership {
file_path: self.file_path.clone(),
ranges,
}
.normalize()
}
// returns a copy of self, with another ranges removed
// if all of the ranges are removed, return None
pub fn minus(&self, another: &Ownership) -> Option<Ownership> {
if !self.file_path.eq(&another.file_path) {
return Some(self.clone());
}
if another.ranges.is_empty() {
// any ownership - full ownership = empty ownership
return None;
}
if self.ranges.is_empty() {
// full ownership - partial ownership = full ownership, since we don't know all the
// hunks.
return Some(self.clone());
}
let mut ranges = self.ranges.clone();
for range in &another.ranges {
ranges = ranges
.iter()
.flat_map(
|r: &ops::RangeInclusive<usize>| -> Vec<ops::RangeInclusive<usize>> {
if r.eq(range) {
vec![]
} else {
vec![r.clone()]
}
},
)
.collect();
}
if ranges.is_empty() {
None
} else {
Some(Ownership {
file_path: self.file_path.clone(),
ranges,
})
}
}
pub fn contains(&self, another: &Ownership) -> bool {
if self.file_path != another.file_path {
return false;
}
if self.ranges.is_empty() {
// full ownership
return true;
}
if another.ranges.is_empty() {
// empty ownership
return false;
}
another
.ranges
.iter()
.map(|range| self.ranges.iter().find(|r| r.eq(&range)))
.all(|x| x.is_some())
}
fn parse_range(s: &str) -> Result<ops::RangeInclusive<usize>> {
let mut range = s.split('-');
if range.clone().count() != 2 {
return Err(anyhow!("invalid range: {}", s));
}
let start = range
.next()
.unwrap()
.parse::<usize>()
.context(format!("failed to parse start of range: {}", s))?;
let end = range
.next()
.unwrap()
.parse::<usize>()
.context(format!("failed to parse end of range: {}", s))?;
if start > end {
Err(anyhow!("invalid range: {}", s))
} else {
Ok(start..=end)
}
}
pub fn parse_string(s: &str) -> Result<Self> {
let mut parts = s.split(':');
let file_path = parts.next().unwrap();
let ranges = match parts.next() {
Some(raw_ranges) => raw_ranges
.split(',')
.map(Self::parse_range)
.collect::<Result<Vec<ops::RangeInclusive<usize>>>>(),
None => Ok(vec![]),
}
.context(format!("failed to parse ownership ranges: {}", s))?;
Ok(Self {
file_path: path::PathBuf::from(file_path),
ranges,
})
}
}
#[cfg(test)]
mod ownership_tests {
use super::*;
#[test]
fn parse_ownership() {
let ownership = Ownership::parse_string("foo/bar.rs:1-2,4-5").unwrap();
assert_eq!(
ownership,
Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
ranges: vec![1..=2, 4..=5]
}
);
}
#[test]
fn parse_ownership_no_ranges() {
let ownership = Ownership::parse_string("foo/bar.rs").unwrap();
assert_eq!(
ownership,
Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
ranges: vec![]
}
);
}
#[test]
fn parse_ownership_invalid_range() {
let ownership = Ownership::parse_string("foo/bar.rs:1-2,4-5-6");
assert!(ownership.is_err());
}
#[test]
fn parse_ownership_invalid_range_2() {
let ownership = Ownership::parse_string("foo/bar.rs:1-2,6-5");
assert!(ownership.is_err());
}
#[test]
fn ownership_to_from_string() {
let ownership = Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
ranges: vec![1..=2, 4..=5],
};
assert_eq!(ownership.to_string(), "foo/bar.rs:1-2,4-5".to_string());
assert_eq!(
Ownership::parse_string(&ownership.to_string()).unwrap(),
ownership
);
}
#[test]
fn ownership_to_from_string_no_ranges() {
let ownership = Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
ranges: vec![],
};
assert_eq!(ownership.to_string(), "foo/bar.rs".to_string());
assert_eq!(
Ownership::parse_string(&ownership.to_string()).unwrap(),
ownership
);
}
#[test]
fn test_normalize() {
vec![
("file.txt:1-10", "file.txt:1-10"),
("file.txt:1-10,15-16", "file.txt:1-10,15-16"),
("file.txt:1-10,10-15,15-16", "file.txt:1-10,10-15,15-16"),
("file.txt:1-10,5-12", "file.txt:1-10,5-12"),
("file.txt:15-16,1-10", "file.txt:1-10,15-16"),
]
.into_iter()
.map(|(a, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(expected).unwrap(),
)
})
.for_each(|(a, expected)| {
let got = a.normalize();
assert_eq!(
got, expected,
"normalize {} expected {}, got {}",
a, expected, got
);
});
}
#[test]
fn test_plus() {
vec![
("file.txt:1-10", "another.txt:1-5", "file.txt:1-10"),
("file.txt:1-10", "file.txt:1-5", "file.txt:1-10,1-5"),
("file.txt:1-10", "file.txt:12-15", "file.txt:1-10,12-15"),
(
"file.txt:1-10",
"file.txt:8-15,20-25",
"file.txt:1-10,8-15,20-25",
),
("file.txt:1-10", "file.txt", "file.txt"),
("file.txt", "file.txt:1-10", "file.txt"),
("file.txt:1-10", "file.txt:10-15", "file.txt:1-10,10-15"),
("file.txt:5-10", "file.txt:1-5", "file.txt:1-5,5-10"),
("file.txt:1-10", "file.txt:1-10", "file.txt:1-10"),
("file.txt:5-10", "file.txt:2-7", "file.txt:2-7,5-10"),
("file.txt:5-10", "file.txt:7-12", "file.txt:5-10,7-12"),
]
.into_iter()
.map(|(a, b, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(b).unwrap(),
Ownership::parse_string(expected).unwrap(),
)
})
.for_each(|(a, b, expected)| {
let got = a.plus(&b);
assert_eq!(
got, expected,
"{} plus {}, expected {}, got {}",
a, b, expected, got
);
});
}
#[test]
fn test_minus() {
vec![
("file.txt:1-10", "another.txt:1-5", Some("file.txt:1-10")),
("file.txt:1-10", "file.txt:1-5", Some("file.txt:1-10")),
("file.txt:1-10", "file.txt:11-15", Some("file.txt:1-10")),
("file.txt:1-10", "file.txt:1-10", None),
("file.txt:1-10", "file.txt", None),
("file.txt", "file.txt", None),
("file.txt", "file.txt:1-10", Some("file.txt")),
(
"file.txt:1-10,11-15",
"file.txt:11-15",
Some("file.txt:1-10"),
),
(
"file.txt:1-10,11-15,15-17",
"file.txt:1-10,15-17",
Some("file.txt:11-15"),
),
]
.into_iter()
.map(|(a, b, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(b).unwrap(),
expected.map(|s| Ownership::parse_string(s).unwrap()),
)
})
.for_each(|(a, b, expected)| {
let got = a.minus(&b);
assert_eq!(
got, expected,
"{} minus {}, expected {:?}, got {:?}",
a, b, expected, got
);
});
}
#[test]
fn test_contains() {
vec![
("file.txt", "another.txt", false),
("file.txt", "file.txt", true),
("file.txt", "file.txt:1-10", true),
("file.txt:1-10", "file.txt", false),
("file.txt:1-10", "file.txt:11-20", false),
("file.txt:1-10", "file.txt:1-5", false),
("file.txt:1-10", "file.txt:5-10", false),
("file.txt:1-10", "file.txt:1-10", true),
("file.txt:1-10", "file.txt:2-7", false),
("file.txt:3-5", "file.txt:1-10", false),
("file.txt:1-10", "another.txt:1-10", false),
("file.txt:1-10", "file.txt:1-10,20-25", false),
("file.txt:1-10,11-15", "file.txt:11-15", true),
]
.into_iter()
.map(|(a, b, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(b).unwrap(),
expected,
)
})
.for_each(|(a, b, expected)| {
assert_eq!(
a.contains(&b),
expected,
"{} contains {}, expected {}",
a,
b,
expected
);
});
}
}
impl fmt::Display for Ownership {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
if self.ranges.is_empty() {
write!(f, "{}", self.file_path.to_str().unwrap())
} else {
write!(
f,
"{}:{}",
self.file_path.to_str().unwrap(),
self.ranges
.iter()
.map(|r| format!("{}-{}", r.start(), r.end()))
.collect::<Vec<String>>()
.join(",")
)
}
}
}
pub use ownership::Ownership;
#[derive(Debug, PartialEq, Clone)]
pub struct Branch {

View File

@ -0,0 +1,376 @@
use std::{fmt, path, vec};
use anyhow::{Context, Result};
use super::hunk::Hunk;
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct Ownership {
pub file_path: path::PathBuf,
pub hunks: Vec<Hunk>,
}
impl TryFrom<&String> for Ownership {
type Error = anyhow::Error;
fn try_from(value: &String) -> std::result::Result<Self, Self::Error> {
Self::parse_string(value)
}
}
impl TryFrom<&str> for Ownership {
type Error = anyhow::Error;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
Self::parse_string(value)
}
}
impl TryFrom<String> for Ownership {
type Error = anyhow::Error;
fn try_from(value: String) -> std::result::Result<Self, Self::Error> {
Self::parse_string(&value)
}
}
impl Ownership {
pub fn normalize(&self) -> Ownership {
let mut ranges = self.hunks.clone();
ranges.sort_by(|a, b| a.start().cmp(b.start()));
ranges.dedup();
Ownership {
file_path: self.file_path.clone(),
hunks: ranges,
}
}
// return a copy of self, with another ranges added
pub fn plus(&self, another: &Ownership) -> Ownership {
if !self.file_path.eq(&another.file_path) {
return self.clone();
}
if self.hunks.is_empty() {
// full ownership + partial ownership = full ownership
return self.clone();
}
if another.hunks.is_empty() {
// partial ownership + full ownership = full ownership
return another.clone();
}
let mut ranges = self.hunks.clone();
ranges.extend(another.hunks.clone());
Ownership {
file_path: self.file_path.clone(),
hunks: ranges,
}
.normalize()
}
// returns a copy of self, with another ranges removed
// if all of the ranges are removed, return None
pub fn minus(&self, another: &Ownership) -> Option<Ownership> {
if !self.file_path.eq(&another.file_path) {
return Some(self.clone());
}
if another.hunks.is_empty() {
// any ownership - full ownership = empty ownership
return None;
}
if self.hunks.is_empty() {
// full ownership - partial ownership = full ownership, since we don't know all the
// hunks.
return Some(self.clone());
}
let mut ranges = self.hunks.clone();
for range in &another.hunks {
ranges = ranges
.iter()
.flat_map(|r: &Hunk| -> Vec<Hunk> {
if r.eq(range) {
vec![]
} else {
vec![r.clone()]
}
})
.collect();
}
if ranges.is_empty() {
None
} else {
Some(Ownership {
file_path: self.file_path.clone(),
hunks: ranges,
})
}
}
pub fn contains(&self, another: &Ownership) -> bool {
if self.file_path != another.file_path {
return false;
}
if self.hunks.is_empty() {
// full ownership
return true;
}
if another.hunks.is_empty() {
// empty ownership
return false;
}
another
.hunks
.iter()
.map(|range| self.hunks.iter().find(|r| r.eq(&range)))
.all(|x| x.is_some())
}
pub fn parse_string(s: &str) -> Result<Self> {
let mut parts = s.split(':');
let file_path = parts.next().unwrap();
let ranges = match parts.next() {
Some(raw_ranges) => raw_ranges
.split(',')
.map(Hunk::try_from)
.collect::<Result<Vec<Hunk>>>(),
None => Ok(vec![]),
}
.context(format!("failed to parse ownership ranges: {}", s))?;
Ok(Self {
file_path: path::PathBuf::from(file_path),
hunks: ranges,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_ownership() {
let ownership = Ownership::parse_string("foo/bar.rs:1-2,4-5").unwrap();
assert_eq!(
ownership,
Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
hunks: vec![(1..=2).into(), (4..=5).into()]
}
);
}
#[test]
fn parse_ownership_no_ranges() {
let ownership = Ownership::parse_string("foo/bar.rs").unwrap();
assert_eq!(
ownership,
Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
hunks: vec![]
}
);
}
#[test]
fn parse_ownership_invalid_range() {
let ownership = Ownership::parse_string("foo/bar.rs:1-2,4-5-6");
assert!(ownership.is_err());
}
#[test]
fn parse_ownership_invalid_range_2() {
let ownership = Ownership::parse_string("foo/bar.rs:1-2,6-5");
assert!(ownership.is_err());
}
#[test]
fn ownership_to_from_string() {
let ownership = Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
hunks: vec![(1..=2).into(), (4..=5).into()],
};
assert_eq!(ownership.to_string(), "foo/bar.rs:1-2,4-5".to_string());
assert_eq!(
Ownership::parse_string(&ownership.to_string()).unwrap(),
ownership
);
}
#[test]
fn ownership_to_from_string_no_ranges() {
let ownership = Ownership {
file_path: path::PathBuf::from("foo/bar.rs"),
hunks: vec![],
};
assert_eq!(ownership.to_string(), "foo/bar.rs".to_string());
assert_eq!(
Ownership::parse_string(&ownership.to_string()).unwrap(),
ownership
);
}
#[test]
fn test_normalize() {
vec![
("file.txt:1-10", "file.txt:1-10"),
("file.txt:1-10,15-16", "file.txt:1-10,15-16"),
("file.txt:1-10,10-15,15-16", "file.txt:1-10,10-15,15-16"),
("file.txt:1-10,5-12", "file.txt:1-10,5-12"),
("file.txt:15-16,1-10", "file.txt:1-10,15-16"),
]
.into_iter()
.map(|(a, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(expected).unwrap(),
)
})
.for_each(|(a, expected)| {
let got = a.normalize();
assert_eq!(
got, expected,
"normalize {} expected {}, got {}",
a, expected, got
);
});
}
#[test]
fn test_plus() {
vec![
("file.txt:1-10", "another.txt:1-5", "file.txt:1-10"),
("file.txt:1-10", "file.txt:1-5", "file.txt:1-10,1-5"),
("file.txt:1-10", "file.txt:12-15", "file.txt:1-10,12-15"),
(
"file.txt:1-10",
"file.txt:8-15,20-25",
"file.txt:1-10,8-15,20-25",
),
("file.txt:1-10", "file.txt", "file.txt"),
("file.txt", "file.txt:1-10", "file.txt"),
("file.txt:1-10", "file.txt:10-15", "file.txt:1-10,10-15"),
("file.txt:5-10", "file.txt:1-5", "file.txt:1-5,5-10"),
("file.txt:1-10", "file.txt:1-10", "file.txt:1-10"),
("file.txt:5-10", "file.txt:2-7", "file.txt:2-7,5-10"),
("file.txt:5-10", "file.txt:7-12", "file.txt:5-10,7-12"),
]
.into_iter()
.map(|(a, b, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(b).unwrap(),
Ownership::parse_string(expected).unwrap(),
)
})
.for_each(|(a, b, expected)| {
let got = a.plus(&b);
assert_eq!(
got, expected,
"{} plus {}, expected {}, got {}",
a, b, expected, got
);
});
}
#[test]
fn test_minus() {
vec![
("file.txt:1-10", "another.txt:1-5", Some("file.txt:1-10")),
("file.txt:1-10", "file.txt:1-5", Some("file.txt:1-10")),
("file.txt:1-10", "file.txt:11-15", Some("file.txt:1-10")),
("file.txt:1-10", "file.txt:1-10", None),
("file.txt:1-10", "file.txt", None),
("file.txt", "file.txt", None),
("file.txt", "file.txt:1-10", Some("file.txt")),
(
"file.txt:1-10,11-15",
"file.txt:11-15",
Some("file.txt:1-10"),
),
(
"file.txt:1-10,11-15,15-17",
"file.txt:1-10,15-17",
Some("file.txt:11-15"),
),
]
.into_iter()
.map(|(a, b, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(b).unwrap(),
expected.map(|s| Ownership::parse_string(s).unwrap()),
)
})
.for_each(|(a, b, expected)| {
let got = a.minus(&b);
assert_eq!(
got, expected,
"{} minus {}, expected {:?}, got {:?}",
a, b, expected, got
);
});
}
#[test]
fn test_contains() {
vec![
("file.txt", "another.txt", false),
("file.txt", "file.txt", true),
("file.txt", "file.txt:1-10", true),
("file.txt:1-10", "file.txt", false),
("file.txt:1-10", "file.txt:11-20", false),
("file.txt:1-10", "file.txt:1-5", false),
("file.txt:1-10", "file.txt:5-10", false),
("file.txt:1-10", "file.txt:1-10", true),
("file.txt:1-10", "file.txt:2-7", false),
("file.txt:3-5", "file.txt:1-10", false),
("file.txt:1-10", "another.txt:1-10", false),
("file.txt:1-10", "file.txt:1-10,20-25", false),
("file.txt:1-10,11-15", "file.txt:11-15", true),
]
.into_iter()
.map(|(a, b, expected)| {
(
Ownership::parse_string(a).unwrap(),
Ownership::parse_string(b).unwrap(),
expected,
)
})
.for_each(|(a, b, expected)| {
assert_eq!(
a.contains(&b),
expected,
"{} contains {}, expected {}",
a,
b,
expected
);
});
}
}
impl fmt::Display for Ownership {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
if self.hunks.is_empty() {
write!(f, "{}", self.file_path.to_str().unwrap())
} else {
write!(
f,
"{}:{}",
self.file_path.to_str().unwrap(),
self.hunks
.iter()
.map(|r| r.to_string())
.collect::<Vec<String>>()
.join(",")
)
}
}
}

View File

@ -66,7 +66,7 @@ mod tests {
.unwrap(),
ownership: vec![Ownership {
file_path: format!("file/{}", unsafe { TEST_INDEX }).into(),
ranges: vec![],
hunks: vec![],
}],
}
}

View File

@ -147,7 +147,7 @@ mod tests {
.unwrap(),
ownership: vec![branch::Ownership {
file_path: format!("file/{}", unsafe { TEST_INDEX }).into(),
ranges: vec![],
hunks: vec![],
}],
}
}

View File

@ -94,7 +94,7 @@ mod tests {
.unwrap(),
ownership: vec![branch::Ownership {
file_path: format!("file/{}", unsafe { TEST_INDEX }).into(),
ranges: vec![],
hunks: vec![],
}],
}
}

View File

@ -4,7 +4,7 @@ pub mod target;
use std::{
collections::{HashMap, HashSet},
ops, path, time, vec,
path, time, vec,
};
use anyhow::{bail, Context, Result};
@ -429,7 +429,7 @@ pub fn move_files(
};
for ownership in to_move {
let source_branches = if ownership.ranges.is_empty() {
let source_branches = if ownership.hunks.is_empty() {
// find all branches that own any part of the file
virtual_branches
.clone()
@ -483,34 +483,6 @@ pub fn move_files(
Ok(())
}
fn distance(a: &ops::RangeInclusive<usize>, b: &ops::RangeInclusive<usize>) -> usize {
if a.start() > b.end() {
a.start() - b.end()
} else if b.start() > a.end() {
b.start() - a.end()
} else {
0
}
}
fn ranges_intersect(
one: &ops::RangeInclusive<usize>,
another: &ops::RangeInclusive<usize>,
) -> bool {
one.contains(another.start())
|| one.contains(another.end())
|| another.contains(one.start())
|| another.contains(one.end())
}
fn ranges_touching(
one: &ops::RangeInclusive<usize>,
another: &ops::RangeInclusive<usize>,
context: usize,
) -> bool {
distance(one, another) <= context || distance(another, one) <= context
}
fn explicit_owner(stack: &[branch::Branch], needle: &branch::Ownership) -> Option<branch::Branch> {
stack
.iter()
@ -518,7 +490,7 @@ fn explicit_owner(stack: &[branch::Branch], needle: &branch::Ownership) -> Optio
branch
.ownership
.iter()
.filter(|ownership| !ownership.ranges.is_empty()) // only consider explicit ownership
.filter(|ownership| !ownership.hunks.is_empty()) // only consider explicit ownership
.any(|ownership| ownership.contains(needle))
})
.cloned()
@ -534,13 +506,13 @@ fn owned_by_proximity(
branch
.ownership
.iter()
.filter(|ownership| !ownership.ranges.is_empty()) // only consider explicit ownership
.filter(|ownership| !ownership.hunks.is_empty()) // only consider explicit ownership
.any(|ownership| {
ownership.ranges.iter().any(|range| {
ownership.hunks.iter().any(|range| {
needle
.ranges
.hunks
.iter()
.any(|r| ranges_touching(r, range, 6) || ranges_intersect(r, range))
.any(|r| r.touches(range) || r.intersects(range))
})
})
})

View File

@ -86,7 +86,7 @@ mod tests {
.unwrap(),
ownership: vec![branch::Ownership {
file_path: format!("file/{}", unsafe { TEST_INDEX }).into(),
ranges: vec![],
hunks: vec![],
}],
}
}

View File

@ -104,7 +104,7 @@ mod tests {
.unwrap(),
ownership: vec![branch::Ownership {
file_path: format!("file/{}", unsafe { TEST_INDEX }).into(),
ranges: vec![],
hunks: vec![],
}],
}
}

View File

@ -48,7 +48,7 @@ fn test_branch() -> virtual_branches::branch::Branch {
.unwrap(),
ownership: vec![branch::Ownership {
file_path: format!("file/{}", unsafe { TEST_INDEX }).into(),
ranges: vec![],
hunks: vec![],
}],
}
}