Skip to main content

lux_lib/git/
url.rs

1use serde::{de, Deserialize, Deserializer, Serialize};
2use std::{fmt::Display, str::FromStr};
3use thiserror::Error;
4
5// NOTE: This module implements a basic `GitUrl` struct with only what we need.
6// This is so that we don't have to expose `git_url_parse::GitUrl`, which is  highly unstable,
7// via our API.
8
9/// GitUrl represents an input url that is a url used by git
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct RemoteGitUrl {
12    /// The fully qualified domain name (FQDN) or IP of the repo
13    pub(crate) host: String,
14    /// The name of the repo
15    pub(crate) repo: String,
16    /// The owner/account/project name
17    pub(crate) owner: String,
18    /// The raw URL string
19    url_str: String,
20}
21
22#[derive(Debug, Error)]
23pub enum RemoteGitUrlParseError {
24    #[error("error parsing git URL:\n{0}")]
25    GitUrlParse(String),
26    #[error("not a remote git URL: {0}")]
27    NotARemoteGitUrl(String),
28}
29
30impl FromStr for RemoteGitUrl {
31    type Err = RemoteGitUrlParseError;
32
33    fn from_str(s: &str) -> Result<Self, Self::Err> {
34        let url = git_url_parse::GitUrl::parse(s)
35            .map_err(|err| RemoteGitUrlParseError::GitUrlParse(err.to_string()))?;
36        let host = url
37            .host()
38            .ok_or_else(|| RemoteGitUrlParseError::NotARemoteGitUrl(url.to_string()))?;
39        let provider: Result<
40            git_url_parse::types::provider::GenericProvider,
41            git_url_parse::GitUrlParseError,
42        > = url.provider_info();
43        let provider =
44            provider.map_err(|_err| RemoteGitUrlParseError::NotARemoteGitUrl(s.to_string()))?;
45        Ok(RemoteGitUrl {
46            host: host.to_string(),
47            repo: provider.repo().to_string(),
48            owner: provider.owner().to_string(),
49            url_str: url.to_string(),
50        })
51    }
52}
53
54impl Display for RemoteGitUrl {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        self.url_str.fmt(f)
57    }
58}
59
60impl<'de> Deserialize<'de> for RemoteGitUrl {
61    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
62    where
63        D: Deserializer<'de>,
64    {
65        String::deserialize(deserializer)?
66            .parse()
67            .map_err(de::Error::custom)
68    }
69}
70
71impl Serialize for RemoteGitUrl {
72    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73    where
74        S: serde::Serializer,
75    {
76        self.to_string().serialize(serializer)
77    }
78}