|
| 1 | +use super::GitProvider; |
| 2 | +use crate::types::GitUrlParseHint; |
| 3 | +use crate::{GitUrl, GitUrlParseError}; |
| 4 | + |
| 5 | +use getset::Getters; |
| 6 | +use nom::Parser; |
| 7 | +use nom::bytes::complete::{is_not, tag, take_until}; |
| 8 | +use nom::combinator::opt; |
| 9 | +use nom::sequence::{preceded, separated_pair, terminated}; |
| 10 | +#[cfg(feature = "serde")] |
| 11 | +use serde::{Deserialize, Serialize}; |
| 12 | +#[cfg(feature = "url")] |
| 13 | +use url::Url; |
| 14 | + |
| 15 | +/// Azure DevOps repository provider |
| 16 | +/// ## Supported URL Formats |
| 17 | +/// |
| 18 | +/// - `https://dev.azure.com/org/project/_git/repo` |
| 19 | +/// - `[email protected]:v3/org/project/repo` |
| 20 | +/// |
| 21 | +/// Example: |
| 22 | +/// |
| 23 | +/// ``` |
| 24 | +/// use git_url_parse::{GitUrl, GitUrlParseError}; |
| 25 | +/// use git_url_parse::types::provider::AzureDevOpsProvider; |
| 26 | +/// |
| 27 | +/// let test_url = "https://[email protected]/CompanyName/ProjectName/_git/RepoName"; |
| 28 | +/// let parsed = GitUrl::parse(test_url).expect("URL parse failed"); |
| 29 | +/// |
| 30 | +/// let provider_info: AzureDevOpsProvider = parsed.provider_info().unwrap(); |
| 31 | +/// |
| 32 | +/// assert_eq!(provider_info.org(), "CompanyName"); |
| 33 | +/// assert_eq!(provider_info.project(), "ProjectName"); |
| 34 | +/// assert_eq!(provider_info.repo(), "RepoName"); |
| 35 | +/// assert_eq!(provider_info.fullname(), "CompanyName/ProjectName/RepoName"); |
| 36 | +/// ``` |
| 37 | +/// |
| 38 | +#[derive(Debug, PartialEq, Eq, Clone, Getters)] |
| 39 | +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] |
| 40 | +#[getset(get = "pub")] |
| 41 | +pub struct AzureDevOpsProvider { |
| 42 | + /// Azure Devops organization name |
| 43 | + org: String, |
| 44 | + /// Azure Devops project name |
| 45 | + project: String, |
| 46 | + /// Azure Devops repo name |
| 47 | + repo: String, |
| 48 | +} |
| 49 | + |
| 50 | +impl AzureDevOpsProvider { |
| 51 | + /// Helper method to get the full name of a repo: `{org}/{project}/{repo}` |
| 52 | + pub fn fullname(&self) -> String { |
| 53 | + format!("{}/{}/{}", self.org, self.project, self.repo) |
| 54 | + } |
| 55 | + |
| 56 | + /// Parse the path of a http url for Azure Devops patterns |
| 57 | + fn parse_http_path(input: &str) -> Result<(&str, AzureDevOpsProvider), GitUrlParseError> { |
| 58 | + // Handle optional leading / |
| 59 | + let (input, _) = opt(tag("/")).parse(input)?; |
| 60 | + |
| 61 | + // Parse org/project/repo |
| 62 | + let (input, (org, (project, repo))) = separated_pair( |
| 63 | + is_not("/"), |
| 64 | + tag("/"), |
| 65 | + separated_pair( |
| 66 | + is_not("/"), |
| 67 | + tag("/"), |
| 68 | + preceded(opt(tag("_git/")), is_not("")), |
| 69 | + ), |
| 70 | + ) |
| 71 | + .parse(input)?; |
| 72 | + |
| 73 | + Ok(( |
| 74 | + input, |
| 75 | + AzureDevOpsProvider { |
| 76 | + org: org.to_string(), |
| 77 | + project: project.to_string(), |
| 78 | + repo: repo.to_string(), |
| 79 | + }, |
| 80 | + )) |
| 81 | + } |
| 82 | + |
| 83 | + /// Parse the path of an ssh url for Azure Devops patterns |
| 84 | + fn parse_ssh_path(input: &str) -> Result<(&str, AzureDevOpsProvider), GitUrlParseError> { |
| 85 | + // Handle optional leading v3/ or other prefix |
| 86 | + let (input, _) = opt(take_until("/")).parse(input)?; |
| 87 | + let (input, _) = opt(tag("/")).parse(input)?; |
| 88 | + |
| 89 | + // Parse org/project/repo |
| 90 | + let (input, (org, (project, repo))) = separated_pair( |
| 91 | + is_not("/"), |
| 92 | + tag("/"), |
| 93 | + separated_pair( |
| 94 | + is_not("/"), |
| 95 | + tag("/"), |
| 96 | + terminated(is_not("."), opt(tag(".git"))), |
| 97 | + ), |
| 98 | + ) |
| 99 | + .parse(input)?; |
| 100 | + |
| 101 | + Ok(( |
| 102 | + input, |
| 103 | + AzureDevOpsProvider { |
| 104 | + org: org.to_string(), |
| 105 | + project: project.to_string(), |
| 106 | + repo: repo.to_string(), |
| 107 | + }, |
| 108 | + )) |
| 109 | + } |
| 110 | +} |
| 111 | + |
| 112 | +impl GitProvider<GitUrl, GitUrlParseError> for AzureDevOpsProvider { |
| 113 | + fn from_git_url(url: &GitUrl) -> Result<Self, GitUrlParseError> { |
| 114 | + let path = url.path(); |
| 115 | + |
| 116 | + let parsed = if url.hint() == GitUrlParseHint::Httplike { |
| 117 | + Self::parse_http_path(path) |
| 118 | + } else { |
| 119 | + Self::parse_ssh_path(path) |
| 120 | + }; |
| 121 | + |
| 122 | + parsed.map(|(_, provider)| provider) |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +#[cfg(feature = "url")] |
| 127 | +impl GitProvider<Url, GitUrlParseError> for AzureDevOpsProvider { |
| 128 | + fn from_git_url(url: &Url) -> Result<Self, GitUrlParseError> { |
| 129 | + let path = url.path(); |
| 130 | + |
| 131 | + let parsed = if url.scheme().contains("http") { |
| 132 | + Self::parse_http_path(path) |
| 133 | + } else { |
| 134 | + Self::parse_ssh_path(path) |
| 135 | + }; |
| 136 | + |
| 137 | + parsed.map(|(_, provider)| provider) |
| 138 | + } |
| 139 | +} |
0 commit comments