Make `Id` to string conversion more explicit
Two types of conversion: `canonical`, which is just the base58 encoding, and `urn` which is like canonical, but includes `rad:`.
This commit is contained in:
parent
b78790622f
commit
1f06f5340f
|
|
@ -232,7 +232,7 @@ pub fn init(options: Options, profile: &profile::Profile) -> anyhow::Result<()>
|
||||||
term::blank();
|
term::blank();
|
||||||
term::info!(
|
term::info!(
|
||||||
"Your project id is {}. You can show it any time by running:",
|
"Your project id is {}. You can show it any time by running:",
|
||||||
term::format::highlight(id)
|
term::format::highlight(id.urn())
|
||||||
);
|
);
|
||||||
term::indented(term::format::secondary("rad ."));
|
term::indented(term::format::secondary("rad ."));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -130,7 +130,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let path = profile
|
let path = profile
|
||||||
.paths()
|
.paths()
|
||||||
.storage()
|
.storage()
|
||||||
.join(id.to_human())
|
.join(id.urn())
|
||||||
.join("refs")
|
.join("refs")
|
||||||
.join("namespaces");
|
.join("namespaces");
|
||||||
|
|
||||||
|
|
@ -195,7 +195,7 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
} else if options.id_only {
|
} else if options.id_only {
|
||||||
term::info!("{}", term::format::highlight(id.to_human()));
|
term::info!("{}", term::format::highlight(id.urn()));
|
||||||
} else {
|
} else {
|
||||||
term::info!("{}", term::format::highlight(id));
|
term::info!("{}", term::format::highlight(id));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ pub fn run(_options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let head = term::format::oid(head);
|
let head = term::format::oid(head);
|
||||||
table.push([
|
table.push([
|
||||||
term::format::bold(proj.name()),
|
term::format::bold(proj.name()),
|
||||||
term::format::tertiary(id),
|
term::format::tertiary(id.urn()),
|
||||||
term::format::secondary(head),
|
term::format::secondary(head),
|
||||||
term::format::italic(proj.description()),
|
term::format::italic(proj.description()),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -81,12 +81,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
let id = options.id;
|
let id = options.id;
|
||||||
|
|
||||||
if let Ok(Some(_)) = storage.get(signer.public_key(), id.to_owned()) {
|
if let Ok(Some(_)) = storage.get(signer.public_key(), id.to_owned()) {
|
||||||
let namespace = profile.paths().storage().join(id.to_human());
|
let namespace = profile.paths().storage().join(id.urn());
|
||||||
|
|
||||||
if !options.confirm
|
if !options.confirm
|
||||||
|| term::confirm(format!(
|
|| term::confirm(format!(
|
||||||
"Are you sure you would like to delete {}?",
|
"Are you sure you would like to delete {}?",
|
||||||
term::format::dim(id.to_human())
|
term::format::dim(id.urn())
|
||||||
))
|
))
|
||||||
{
|
{
|
||||||
rad_untrack::untrack(id.to_owned(), &profile)?;
|
rad_untrack::untrack(id.to_owned(), &profile)?;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl Args for Options {
|
||||||
Value(val) if id.is_none() => {
|
Value(val) if id.is_none() => {
|
||||||
let val = val.to_string_lossy();
|
let val = val.to_string_lossy();
|
||||||
|
|
||||||
if let Ok(val) = Id::from_human(&val) {
|
if let Ok(val) = Id::from_urn(&val) {
|
||||||
id = Some(val);
|
id = Some(val);
|
||||||
} else {
|
} else {
|
||||||
return Err(anyhow!("invalid ID '{}'", val));
|
return Err(anyhow!("invalid ID '{}'", val));
|
||||||
|
|
@ -74,13 +74,13 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
|
||||||
term::success!(
|
term::success!(
|
||||||
"Tracking relationships for {} ({}) removed",
|
"Tracking relationships for {} ({}) removed",
|
||||||
term::format::highlight(project.name()),
|
term::format::highlight(project.name()),
|
||||||
&id.to_human()
|
&id.urn()
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
term::info!(
|
term::info!(
|
||||||
"Tracking relationships for {} ({}) doesn't exist",
|
"Tracking relationships for {} ({}) doesn't exist",
|
||||||
term::format::highlight(project.name()),
|
term::format::highlight(project.name()),
|
||||||
&id.to_human()
|
&id.urn()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use radicle::identity::Id;
|
use radicle::identity::Id;
|
||||||
|
|
||||||
|
|
@ -10,7 +9,7 @@ fn main() -> anyhow::Result<()> {
|
||||||
let signer = profile.signer()?;
|
let signer = profile.signer()?;
|
||||||
|
|
||||||
if let Some(id) = env::args().nth(1) {
|
if let Some(id) = env::args().nth(1) {
|
||||||
let id = Id::from_str(&id)?;
|
let id = Id::from_urn(&id)?;
|
||||||
let mut node = radicle::node::connect(profile.socket())?;
|
let mut node = radicle::node::connect(profile.socket())?;
|
||||||
let repo = radicle::rad::clone(id, &cwd, &signer, &profile.storage, &mut node)?;
|
let repo = radicle::rad::clone(id, &cwd, &signer, &profile.storage, &mut node)?;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ mod test {
|
||||||
assert_eq!(format!("\"{}\"", pk), json);
|
assert_eq!(format!("\"{}\"", pk), json);
|
||||||
|
|
||||||
let json = serde_json::to_string(&proj).unwrap();
|
let json = serde_json::to_string(&proj).unwrap();
|
||||||
assert_eq!(format!("\"{}\"", proj), json);
|
assert_eq!(format!("\"{}\"", proj.urn()), json);
|
||||||
|
|
||||||
let json = serde_json::to_string(&did).unwrap();
|
let json = serde_json::to_string(&did).unwrap();
|
||||||
assert_eq!(format!("\"{}\"", did), json);
|
assert_eq!(format!("\"{}\"", did), json);
|
||||||
|
|
|
||||||
|
|
@ -394,10 +394,7 @@ mod test {
|
||||||
(*id).to_string(),
|
(*id).to_string(),
|
||||||
"d96f425412c9f8ad5d9a9a05c9831d0728e2338d"
|
"d96f425412c9f8ad5d9a9a05c9831d0728e2338d"
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(id.urn(), String::from("rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji"));
|
||||||
id.to_human(),
|
|
||||||
String::from("rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ pub struct Id(git::Oid);
|
||||||
|
|
||||||
impl fmt::Display for Id {
|
impl fmt::Display for Id {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str(&self.to_human())
|
f.write_str(self.urn().as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,23 +38,34 @@ impl fmt::Debug for Id {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Id {
|
impl Id {
|
||||||
/// Format the identifier in a human-readable way.
|
/// Format the identifier as a human-readable URN.
|
||||||
///
|
///
|
||||||
/// Eg. `rad:z3XncAdkZjeK9mQS5Sdc4qhw98BUX`.
|
/// Eg. `rad:z3XncAdkZjeK9mQS5Sdc4qhw98BUX`.
|
||||||
///
|
///
|
||||||
pub fn to_human(&self) -> String {
|
pub fn urn(&self) -> String {
|
||||||
format!(
|
format!("{RAD_PREFIX}{}", self.canonical())
|
||||||
"{RAD_PREFIX}{}",
|
|
||||||
multibase::encode(multibase::Base::Base58Btc, self.0.as_bytes())
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse an identifier from the human-readable format.
|
/// Parse an identifier from the human-readable URN format.
|
||||||
/// Accepts strings without the radicle prefix as well,
|
/// Accepts strings without the radicle prefix as well,
|
||||||
/// for convenience.
|
/// for convenience.
|
||||||
pub fn from_human(s: &str) -> Result<Self, IdError> {
|
pub fn from_urn(s: &str) -> Result<Self, IdError> {
|
||||||
let s = s.strip_prefix(RAD_PREFIX).unwrap_or(s);
|
let s = s.strip_prefix(RAD_PREFIX).unwrap_or(s);
|
||||||
let (_, bytes) = multibase::decode(s)?;
|
let id = Self::from_canonical(s)?;
|
||||||
|
|
||||||
|
Ok(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format the identifier as a multibase string.
|
||||||
|
///
|
||||||
|
/// Eg. `z3XncAdkZjeK9mQS5Sdc4qhw98BUX`.
|
||||||
|
///
|
||||||
|
pub fn canonical(&self) -> String {
|
||||||
|
multibase::encode(multibase::Base::Base58Btc, self.0.as_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_canonical(input: &str) -> Result<Self, IdError> {
|
||||||
|
let (_, bytes) = multibase::decode(input)?;
|
||||||
let array: git::Oid = bytes.as_slice().try_into()?;
|
let array: git::Oid = bytes.as_slice().try_into()?;
|
||||||
|
|
||||||
Ok(Self(array))
|
Ok(Self(array))
|
||||||
|
|
@ -65,7 +76,7 @@ impl FromStr for Id {
|
||||||
type Err = IdError;
|
type Err = IdError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
Self::from_human(s)
|
Self::from_urn(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -74,7 +85,7 @@ impl TryFrom<OsString> for Id {
|
||||||
|
|
||||||
fn try_from(value: OsString) -> Result<Self, Self::Error> {
|
fn try_from(value: OsString) -> Result<Self, Self::Error> {
|
||||||
let string = value.to_string_lossy();
|
let string = value.to_string_lossy();
|
||||||
Self::from_str(&string)
|
Self::from_canonical(&string)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -103,7 +114,7 @@ impl serde::Serialize for Id {
|
||||||
where
|
where
|
||||||
S: serde::Serializer,
|
S: serde::Serializer,
|
||||||
{
|
{
|
||||||
serde_ext::string::serialize(self, serializer)
|
serde_ext::string::serialize(&self.urn(), serializer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ impl Handle for Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch(&mut self, id: Id) -> Result<(), Error> {
|
fn fetch(&mut self, id: Id) -> Result<(), Error> {
|
||||||
for line in self.call("fetch", &[id])? {
|
for line in self.call("fetch", &[id.urn()])? {
|
||||||
let line = line?;
|
let line = line?;
|
||||||
log::debug!("node: {}", line);
|
log::debug!("node: {}", line);
|
||||||
}
|
}
|
||||||
|
|
@ -169,7 +169,7 @@ impl Handle for Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn track_repo(&mut self, id: Id) -> Result<bool, Error> {
|
fn track_repo(&mut self, id: Id) -> Result<bool, Error> {
|
||||||
let mut line = self.call("track-repo", &[id])?;
|
let mut line = self.call("track-repo", &[id.urn()])?;
|
||||||
let line = line
|
let line = line
|
||||||
.next()
|
.next()
|
||||||
.ok_or(Error::EmptyResponse { cmd: "track-repo" })??;
|
.ok_or(Error::EmptyResponse { cmd: "track-repo" })??;
|
||||||
|
|
@ -205,7 +205,7 @@ impl Handle for Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn untrack_repo(&mut self, id: Id) -> Result<bool, Error> {
|
fn untrack_repo(&mut self, id: Id) -> Result<bool, Error> {
|
||||||
let mut line = self.call("untrack-repo", &[id])?;
|
let mut line = self.call("untrack-repo", &[id.urn()])?;
|
||||||
let line = line.next().ok_or(Error::EmptyResponse {
|
let line = line.next().ok_or(Error::EmptyResponse {
|
||||||
cmd: "untrack-repo",
|
cmd: "untrack-repo",
|
||||||
})??;
|
})??;
|
||||||
|
|
@ -223,7 +223,7 @@ impl Handle for Node {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn announce_refs(&mut self, id: Id) -> Result<(), Error> {
|
fn announce_refs(&mut self, id: Id) -> Result<(), Error> {
|
||||||
for line in self.call("announce-refs", &[id])? {
|
for line in self.call("announce-refs", &[id.urn()])? {
|
||||||
let line = line?;
|
let line = line?;
|
||||||
log::debug!("node: {}", line);
|
log::debug!("node: {}", line);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ impl TryFrom<&Value> for Id {
|
||||||
|
|
||||||
fn try_from(value: &Value) -> Result<Self, Self::Error> {
|
fn try_from(value: &Value) -> Result<Self, Self::Error> {
|
||||||
match value {
|
match value {
|
||||||
Value::String(id) => Id::from_str(id).map_err(|e| sql::Error {
|
Value::String(id) => Id::from_urn(id).map_err(|e| sql::Error {
|
||||||
code: None,
|
code: None,
|
||||||
message: Some(e.to_string()),
|
message: Some(e.to_string()),
|
||||||
}),
|
}),
|
||||||
|
|
@ -27,7 +27,7 @@ impl TryFrom<&Value> for Id {
|
||||||
|
|
||||||
impl sqlite::BindableWithIndex for &Id {
|
impl sqlite::BindableWithIndex for &Id {
|
||||||
fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
|
fn bind<I: sql::ParameterIndex>(self, stmt: &mut sql::Statement<'_>, i: I) -> sql::Result<()> {
|
||||||
self.to_human().as_str().bind(stmt, i)
|
self.urn().as_str().bind(stmt, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ impl Storage {
|
||||||
let name = r.name().ok_or(Error::InvalidRef)?;
|
let name = r.name().ok_or(Error::InvalidRef)?;
|
||||||
let oid = r.target().ok_or(Error::InvalidRef)?;
|
let oid = r.target().ok_or(Error::InvalidRef)?;
|
||||||
|
|
||||||
println!("{} {} {}", proj, oid, name);
|
println!("{} {} {}", proj.urn(), oid, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -726,21 +726,17 @@ pub mod paths {
|
||||||
use super::ReadStorage;
|
use super::ReadStorage;
|
||||||
|
|
||||||
pub fn repository<S: ReadStorage>(storage: &S, proj: &Id) -> PathBuf {
|
pub fn repository<S: ReadStorage>(storage: &S, proj: &Id) -> PathBuf {
|
||||||
storage.path().join(proj.to_string())
|
storage.path().join(proj.canonical())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::{io, net, process, thread};
|
|
||||||
|
|
||||||
use crypto::test::signer::MockSigner;
|
use crypto::test::signer::MockSigner;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::assert_matches;
|
use crate::assert_matches;
|
||||||
use crate::git;
|
use crate::git;
|
||||||
use crate::rad;
|
|
||||||
use crate::storage::refs::SIGREFS_BRANCH;
|
use crate::storage::refs::SIGREFS_BRANCH;
|
||||||
use crate::storage::{ReadRepository, ReadStorage, RefUpdate, WriteRepository};
|
use crate::storage::{ReadRepository, ReadStorage, RefUpdate, WriteRepository};
|
||||||
use crate::test::arbitrary;
|
use crate::test::arbitrary;
|
||||||
|
|
@ -903,115 +899,6 @@ mod tests {
|
||||||
assert_eq!(refs, vec!["refs/heads/master", "refs/rad/id"]);
|
assert_eq!(refs, vec!["refs/heads/master", "refs/rad/id"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[ignore]
|
|
||||||
// Test the remote transport using `git-upload-pack` and TCP streams.
|
|
||||||
// Must be run on its own, since it tries to register the remote transport, which
|
|
||||||
// will fail if the mock transport was already registered.
|
|
||||||
fn test_upload_pack() {
|
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
|
||||||
let signer = MockSigner::default();
|
|
||||||
let remote = *signer.public_key();
|
|
||||||
let storage = Storage::open(tmp.path().join("storage")).unwrap();
|
|
||||||
let socket = net::TcpListener::bind(net::SocketAddr::from(([0, 0, 0, 0], 0))).unwrap();
|
|
||||||
let addr = socket.local_addr().unwrap();
|
|
||||||
let source_path = tmp.path().join("source");
|
|
||||||
let target_path = tmp.path().join("target");
|
|
||||||
let (source, _) = fixtures::repository(source_path);
|
|
||||||
|
|
||||||
transport::local::register(storage.clone());
|
|
||||||
|
|
||||||
let (proj, _, _) = rad::init(
|
|
||||||
&source,
|
|
||||||
"radicle",
|
|
||||||
"radicle",
|
|
||||||
git::refname!("master"),
|
|
||||||
&signer,
|
|
||||||
&storage,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let t = thread::spawn(move || {
|
|
||||||
let (stream, _) = socket.accept().unwrap();
|
|
||||||
let repo = storage.repository(proj).unwrap();
|
|
||||||
// NOTE: `GIT_PROTOCOL=version=2` doesn't work.
|
|
||||||
let mut child = process::Command::new("git")
|
|
||||||
.current_dir(repo.path())
|
|
||||||
.arg("upload-pack")
|
|
||||||
.arg("--strict") // The path to the git repo must be exact.
|
|
||||||
.arg(".")
|
|
||||||
.stdout(process::Stdio::piped())
|
|
||||||
.stdin(process::Stdio::piped())
|
|
||||||
.spawn()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut stdin = child.stdin.take().unwrap();
|
|
||||||
let mut stdout = child.stdout.take().unwrap();
|
|
||||||
|
|
||||||
let mut stream_r = stream.try_clone().unwrap();
|
|
||||||
let mut stream_w = stream;
|
|
||||||
|
|
||||||
let t = thread::spawn(move || {
|
|
||||||
let mut buf = [0u8; 1024];
|
|
||||||
|
|
||||||
while let Ok(n) = stream_r.read(&mut buf) {
|
|
||||||
if n == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if stdin.write_all(&buf[..n]).is_err() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
io::copy(&mut stdout, &mut stream_w).unwrap();
|
|
||||||
|
|
||||||
t.join().unwrap();
|
|
||||||
child.wait().unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut updates = Vec::new();
|
|
||||||
{
|
|
||||||
let mut callbacks = git2::RemoteCallbacks::new();
|
|
||||||
let mut opts = git2::FetchOptions::default();
|
|
||||||
|
|
||||||
callbacks.update_tips(|name, _, _| {
|
|
||||||
updates.push(name.to_owned());
|
|
||||||
true
|
|
||||||
});
|
|
||||||
opts.remote_callbacks(callbacks);
|
|
||||||
|
|
||||||
let target = git2::Repository::init_bare(target_path).unwrap();
|
|
||||||
let stream = net::TcpStream::connect(addr).unwrap();
|
|
||||||
|
|
||||||
// Register the `heartwood://` transport for this stream.
|
|
||||||
transport::remote::register(remote, stream.try_clone().unwrap());
|
|
||||||
|
|
||||||
// Fetch with the `heartwood://` transport.
|
|
||||||
target
|
|
||||||
.remote_anonymous(&format!("heartwood://{remote}/{proj}"))
|
|
||||||
.unwrap()
|
|
||||||
.fetch(
|
|
||||||
&["refs/namespaces/*:refs/namespaces/*"],
|
|
||||||
Some(&mut opts),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
stream.shutdown(net::Shutdown::Both).unwrap();
|
|
||||||
|
|
||||||
t.join().unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
updates,
|
|
||||||
vec![
|
|
||||||
format!("refs/namespaces/{remote}/refs/heads/master"),
|
|
||||||
format!("refs/namespaces/{remote}/refs/rad/id"),
|
|
||||||
format!("refs/namespaces/{remote}/refs/rad/sigrefs")
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_sign_refs() {
|
fn test_sign_refs() {
|
||||||
let tmp = tempfile::tempdir().unwrap();
|
let tmp = tempfile::tempdir().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,9 @@ impl From<Id> for Url {
|
||||||
impl fmt::Display for Url {
|
impl fmt::Display for Url {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(ns) = self.namespace {
|
if let Some(ns) = self.namespace {
|
||||||
write!(f, "{}://{}/{}", Self::SCHEME, self.repo, ns)
|
write!(f, "{}://{}/{}", Self::SCHEME, self.repo.canonical(), ns)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{}://{}", Self::SCHEME, self.repo)
|
write!(f, "{}://{}", Self::SCHEME, self.repo.canonical())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -88,7 +88,7 @@ impl FromStr for Url {
|
||||||
_ => return Err(UrlError::InvalidFormat),
|
_ => return Err(UrlError::InvalidFormat),
|
||||||
};
|
};
|
||||||
|
|
||||||
let resource = Id::from_str(resource).map_err(UrlError::InvalidRepository)?;
|
let resource = Id::from_canonical(resource).map_err(UrlError::InvalidRepository)?;
|
||||||
let namespace = namespace
|
let namespace = namespace
|
||||||
.map(|pk| Namespace::from_str(pk).map_err(UrlError::InvalidNamespace))
|
.map(|pk| Namespace::from_str(pk).map_err(UrlError::InvalidNamespace))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
@ -106,33 +106,37 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_parse() {
|
fn test_url_parse() {
|
||||||
let repo = Id::from_str("z2w8RArM3gaBXZxXhQUswE3hhLcss").unwrap();
|
let repo = Id::from_canonical("z2w8RArM3gaBXZxXhQUswE3hhLcss").unwrap();
|
||||||
let namespace =
|
let namespace =
|
||||||
Namespace::from_str("z6Mkifeb5NPS6j7JP72kEQEeuqMTpCAVcHsJi1C86jGTzHRi").unwrap();
|
Namespace::from_str("z6Mkifeb5NPS6j7JP72kEQEeuqMTpCAVcHsJi1C86jGTzHRi").unwrap();
|
||||||
|
|
||||||
let url = format!("rad://{repo}");
|
let url = format!("rad://{}", repo.canonical());
|
||||||
let url = Url::from_str(&url).unwrap();
|
let url = Url::from_str(&url).unwrap();
|
||||||
|
|
||||||
assert_eq!(url.repo, repo);
|
assert_eq!(url.repo, repo);
|
||||||
assert_eq!(url.namespace, None);
|
assert_eq!(url.namespace, None);
|
||||||
|
|
||||||
let url = format!("rad://{repo}/{namespace}");
|
let url = format!("rad://{}/{namespace}", repo.canonical());
|
||||||
let url = Url::from_str(&url).unwrap();
|
let url = Url::from_str(&url).unwrap();
|
||||||
|
|
||||||
assert_eq!(url.repo, repo);
|
assert_eq!(url.repo, repo);
|
||||||
assert_eq!(url.namespace, Some(namespace));
|
assert_eq!(url.namespace, Some(namespace));
|
||||||
|
|
||||||
assert!(format!("heartwood://{repo}").parse::<Url>().is_err());
|
assert!(format!("heartwood://{}", repo.canonical())
|
||||||
assert!(format!("git://{repo}").parse::<Url>().is_err());
|
.parse::<Url>()
|
||||||
|
.is_err());
|
||||||
|
assert!(format!("git://{}", repo.canonical())
|
||||||
|
.parse::<Url>()
|
||||||
|
.is_err());
|
||||||
assert!(format!("rad://{namespace}").parse::<Url>().is_err());
|
assert!(format!("rad://{namespace}").parse::<Url>().is_err());
|
||||||
assert!(format!("rad://{repo}/{namespace}/fnord")
|
assert!(format!("rad://{}/{namespace}/fnord", repo.canonical())
|
||||||
.parse::<Url>()
|
.parse::<Url>()
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_to_string() {
|
fn test_url_to_string() {
|
||||||
let repo = Id::from_str("z2w8RArM3gaBXZxXhQUswE3hhLcss").unwrap();
|
let repo = Id::from_canonical("z2w8RArM3gaBXZxXhQUswE3hhLcss").unwrap();
|
||||||
let namespace =
|
let namespace =
|
||||||
Namespace::from_str("z6Mkifeb5NPS6j7JP72kEQEeuqMTpCAVcHsJi1C86jGTzHRi").unwrap();
|
Namespace::from_str("z6Mkifeb5NPS6j7JP72kEQEeuqMTpCAVcHsJi1C86jGTzHRi").unwrap();
|
||||||
|
|
||||||
|
|
@ -140,12 +144,15 @@ mod test {
|
||||||
repo,
|
repo,
|
||||||
namespace: None,
|
namespace: None,
|
||||||
};
|
};
|
||||||
assert_eq!(url.to_string(), format!("rad://{repo}"));
|
assert_eq!(url.to_string(), format!("rad://{}", repo.canonical()));
|
||||||
|
|
||||||
let url = Url {
|
let url = Url {
|
||||||
repo,
|
repo,
|
||||||
namespace: Some(namespace),
|
namespace: Some(namespace),
|
||||||
};
|
};
|
||||||
assert_eq!(url.to_string(), format!("rad://{repo}/{namespace}"));
|
assert_eq!(
|
||||||
|
url.to_string(),
|
||||||
|
format!("rad://{}/{namespace}", repo.canonical())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ impl git2::transport::SmartSubtransport for MockTransport {
|
||||||
url.node
|
url.node
|
||||||
)));
|
)));
|
||||||
};
|
};
|
||||||
let git_dir = storage.join(url.repo.to_human());
|
let git_dir = storage.join(url.repo.canonical());
|
||||||
let mut cmd = process::Command::new("git");
|
let mut cmd = process::Command::new("git");
|
||||||
let mut child = cmd
|
let mut child = cmd
|
||||||
.arg("upload-pack")
|
.arg("upload-pack")
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,22 @@ impl Url {
|
||||||
impl fmt::Display for Url {
|
impl fmt::Display for Url {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
if let Some(ns) = self.namespace {
|
if let Some(ns) = self.namespace {
|
||||||
write!(f, "{}://{}/{}/{}", Self::SCHEME, self.node, self.repo, ns)
|
write!(
|
||||||
|
f,
|
||||||
|
"{}://{}/{}/{}",
|
||||||
|
Self::SCHEME,
|
||||||
|
self.node,
|
||||||
|
self.repo.canonical(),
|
||||||
|
ns
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
write!(f, "{}://{}/{}", Self::SCHEME, self.node, self.repo)
|
write!(
|
||||||
|
f,
|
||||||
|
"{}://{}/{}",
|
||||||
|
Self::SCHEME,
|
||||||
|
self.node,
|
||||||
|
self.repo.canonical()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -76,7 +89,7 @@ impl FromStr for Url {
|
||||||
};
|
};
|
||||||
|
|
||||||
let node = NodeId::from_str(node).map_err(UrlError::InvalidNode)?;
|
let node = NodeId::from_str(node).map_err(UrlError::InvalidNode)?;
|
||||||
let resource = Id::from_str(resource).map_err(UrlError::InvalidRepository)?;
|
let resource = Id::from_canonical(resource).map_err(UrlError::InvalidRepository)?;
|
||||||
let namespace = namespace
|
let namespace = namespace
|
||||||
.map(|pk| Namespace::from_str(pk).map_err(UrlError::InvalidNamespace))
|
.map(|pk| Namespace::from_str(pk).map_err(UrlError::InvalidNamespace))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
@ -96,18 +109,18 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_url_parse() {
|
fn test_url_parse() {
|
||||||
let node = NodeId::from_str("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK").unwrap();
|
let node = NodeId::from_str("z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK").unwrap();
|
||||||
let repo = Id::from_str("z2w8RArM3gaBXZxXhQUswE3hhLcss").unwrap();
|
let repo = Id::from_canonical("z2w8RArM3gaBXZxXhQUswE3hhLcss").unwrap();
|
||||||
let namespace =
|
let namespace =
|
||||||
Namespace::from_str("z6Mkifeb5NPS6j7JP72kEQEeuqMTpCAVcHsJi1C86jGTzHRi").unwrap();
|
Namespace::from_str("z6Mkifeb5NPS6j7JP72kEQEeuqMTpCAVcHsJi1C86jGTzHRi").unwrap();
|
||||||
|
|
||||||
let url = format!("heartwood://{node}/{repo}");
|
let url = format!("heartwood://{node}/{}", repo.canonical());
|
||||||
let url = Url::from_str(&url).unwrap();
|
let url = Url::from_str(&url).unwrap();
|
||||||
|
|
||||||
assert_eq!(url.node, node);
|
assert_eq!(url.node, node);
|
||||||
assert_eq!(url.repo, repo);
|
assert_eq!(url.repo, repo);
|
||||||
assert_eq!(url.namespace, None);
|
assert_eq!(url.namespace, None);
|
||||||
|
|
||||||
let url = format!("heartwood://{node}/{repo}/{namespace}");
|
let url = format!("heartwood://{node}/{}/{namespace}", repo.canonical());
|
||||||
let url = Url::from_str(&url).unwrap();
|
let url = Url::from_str(&url).unwrap();
|
||||||
|
|
||||||
assert_eq!(url.node, node);
|
assert_eq!(url.node, node);
|
||||||
|
|
@ -119,8 +132,10 @@ mod test {
|
||||||
assert!(format!("heartwood://{node}/{namespace}")
|
assert!(format!("heartwood://{node}/{namespace}")
|
||||||
.parse::<Url>()
|
.parse::<Url>()
|
||||||
.is_err());
|
.is_err());
|
||||||
assert!(format!("heartwood://{node}/{repo}/{namespace}/fnord")
|
assert!(
|
||||||
.parse::<Url>()
|
format!("heartwood://{node}/{}/{namespace}/fnord", repo.canonical())
|
||||||
.is_err());
|
.parse::<Url>()
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue