git-ents.gitmain
⌘K
foforge
util.rs71 lines · 2.7 KB · rusthistorycomment on this file
1//! Small `gix` plumbing helpers shared by [`crate::capture`] and
2//! [`crate::project`]/[`crate::project_exact`]/[`crate::project_from_context`].
3//!
4//! Nothing here is public API; each function is a thin, single-purpose
5//! wrapper over a `gix::Repository` lookup, kept out of the call sites that
6//! use it so the projection and capture logic reads as policy rather than
7//! plumbing.
8
9use gix::ObjectId;
10use gix::bstr::ByteSlice as _;
11
12use crate::error::{Error, Result};
13
14/// Resolve `revision` (a hex id, ref name, or revspec) to the commit it
15/// names in `repo`.
16pub(crate) fn resolve_commit<'repo>(
17 repo: &'repo gix::Repository,
18 revision: &str,
19) -> Result<gix::Commit<'repo>> {
20 let resolve = || Error::Resolve(revision.to_owned());
21 repo.rev_parse_single(revision)
22 .map_err(|_error| resolve())?
23 .object()
24 .map_err(|_error| resolve())?
25 .peel_to_kind(gix::object::Kind::Commit)
26 .map_err(|_error| resolve())?
27 .try_into_commit()
28 .map_err(|_error| resolve())
29}
30
31/// Look up the commit `id` names directly, with no revision parsing — for an
32/// [`crate::Anchor`]'s own recorded commit, which already names a concrete
33/// object rather than an arbitrary revision.
34pub(crate) fn commit_at(repo: &gix::Repository, id: ObjectId) -> Result<gix::Commit<'_>> {
35 let resolve = || Error::Resolve(id.to_string());
36 repo.find_object(id)
37 .map_err(|_error| resolve())?
38 .peel_to_kind(gix::object::Kind::Commit)
39 .map_err(|_error| resolve())?
40 .try_into_commit()
41 .map_err(|_error| resolve())
42}
43
44/// Read the full contents of the blob at `id`.
45pub(crate) fn read_blob(repo: &gix::Repository, id: ObjectId) -> Result<Vec<u8>> {
46 Ok(repo
47 .find_blob(id)
48 .map_err(|error| Error::Object(error.to_string()))?
49 .take_data())
50}
51
52/// The text of the 1-based inclusive `range` within `data`, or
53/// [`Error::LinesOutOfRange`] (naming `path`) when the range does not fit.
54pub(crate) fn lines_of(data: &[u8], path: &str, range: crate::LineRange) -> Result<String> {
55 let all: Vec<&[u8]> = data.lines_with_terminator().collect();
56 let out_of_range = || Error::LinesOutOfRange {
57 path: path.to_owned(),
58 start: range.start,
59 end: range.end,
60 len: u64::try_from(all.len()).unwrap_or(u64::MAX),
61 };
62 // One slice lookup validates the whole range: start == 0 dies in
63 // checked_sub, an inverted or oversized range dies in get.
64 let first = usize::try_from(range.start)
65 .ok()
66 .and_then(|start| start.checked_sub(1))
67 .ok_or_else(out_of_range)?;
68 let last = usize::try_from(range.end).ok().ok_or_else(out_of_range)?;
69 let bytes = all.get(first..last).ok_or_else(out_of_range)?.concat();
70 Ok(String::from_utf8_lossy(&bytes).into_owned())
71}