129 lines
3.5 KiB
Rust
129 lines
3.5 KiB
Rust
use std::str::FromStr;
|
|
|
|
use thiserror::Error;
|
|
|
|
use crate::prelude::RepoId;
|
|
use crate::{cob, git};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum ExplorerError {
|
|
#[error("invalid explorer URL {0:?}: unknown protocol")]
|
|
UnknownProtocol(String),
|
|
#[error("invalid explorer URL {0:?}: missing `$host` component")]
|
|
MissingHost(String),
|
|
#[error("invalid explorer URL {0:?}: missing `$rid` component")]
|
|
MissingRid(String),
|
|
#[error("invalid explorer URL {0:?}: missing `$path` component")]
|
|
MissingPath(String),
|
|
}
|
|
|
|
/// A resource such as a branch, patch or commit.
|
|
#[derive(Debug, Hash, PartialEq, Eq)]
|
|
pub enum ExplorerResource {
|
|
/// Git tree object. Used for the repository root.
|
|
Tree { oid: git::Oid },
|
|
/// A Patch COB.
|
|
Patch { id: cob::ObjectId },
|
|
}
|
|
|
|
impl std::fmt::Display for ExplorerResource {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
match self {
|
|
Self::Tree { oid } => {
|
|
write!(f, "/tree/{oid}")
|
|
}
|
|
Self::Patch { id } => {
|
|
write!(f, "/patches/{id}")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A URL to a specific repository or resource within a repository.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct ExplorerUrl {
|
|
/// URL template.
|
|
pub template: Explorer,
|
|
/// Host serving the repository.
|
|
pub host: String,
|
|
/// Repository.
|
|
pub rid: RepoId,
|
|
/// Resource under the repository.
|
|
pub resource: Option<ExplorerResource>,
|
|
}
|
|
|
|
impl ExplorerUrl {
|
|
/// Set a resource on for this URL.
|
|
pub fn resource(mut self, resource: ExplorerResource) -> Self {
|
|
self.resource = Some(resource);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl std::fmt::Display for ExplorerUrl {
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
f.write_str(
|
|
self.template
|
|
.0
|
|
.replace("$host", &self.host)
|
|
.replace("$rid", self.rid.urn().as_str())
|
|
.replace(
|
|
"$path",
|
|
self.resource
|
|
.as_ref()
|
|
.map(|r| r.to_string())
|
|
.as_deref()
|
|
.unwrap_or(""),
|
|
)
|
|
.as_str(),
|
|
)
|
|
}
|
|
}
|
|
|
|
/// A public explorer, eg. `https://app.radicle.xyz`.
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
|
|
#[serde(transparent)]
|
|
pub struct Explorer(String);
|
|
|
|
impl Default for Explorer {
|
|
fn default() -> Self {
|
|
Self(String::from(
|
|
"https://app.radicle.xyz/nodes/$host/$rid$path",
|
|
))
|
|
}
|
|
}
|
|
|
|
impl Explorer {
|
|
/// Get the explorer URL, filling in the host and RID.
|
|
pub fn url(&self, host: impl ToString, rid: RepoId) -> ExplorerUrl {
|
|
ExplorerUrl {
|
|
template: self.clone(),
|
|
host: host.to_string(),
|
|
rid,
|
|
resource: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl FromStr for Explorer {
|
|
type Err = ExplorerError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
let url = s.to_owned();
|
|
|
|
if !url.starts_with("http://") && !url.starts_with("https://") {
|
|
return Err(ExplorerError::UnknownProtocol(url));
|
|
}
|
|
if !url.contains("$host") {
|
|
return Err(ExplorerError::MissingHost(url));
|
|
}
|
|
if !url.contains("$rid") {
|
|
return Err(ExplorerError::MissingRid(url));
|
|
}
|
|
if !url.contains("$path") {
|
|
return Err(ExplorerError::MissingPath(url));
|
|
}
|
|
Ok(Explorer(url))
|
|
}
|
|
}
|