rust: migrate libutil/git.cc
Change-Id: Ic3932fdf3afd3dc09019623516597f1c648f7fc4
This commit is contained in:
committed by
eldritch horrors
parent
dd8f1843cc
commit
41bad096e3
Generated
+32
-2
@@ -189,7 +189,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e82d74e6c83060ec269fe9e0d408d6de4a1645d525f9a0bbbb841ba4efd91ac"
|
||||
dependencies = [
|
||||
"hashbrown 0.15.5",
|
||||
"regex-automata",
|
||||
"regex-automata 0.3.9",
|
||||
"serde",
|
||||
"stacker",
|
||||
"unicode-ident",
|
||||
@@ -451,6 +451,7 @@ name = "lix"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"lix-doc",
|
||||
"regex",
|
||||
"rustyline",
|
||||
"rustyline-derive",
|
||||
"zngur",
|
||||
@@ -604,6 +605,18 @@ dependencies = [
|
||||
"nibble_vec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata 0.4.14",
|
||||
"regex-syntax 0.8.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.3.9"
|
||||
@@ -612,7 +625,18 @@ checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.7.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax 0.8.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -621,6 +645,12 @@ version = "0.7.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
|
||||
|
||||
[[package]]
|
||||
name = "rnix"
|
||||
version = "0.12.0"
|
||||
|
||||
@@ -11,6 +11,7 @@ edition = "2021"
|
||||
|
||||
[workspace.dependencies]
|
||||
clap = "4"
|
||||
regex = "1.12.4"
|
||||
rustyline = "18"
|
||||
rustyline-derive = "0.12"
|
||||
syn = "2.0"
|
||||
|
||||
+10
-11
@@ -16,9 +16,10 @@
|
||||
#include "lix/libutil/url-parts.hh"
|
||||
#include "lix/libstore/pathlocks.hh"
|
||||
#include "lix/libutil/users.hh"
|
||||
#include "lix/libutil/git.hh"
|
||||
#include "lix/libutil/logging.hh"
|
||||
#include "lix/libutil/finally.hh"
|
||||
#include "lix/lix-rs/main.gen.hh"
|
||||
#include "lix/lix-rs/utils.hh"
|
||||
|
||||
#include "lix/libfetchers/fetch-settings.hh"
|
||||
|
||||
@@ -81,17 +82,15 @@ try {
|
||||
));
|
||||
|
||||
std::string_view line = output;
|
||||
line = line.substr(0, line.find("\n"));
|
||||
if (const auto parseResult = git::parseLsRemoteLine(line)) {
|
||||
switch (parseResult->kind) {
|
||||
case git::LsRemoteRefLine::Kind::Symbolic:
|
||||
debug("resolved HEAD ref '%s' for repo '%s'", parseResult->target, path);
|
||||
break;
|
||||
case git::LsRemoteRefLine::Kind::Object:
|
||||
debug("resolved HEAD rev '%s' for repo '%s'", parseResult->target, path);
|
||||
break;
|
||||
auto line_rs = rust::to_string(line.substr(0, line.find("\n")));
|
||||
if (const auto parseResult = to_std(rust::lix::fetchers::git::parse_ls_remote_line(line_rs.as_str()))) {
|
||||
auto target = to_std_string(parseResult->target.as_str());
|
||||
if (parseResult->kind.matches_Object()) {
|
||||
debug("resolved HEAD rev '%s' for repo '%s'", target, path);
|
||||
} else {
|
||||
debug("resolved HEAD ref '%s' for repo '%s'", target, path);
|
||||
}
|
||||
co_return parseResult->target;
|
||||
co_return to_std_string(parseResult->target.as_str());
|
||||
}
|
||||
co_return std::nullopt;
|
||||
} catch (ExecError &) {
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
#include "lix/libutil/result.hh"
|
||||
#include "lix/libutil/types.hh"
|
||||
#include "lix/libutil/url-parts.hh"
|
||||
#include "lix/libutil/git.hh"
|
||||
#include "lix/libutil/json.hh"
|
||||
#include "lix/libfetchers/fetchers.hh"
|
||||
#include "lix/libfetchers/fetch-settings.hh"
|
||||
#include "lix/lix-rs/main.gen.hh"
|
||||
#include "lix/lix-rs/utils.hh"
|
||||
|
||||
#include <optional>
|
||||
#include <fstream>
|
||||
@@ -486,15 +487,15 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
std::string line;
|
||||
getline(is, line);
|
||||
|
||||
auto remoteLine = git::parseLsRemoteLine(line);
|
||||
auto remoteLine =
|
||||
to_std(rust::lix::fetchers::git::parse_ls_remote_line(rust::to_string(line).as_str()));
|
||||
if (!remoteLine) {
|
||||
throw BadURL("in '%d', couldn't resolve HEAD ref '%d'", input.to_string(), ref);
|
||||
}
|
||||
refUri = remoteLine->target;
|
||||
refUri = to_std_string(remoteLine->target.as_str());
|
||||
} else {
|
||||
refUri = fmt("refs/(heads|tags)/%s", ref);
|
||||
}
|
||||
std::regex refRegex = regex::parse(refUri);
|
||||
|
||||
auto file = store->toRealPath(
|
||||
TRY_AWAIT(downloadFile(store, fmt("%s/info/refs", base_url), "source", false, headers))
|
||||
@@ -505,9 +506,11 @@ struct SourceHutInputScheme : GitArchiveInputScheme
|
||||
std::string line;
|
||||
std::optional<std::string> id;
|
||||
while(!id && getline(is, line)) {
|
||||
auto parsedLine = git::parseLsRemoteLine(line);
|
||||
if (parsedLine && parsedLine->reference && std::regex_match(*parsedLine->reference, refRegex))
|
||||
id = parsedLine->target;
|
||||
auto parsedLine =
|
||||
to_std(rust::lix::fetchers::git::parse_ls_remote_line(rust::to_string(line).as_str()));
|
||||
if (parsedLine && (*parsedLine).matches_ref_uri(rust::to_string(refUri).as_str())) {
|
||||
id = to_std_string(parsedLine->target.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
if(!id)
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "lix/libutil/git.hh"
|
||||
#include "regex.hh"
|
||||
|
||||
#include <regex>
|
||||
|
||||
namespace nix {
|
||||
namespace git {
|
||||
|
||||
std::optional<LsRemoteRefLine> parseLsRemoteLine(std::string_view line)
|
||||
{
|
||||
const static std::regex line_regex = regex::parse("^(ref: *)?([^\\s]+)(?:\\t+(.*))?$");
|
||||
std::match_results<std::string_view::const_iterator> match;
|
||||
if (!std::regex_match(line.cbegin(), line.cend(), match, line_regex))
|
||||
return std::nullopt;
|
||||
|
||||
return LsRemoteRefLine {
|
||||
.kind = match[1].length() == 0
|
||||
? LsRemoteRefLine::Kind::Object
|
||||
: LsRemoteRefLine::Kind::Symbolic,
|
||||
.target = match[2],
|
||||
.reference = match[3].length() == 0 ? std::nullopt : std::optional<std::string>{ match[3] }
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
///@file
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <optional>
|
||||
|
||||
namespace nix {
|
||||
|
||||
namespace git {
|
||||
|
||||
/**
|
||||
* A line from the output of `git ls-remote --symref`.
|
||||
*
|
||||
* These can be of two kinds:
|
||||
*
|
||||
* - Symbolic references of the form
|
||||
*
|
||||
* ref: {target} {reference}
|
||||
*
|
||||
* where {target} is itself a reference and {reference} is optional
|
||||
*
|
||||
* - Object references of the form
|
||||
*
|
||||
* {target} {reference}
|
||||
*
|
||||
* where {target} is a commit id and {reference} is mandatory
|
||||
*/
|
||||
struct LsRemoteRefLine {
|
||||
enum struct Kind {
|
||||
Symbolic,
|
||||
Object
|
||||
};
|
||||
Kind kind;
|
||||
std::string target;
|
||||
std::optional<std::string> reference;
|
||||
};
|
||||
|
||||
std::optional<LsRemoteRefLine> parseLsRemoteLine(std::string_view line);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@ liblix_sources += files(
|
||||
'file-descriptor.cc',
|
||||
'file-system.cc',
|
||||
'fmt.cc',
|
||||
'git.cc',
|
||||
'hash.cc',
|
||||
'hilite.cc',
|
||||
'io-buffer.cc',
|
||||
@@ -103,7 +102,6 @@ libutil_headers = files(
|
||||
'finally.hh',
|
||||
'fmt.hh',
|
||||
'generator.hh',
|
||||
'git.hh',
|
||||
'hash.hh',
|
||||
'hilite.hh',
|
||||
'input-accessor.hh',
|
||||
|
||||
@@ -12,6 +12,7 @@ default = []
|
||||
|
||||
[dependencies]
|
||||
lix-doc.path = "../lix-doc"
|
||||
regex.workspace=true
|
||||
rustyline.workspace = true
|
||||
rustyline-derive.workspace = true
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
lix_rs_files += files(
|
||||
'Cargo.toml',
|
||||
# keep-sorted start
|
||||
'src/fetchers/git.rs',
|
||||
'src/fetchers/mod.rs',
|
||||
'src/ffi_test.rs',
|
||||
'src/lib.rs',
|
||||
'src/repl.rs',
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
use ::std::string::String as String;
|
||||
|
||||
mod crate::fetchers::git {
|
||||
type Kind {
|
||||
#layout_conservative(size=16, align=8);
|
||||
constructor Symbolic;
|
||||
constructor Object;
|
||||
}
|
||||
type LsRemoteRefLine {
|
||||
#layout_conservative(size=56, align=8);
|
||||
field kind (offset=auto, type=Kind);
|
||||
field target (offset=auto, type=String);
|
||||
field reference (offset=auto, type=::std::option::Option<String>);
|
||||
|
||||
fn matches_ref_uri(&self, &str) -> bool;
|
||||
}
|
||||
|
||||
fn parse_ls_remote_line(&str) -> ::std::option::Option<LsRemoteRefLine>;
|
||||
|
||||
type ::std::option::Option<LsRemoteRefLine> {
|
||||
#layout_conservative(size=56, align=8);
|
||||
|
||||
constructor None;
|
||||
constructor Some(LsRemoteRefLine);
|
||||
|
||||
fn unwrap(self) -> LsRemoteRefLine;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
use std::sync::LazyLock;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum Kind {
|
||||
Symbolic,
|
||||
Object,
|
||||
}
|
||||
|
||||
///
|
||||
/// A line from the output of `git ls-remote --symref`
|
||||
///
|
||||
/// These can be of two kinds:
|
||||
///
|
||||
/// - Symbolic references of the form
|
||||
/// ref: {target} {references}
|
||||
/// where {target} is itself a reference and {reference} is optional
|
||||
///
|
||||
/// - Object reference of the form
|
||||
/// {target} {reference}
|
||||
/// where {target} is a commit id and {reference} is mandatory
|
||||
pub struct LsRemoteRefLine {
|
||||
pub kind: Kind,
|
||||
pub target: String,
|
||||
pub reference: Option<String>,
|
||||
}
|
||||
|
||||
impl LsRemoteRefLine {
|
||||
pub fn matches_ref_uri(&self, uri: &str) -> bool {
|
||||
let uri_regex = Regex::new(uri).unwrap();
|
||||
match &self.reference {
|
||||
None => false,
|
||||
Some(r) => uri_regex.is_match(r.as_str()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static LS_REMOTE_REF_REGEX: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"^(ref: *)?([^\s]+)(?:\t+(.*))?$").unwrap());
|
||||
|
||||
pub fn parse_ls_remote_line(line: &str) -> Option<LsRemoteRefLine> {
|
||||
LS_REMOTE_REF_REGEX.captures(line).map(|m| {
|
||||
let kind = m
|
||||
.get(1)
|
||||
.filter(|ma| !ma.is_empty())
|
||||
.map_or(Kind::Object, |_| Kind::Symbolic);
|
||||
let re = m.get(3).map(|ma| ma.as_str()).map(|ma| {
|
||||
if ma.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(ma.to_string())
|
||||
}
|
||||
});
|
||||
|
||||
LsRemoteRefLine {
|
||||
kind: kind,
|
||||
target: m
|
||||
.get(2)
|
||||
.expect("regex for matching remotes has two capture groups defined, so this should not error")
|
||||
.as_str()
|
||||
.to_string(),
|
||||
reference: re.flatten(),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_symref_line_with_reference() {
|
||||
let line = "ref: refs/head/main\tHEAD";
|
||||
let res = parse_ls_remote_line(line);
|
||||
assert!(res.is_some());
|
||||
let res = res.unwrap();
|
||||
assert_eq!(res.kind, Kind::Symbolic);
|
||||
assert_eq!(res.target, "refs/head/main");
|
||||
assert_eq!(res.reference, Some("HEAD".to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_symref_line_with_no_reference() {
|
||||
let line = "ref: refs/head/main";
|
||||
let res = parse_ls_remote_line(line);
|
||||
assert!(res.is_some());
|
||||
let res = res.unwrap();
|
||||
assert_eq!(res.kind, Kind::Symbolic);
|
||||
assert_eq!(res.target, "refs/head/main");
|
||||
assert_eq!(res.reference, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_object_ref_line() {
|
||||
let line = "abc123 refs/head/main";
|
||||
let res = parse_ls_remote_line(line);
|
||||
assert!(res.is_some());
|
||||
let res = res.unwrap();
|
||||
assert_eq!(res.kind, Kind::Object);
|
||||
assert_eq!(res.target, "abc123");
|
||||
assert_eq!(res.reference, Some("refs/head/main".to_string()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod git;
|
||||
@@ -145,5 +145,6 @@ pub(crate) mod log {
|
||||
}
|
||||
}
|
||||
|
||||
pub mod fetchers;
|
||||
mod ffi_test;
|
||||
mod repl;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
merge "./ffi_test.zng";
|
||||
merge "./repl.zng";
|
||||
merge "./fetchers.zng";
|
||||
|
||||
mod crate::ffi {
|
||||
use ::std::result::Result as Result;
|
||||
|
||||
@@ -35,6 +35,15 @@ mod ::std::option {
|
||||
|
||||
fn unwrap(self) -> u64;
|
||||
}
|
||||
|
||||
type Option<String> {
|
||||
#layout_conservative(size=24, align=8);
|
||||
|
||||
constructor Some(String);
|
||||
constructor None;
|
||||
|
||||
fn unwrap(self) -> String;
|
||||
}
|
||||
}
|
||||
|
||||
mod ::std::result {
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
#include "lix/libutil/git.hh"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace nix {
|
||||
|
||||
TEST(GitLsRemote, parseSymrefLineWithReference) {
|
||||
auto line = "ref: refs/head/main HEAD";
|
||||
auto res = git::parseLsRemoteLine(line);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic);
|
||||
ASSERT_EQ(res->target, "refs/head/main");
|
||||
ASSERT_EQ(res->reference, "HEAD");
|
||||
}
|
||||
|
||||
TEST(GitLsRemote, parseSymrefLineWithNoReference) {
|
||||
auto line = "ref: refs/head/main";
|
||||
auto res = git::parseLsRemoteLine(line);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic);
|
||||
ASSERT_EQ(res->target, "refs/head/main");
|
||||
ASSERT_EQ(res->reference, std::nullopt);
|
||||
}
|
||||
|
||||
TEST(GitLsRemote, parseObjectRefLine) {
|
||||
auto line = "abc123 refs/head/main";
|
||||
auto res = git::parseLsRemoteLine(line);
|
||||
ASSERT_TRUE(res.has_value());
|
||||
ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Object);
|
||||
ASSERT_EQ(res->target, "abc123");
|
||||
ASSERT_EQ(res->reference, "refs/head/main");
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,6 @@ libutil_tests_sources = files(
|
||||
'libutil/escape-string.cc',
|
||||
'libutil/fmt.cc',
|
||||
'libutil/generator.cc',
|
||||
'libutil/git.cc',
|
||||
'libutil/hash.cc',
|
||||
'libutil/hilite.cc',
|
||||
'libutil/io-buffer.cc',
|
||||
|
||||
Reference in New Issue
Block a user