diff --git a/README.md b/README.md index f653d62..e332ba4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ We currently support projects and crates hosted on crates.io, Github, Gitlab, Bi To analyze the state of your dependencies you can use the following URLs: - for projects on crates.io: `https://deps.rs/crate/` -- for projects on Github, Gitlab, Bitbucket, SourceHut, or Codeberg: `https://deps.rs/repo///` (where `` is either `github`, `gitlab`, `bitbucket`, `sourcehut`, or `codeberg`) +- for projects on Github, Gitlab, Bitbucket, SourceHut, Codeberg, or Gitea: `https://deps.rs/repo///` (where `` is either `github`, `gitlab`, `bitbucket`, `sourcehut`, `codeberg`, or `gitea/`) ## Badges diff --git a/src/models/repo.rs b/src/models/repo.rs index 7b867cb..5c22e1a 100644 --- a/src/models/repo.rs +++ b/src/models/repo.rs @@ -42,49 +42,52 @@ impl fmt::Display for RepoPath { write!( f, "{} => {}/{}", - self.site.as_ref(), + self.site.to_string(), self.qual.as_ref(), self.name.as_ref() ) } } -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +#[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum RepoSite { Github, Gitlab, Bitbucket, Sourcehut, Codeberg, + Gitea(GiteaDomain), } impl RepoSite { - pub fn to_base_uri(self) -> &'static str { + pub fn to_base_uri(&self) -> &str { match self { RepoSite::Github => "https://github.com", RepoSite::Gitlab => "https://gitlab.com", RepoSite::Bitbucket => "https://bitbucket.org", RepoSite::Sourcehut => "https://git.sr.ht", RepoSite::Codeberg => "https://codeberg.org", + RepoSite::Gitea(domain) => domain.as_ref(), } } - pub fn to_usercontent_base_uri(self) -> &'static str { + pub fn to_usercontent_base_uri(&self) -> &str { match self { RepoSite::Github => "https://raw.githubusercontent.com", RepoSite::Gitlab => "https://gitlab.com", RepoSite::Bitbucket => "https://bitbucket.org", RepoSite::Sourcehut => "https://git.sr.ht", RepoSite::Codeberg => "https://codeberg.org", + RepoSite::Gitea(domain) => domain.as_ref(), } } - pub fn to_usercontent_repo_suffix(self) -> &'static str { + pub fn to_usercontent_repo_suffix(&self) -> &'static str { match self { RepoSite::Github => "HEAD", RepoSite::Gitlab | RepoSite::Bitbucket => "raw/HEAD", RepoSite::Sourcehut => "blob/HEAD", - RepoSite::Codeberg => "raw", + RepoSite::Codeberg | RepoSite::Gitea(_) => "raw", } } } @@ -93,25 +96,64 @@ impl FromStr for RepoSite { type Err = Error; fn from_str(input: &str) -> Result { - match input { - "github" => Ok(RepoSite::Github), - "gitlab" => Ok(RepoSite::Gitlab), - "bitbucket" => Ok(RepoSite::Bitbucket), - "sourcehut" => Ok(RepoSite::Sourcehut), - "codeberg" => Ok(RepoSite::Codeberg), - _ => Err(anyhow!("unknown repo site identifier")), + if let Some((site, domain)) = input.split_once("/") { + match site { + "gitea" => Ok(RepoSite::Gitea(domain.parse()?)), + _ => Err(anyhow!("unknown repo site identifier")), + } + } else { + match input { + "github" => Ok(RepoSite::Github), + "gitlab" => Ok(RepoSite::Gitlab), + "bitbucket" => Ok(RepoSite::Bitbucket), + "sourcehut" => Ok(RepoSite::Sourcehut), + "codeberg" => Ok(RepoSite::Codeberg), + _ => Err(anyhow!("unknown repo site identifier")), + } } } } -impl AsRef for RepoSite { - fn as_ref(&self) -> &str { +impl fmt::Display for RepoSite { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - RepoSite::Github => "github", - RepoSite::Gitlab => "gitlab", - RepoSite::Bitbucket => "bitbucket", - RepoSite::Sourcehut => "sourcehut", - RepoSite::Codeberg => "codeberg", + RepoSite::Github => write!(f, "github"), + RepoSite::Gitlab => write!(f, "gitlab"), + RepoSite::Bitbucket => write!(f, "bitbucket"), + RepoSite::Sourcehut => write!(f, "sourcehut"), + RepoSite::Codeberg => write!(f, "codeberg"), + RepoSite::Gitea(s) => write!(f, "gitea/{s}"), + } + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct GiteaDomain(String); + +impl FromStr for GiteaDomain { + type Err = Error; + + fn from_str(input: &str) -> Result { + if input.starts_with("https://") || input.starts_with("http://") { + Ok(GiteaDomain(input.to_string())) + } else { + Ok(GiteaDomain(format!("https://{input}"))) + } + } +} + +impl AsRef for GiteaDomain { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl fmt::Display for GiteaDomain { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.0.starts_with("https://") { + f.write_str(&self.0["https://".len()..]) + } else { + self.0.fmt(f) } } } @@ -215,5 +257,21 @@ mod tests { let exp = format!("https://codeberg.org/deps-rs/deps.rs/raw/{}", expected); assert_eq!(out.to_string(), exp); } + + for (input, expected) in &paths { + let repo = RepoPath::from_parts("gitea/gitea.com", "deps-rs", "deps.rs").unwrap(); + let out = repo.to_usercontent_file_url(RelativePath::new(input)); + + let exp = format!("https://gitea.com/deps-rs/deps.rs/raw/{}", expected); + assert_eq!(out.to_string(), exp); + } + + for (input, expected) in &paths { + let repo = RepoPath::from_parts("gitea/example.com/git", "deps-rs", "deps.rs").unwrap(); + let out = repo.to_usercontent_file_url(RelativePath::new(input)); + + let exp = format!("https://example.com/git/deps-rs/deps.rs/raw/{}", expected); + assert_eq!(out.to_string(), exp); + } } } diff --git a/src/server/mod.rs b/src/server/mod.rs index 7bbfca0..8134cc1 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -58,11 +58,11 @@ impl App { router.add("/static/logo.svg", Route::Static(StaticFile::FaviconPng)); router.add( - "/repo/:site/:qual/:name", + "/repo/*site/:qual/:name", Route::RepoStatus(StatusFormat::Html), ); router.add( - "/repo/:site/:qual/:name/status.svg", + "/repo/*site/:qual/:name/status.svg", Route::RepoStatus(StatusFormat::Svg), ); diff --git a/src/server/views/html/index.rs b/src/server/views/html/index.rs index 43211e8..e28c3e4 100644 --- a/src/server/views/html/index.rs +++ b/src/server/views/html/index.rs @@ -21,12 +21,12 @@ fn popular_table(popular_repos: Vec, popular_crates: Vec) @for repo in popular_repos.into_iter().take(10) { tr { td { - a href=(format!("{}/repo/{}/{}/{}", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref())) { + a href=(format!("{}/repo/{}/{}/{}", &super::SELF_BASE_URL as &str, repo.path.site, repo.path.qual.as_ref(), repo.path.name.as_ref())) { (format!("{} / {}", repo.path.qual.as_ref(), repo.path.name.as_ref())) } } td class="has-text-right" { - img src=(format!("{}/repo/{}/{}/{}/status.svg", &super::SELF_BASE_URL as &str, repo.path.site.as_ref(), repo.path.qual.as_ref(), repo.path.name.as_ref())); + img src=(format!("{}/repo/{}/{}/{}/status.svg", &super::SELF_BASE_URL as &str, repo.path.site, repo.path.qual.as_ref(), repo.path.name.as_ref())); } } } diff --git a/src/server/views/html/status.rs b/src/server/views/html/status.rs index 4596794..9df8225 100644 --- a/src/server/views/html/status.rs +++ b/src/server/views/html/status.rs @@ -127,9 +127,11 @@ fn get_site_icon(site: &RepoSite) -> (FaType, &'static str) { RepoSite::Github => (FaType::Brands, "github"), RepoSite::Gitlab => (FaType::Brands, "gitlab"), RepoSite::Bitbucket => (FaType::Brands, "bitbucket"), - // FIXME: There is no brands/{sourcehut, codeberg} icon, so just use a + // FIXME: There is no brands/{sourcehut, codeberg, gitea} icon, so just use a // regular circle which looks close enough. - RepoSite::Sourcehut | RepoSite::Codeberg => (FaType::Regular, "circle"), + RepoSite::Sourcehut | RepoSite::Codeberg | RepoSite::Gitea(_) => { + (FaType::Regular, "circle") + } } } @@ -334,7 +336,7 @@ fn render_success( let self_path = match subject_path { SubjectPath::Repo(ref repo_path) => format!( "repo/{}/{}/{}", - repo_path.site.as_ref(), + repo_path.site, repo_path.qual.as_ref(), repo_path.name.as_ref() ),