//! Information about version control systems.
use debian_control::vcs::ParsedVcs;
use log::debug;
use url::Url;

/// List of known GitLab sites.
pub const KNOWN_GITLAB_SITES: &[&str] = &["salsa.debian.org", "invent.kde.org", "0xacab.org"];

/// Check if a particular host is a GitLab instance.
pub fn is_gitlab_site(hostname: &str, net_access: Option<bool>) -> bool {
    if KNOWN_GITLAB_SITES.contains(&hostname) {
        return true;
    }

    if hostname.starts_with("gitlab.") {
        return true;
    }

    if net_access.unwrap_or(false) {
        probe_gitlab_host(hostname)
    } else {
        false
    }
}

/// Check if a particular host is a GitLab instance.
pub fn probe_gitlab_host(hostname: &str) -> bool {
    use reqwest::header::HeaderMap;
    let url = format!("https://{}/api/v4/version", hostname);

    let mut headers = HeaderMap::new();
    headers.insert(reqwest::header::ACCEPT, "application/json".parse().unwrap());

    let client = reqwest::blocking::Client::builder()
        .default_headers(headers)
        .build()
        .unwrap();

    let http_url: reqwest::Url = Into::<String>::into(url.clone()).parse().unwrap();

    let request = client.get(http_url).build().unwrap();

    let response = client.execute(request).unwrap();

    match response.status().as_u16() {
        401 => {
            if let Ok(data) = response.json::<serde_json::Value>() {
                if let Some(message) = data["message"].as_str() {
                    if message == "401 Unauthorized" {
                        true
                    } else {
                        debug!("failed to parse JSON response: {:?}", data);
                        false
                    }
                } else {
                    debug!("failed to parse JSON response: {:?}", data);
                    false
                }
            } else {
                debug!("failed to parse JSON response");
                false
            }
        }
        200 => true,
        _ => {
            debug!("unexpected HTTP status code: {:?}", response.status());
            false
        }
    }
}

/// Determine the URL of the browser for a GitLab repository.
pub fn determine_gitlab_browser_url(url: &str) -> Url {
    let parsed_vcs: ParsedVcs = url.trim_end_matches('/').parse().unwrap();

    // TODO(jelmer): Add support for branches
    let parsed_url = Url::parse(&parsed_vcs.repo_url).unwrap();

    let path = parsed_url
        .path()
        .trim_end_matches('/')
        .trim_end_matches(".git");

    let branch = if let Some(branch) = parsed_vcs.branch {
        Some(branch)
    } else if parsed_vcs.subpath.is_some() {
        Some("HEAD".to_string())
    } else {
        None
    };

    let mut path = if let Some(branch) = branch {
        format!("{}/-/tree/{}", path, branch)
    } else {
        path.to_string()
    };

    if let Some(subpath) = parsed_vcs.subpath {
        path.push_str(&format!("/{}", subpath));
    }

    let url = format!(
        "https://{}/{}",
        parsed_url.host_str().unwrap(),
        path.trim_start_matches('/')
    );

    Url::parse(&url).unwrap()
}

/// Determine the URL of the browser for a VCS repository.
pub fn determine_browser_url(
    _vcs_type: &str,
    vcs_url: &str,
    net_access: Option<bool>,
) -> Option<Url> {
    let parsed_vcs: ParsedVcs = vcs_url.parse().unwrap();

    let parsed_url: Url = parsed_vcs.repo_url.parse().unwrap();

    match parsed_url.host_str().unwrap() {
        host if is_gitlab_site(host, net_access) => Some(determine_gitlab_browser_url(vcs_url)),

        "github.com" => {
            let path = parsed_url.path().trim_end_matches(".git");

            let branch = if let Some(branch) = parsed_vcs.branch {
                Some(branch)
            } else if parsed_vcs.subpath.is_some() {
                Some("HEAD".to_string())
            } else {
                None
            };

            let mut path = if let Some(branch) = branch {
                format!("{}/tree/{}", path, branch)
            } else {
                path.to_string()
            };

            if let Some(subpath) = parsed_vcs.subpath {
                path.push_str(&format!("/{}", subpath));
            }

            let url = format!(
                "https://{}/{}",
                parsed_url.host_str().unwrap(),
                path.trim_start_matches('/')
            );

            Some(Url::parse(&url).unwrap())
        }
        host if (host == "code.launchpad.net" || host == "launchpad.net")
            && parsed_vcs.branch.is_none()
            && parsed_vcs.subpath.is_none() =>
        {
            let url = format!(
                "https://code.launchpad.net/{}",
                parsed_url.path().trim_start_matches('/')
            );

            Some(Url::parse(&url).unwrap())
        }
        "git.savannah.gnu.org" | "git.sv.gnu.org" => {
            let mut path_elements = parsed_url.path_segments().unwrap().collect::<Vec<_>>();
            if parsed_url.scheme() == "https" && path_elements.first() == Some(&"git") {
                path_elements.remove(0);
            }
            // Why cgit and not gitweb?
            path_elements.insert(0, "cgit");
            Some(
                Url::parse(&format!(
                    "https://{}/{}",
                    parsed_url.host_str().unwrap(),
                    path_elements.join("/")
                ))
                .unwrap(),
            )
        }
        "git.code.sf.net" | "git.code.sourceforge.net" => {
            let path_elements = parsed_url.path_segments().unwrap().collect::<Vec<_>>();
            if path_elements.first() != Some(&"p") {
                return None;
            }
            let project = path_elements[1];
            let repository = path_elements[2];
            let mut path_elements = vec!["p", project, repository];
            let branch = if let Some(branch) = parsed_vcs.branch {
                Some(branch)
            } else if parsed_vcs.subpath.is_some() {
                Some("HEAD".to_string())
            } else {
                None
            };

            if let Some(branch) = branch.as_deref() {
                path_elements.extend(["ci", branch, "tree"]);
            }

            if let Some(subpath) = parsed_vcs.subpath.as_ref() {
                path_elements.push(subpath);
            }

            let url = format!("https://sourceforge.net/{}", path_elements.join("/"));
            Some(Url::parse(&url).unwrap())
        }
        _ => None,
    }
}

/// Canonicalize a VCS browser URL.
pub fn canonicalize_vcs_browser_url(url: &str) -> String {
    let url = url.replace(
        "https://svn.debian.org/wsvn/",
        "https://anonscm.debian.org/viewvc/",
    );
    let url = url.replace(
        "http://svn.debian.org/wsvn/",
        "https://anonscm.debian.org/viewvc/",
    );
    let url = url.replace(
        "https://git.debian.org/?p=",
        "https://anonscm.debian.org/git/",
    );
    let url = url.replace(
        "http://git.debian.org/?p=",
        "https://anonscm.debian.org/git/",
    );
    let url = url.replace(
        "https://bzr.debian.org/loggerhead/",
        "https://anonscm.debian.org/loggerhead/",
    );
    let url = url.replace(
        "http://bzr.debian.org/loggerhead/",
        "https://anonscm.debian.org/loggerhead/",
    );

    lazy_regex::regex_replace!(
        r"^https?://salsa.debian.org/([^/]+/[^/]+)\.git/?$",
        &url,
        |_, x| "https://salsa.debian.org/".to_string() + x
    )
    .into_owned()
}

/// VCS information for a package.
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PackageVcs {
    /// Git repository.
    Git {
        /// URL of the repository.
        url: Url,

        /// Branch name.
        branch: Option<String>,

        /// Subpath within the repository.
        subpath: Option<std::path::PathBuf>,
    },
    /// Subversion repository.
    Svn(Url),

    /// Bazaar repository.
    Bzr(Url),

    /// Mercurial repository.
    Hg {
        /// URL of the repository.
        url: Url,

        /// Branch name.
        branch: Option<String>,

        /// Subpath within the repository.
        subpath: Option<std::path::PathBuf>,
    },

    /// Monotone repository.
    Mtn(Url),

    /// CVS repository.
    Cvs(String),

    /// Darcs repository.
    Darcs(Url),

    /// Arch repository.
    Arch(Url),

    /// Svk repository.
    Svk(Url),
}

impl PackageVcs {
    /// Get the type of the VCS repository as a string.
    pub fn type_str(&self) -> &str {
        match self {
            PackageVcs::Git { .. } => "Git",
            PackageVcs::Svn(_) => "Svn",
            PackageVcs::Bzr(_) => "Bzr",
            PackageVcs::Hg { .. } => "Hg",
            PackageVcs::Mtn(_) => "Mtn",
            PackageVcs::Cvs(_) => "Cvs",
            PackageVcs::Darcs(_) => "Darcs",
            PackageVcs::Arch(_) => "Arch",
            PackageVcs::Svk(_) => "Svk",
        }
    }

    /// Get the URL of the VCS repository.
    pub fn url(&self) -> Option<&url::Url> {
        match self {
            PackageVcs::Git { url, .. } => Some(url),
            PackageVcs::Svn(url) => Some(url),
            PackageVcs::Bzr(url) => Some(url),
            PackageVcs::Hg { url, .. } => Some(url),
            PackageVcs::Mtn(url) => Some(url),
            PackageVcs::Darcs(url) => Some(url),
            PackageVcs::Arch(url) => Some(url),
            PackageVcs::Svk(url) => Some(url),
            PackageVcs::Cvs(_) => None,
        }
    }

    /// Get the branch name of the VCS repository.
    pub fn branch(&self) -> Option<&str> {
        match self {
            PackageVcs::Git { branch, .. } => branch.as_deref(),
            PackageVcs::Hg { branch, .. } => branch.as_deref(),
            _ => None,
        }
    }

    /// Get the subpath of the VCS repository.
    pub fn subpath(&self) -> Option<&std::path::Path> {
        match self {
            PackageVcs::Git { subpath, .. } => subpath.as_deref(),
            PackageVcs::Hg { subpath, .. } => subpath.as_deref(),
            _ => None,
        }
    }

    /// Get the location of the VCS repository.
    pub fn location(&self) -> String {
        match self {
            PackageVcs::Git {
                url,
                branch,
                subpath,
            } => {
                let mut result = url.to_string();
                if let Some(branch) = branch {
                    result.push_str(&format!(" -b {}", branch));
                }
                if let Some(subpath) = subpath {
                    result.push_str(&format!(" [{}]", subpath.display()));
                }
                result
            }
            PackageVcs::Svn(url) => url.to_string(),
            PackageVcs::Bzr(url) => url.to_string(),
            PackageVcs::Hg {
                url,
                branch,
                subpath,
            } => {
                let mut result = url.to_string();
                if let Some(branch) = branch {
                    result.push_str(&format!(" -b {}", branch));
                }
                if let Some(subpath) = subpath {
                    result.push_str(&format!(" [{}]", subpath.display()));
                }
                result
            }
            PackageVcs::Mtn(url) => url.to_string(),
            PackageVcs::Cvs(s) => s.clone(),
            PackageVcs::Darcs(url) => url.to_string(),
            PackageVcs::Arch(url) => url.to_string(),
            PackageVcs::Svk(url) => url.to_string(),
        }
    }
}

impl From<PackageVcs> for ParsedVcs {
    fn from(vcs: PackageVcs) -> Self {
        match vcs {
            PackageVcs::Git {
                url,
                branch,
                subpath,
            } => ParsedVcs {
                repo_url: url.to_string(),
                branch,
                subpath: subpath.map(|x| x.to_string_lossy().to_string()),
            },
            PackageVcs::Svn(url) => ParsedVcs {
                repo_url: url.to_string(),
                branch: None,
                subpath: None,
            },
            PackageVcs::Bzr(url) => ParsedVcs {
                repo_url: url.to_string(),
                branch: None,
                subpath: None,
            },
            PackageVcs::Hg {
                url,
                branch,
                subpath,
            } => ParsedVcs {
                repo_url: url.to_string(),
                branch,
                subpath: subpath.map(|x| x.to_string_lossy().to_string()),
            },
            PackageVcs::Mtn(url) => ParsedVcs {
                repo_url: url.to_string(),
                branch: None,
                subpath: None,
            },
            PackageVcs::Cvs(s) => ParsedVcs {
                repo_url: s,
                branch: None,
                subpath: None,
            },
            PackageVcs::Darcs(url) => ParsedVcs {
                repo_url: url.to_string(),
                branch: None,
                subpath: None,
            },
            PackageVcs::Arch(url) => ParsedVcs {
                repo_url: url.to_string(),
                branch: None,
                subpath: None,
            },
            PackageVcs::Svk(url) => ParsedVcs {
                repo_url: url.to_string(),
                branch: None,
                subpath: None,
            },
        }
    }
}

/// Trait for types that can provide VCS information.
pub trait VcsSource {
    /// Get the Vcs-Git field.
    fn vcs_git(&self) -> Option<String>;

    /// Get the Vcs-Svn field.
    fn vcs_svn(&self) -> Option<String>;

    /// Get the Vcs-Bzr field.
    fn vcs_bzr(&self) -> Option<String>;

    /// Get the Vcs-Hg field.
    fn vcs_hg(&self) -> Option<String>;

    /// Get the Vcs-Mtn field.
    fn vcs_mtn(&self) -> Option<String>;

    /// Get the Vcs-Cvs field.
    fn vcs_cvs(&self) -> Option<String>;

    /// Get the Vcs-Darcs field.
    fn vcs_darcs(&self) -> Option<String>;

    /// Get the Vcs-Arch field.
    fn vcs_arch(&self) -> Option<String>;

    /// Get the Vcs-Svk field.
    fn vcs_svk(&self) -> Option<String>;
}

impl VcsSource for debian_control::Source {
    fn vcs_git(&self) -> Option<String> {
        self.vcs_git()
    }

    fn vcs_svn(&self) -> Option<String> {
        self.vcs_svn()
    }

    fn vcs_bzr(&self) -> Option<String> {
        self.vcs_bzr()
    }

    fn vcs_hg(&self) -> Option<String> {
        self.vcs_hg()
    }

    fn vcs_mtn(&self) -> Option<String> {
        self.vcs_mtn()
    }

    fn vcs_cvs(&self) -> Option<String> {
        self.vcs_cvs()
    }

    fn vcs_darcs(&self) -> Option<String> {
        self.vcs_darcs()
    }

    fn vcs_arch(&self) -> Option<String> {
        self.vcs_arch()
    }

    fn vcs_svk(&self) -> Option<String> {
        self.vcs_svk()
    }
}

impl VcsSource for debian_control::apt::Source {
    fn vcs_git(&self) -> Option<String> {
        self.vcs_git()
    }

    fn vcs_svn(&self) -> Option<String> {
        self.vcs_svn()
    }

    fn vcs_bzr(&self) -> Option<String> {
        self.vcs_bzr()
    }

    fn vcs_hg(&self) -> Option<String> {
        self.vcs_hg()
    }

    fn vcs_mtn(&self) -> Option<String> {
        self.vcs_mtn()
    }

    fn vcs_cvs(&self) -> Option<String> {
        self.vcs_cvs()
    }

    fn vcs_darcs(&self) -> Option<String> {
        self.vcs_darcs()
    }

    fn vcs_arch(&self) -> Option<String> {
        self.vcs_arch()
    }

    fn vcs_svk(&self) -> Option<String> {
        self.vcs_svk()
    }
}

/// Determine the VCS field for a source package.
pub fn vcs_field(source_package: &impl VcsSource) -> Option<(String, String)> {
    if let Some(value) = source_package.vcs_git() {
        return Some(("Git".to_string(), value));
    }
    if let Some(value) = source_package.vcs_svn() {
        return Some(("Svn".to_string(), value));
    }
    if let Some(value) = source_package.vcs_bzr() {
        return Some(("Bzr".to_string(), value));
    }
    if let Some(value) = source_package.vcs_hg() {
        return Some(("Hg".to_string(), value));
    }
    if let Some(value) = source_package.vcs_mtn() {
        return Some(("Mtn".to_string(), value));
    }
    if let Some(value) = source_package.vcs_cvs() {
        return Some(("Cvs".to_string(), value));
    }
    if let Some(value) = source_package.vcs_darcs() {
        return Some(("Darcs".to_string(), value));
    }
    if let Some(value) = source_package.vcs_arch() {
        return Some(("Arch".to_string(), value));
    }
    if let Some(value) = source_package.vcs_svk() {
        return Some(("Svk".to_string(), value));
    }
    None
}

/// Determine the VCS URL for a source package.
pub fn source_package_vcs(source_package: &impl VcsSource) -> Option<PackageVcs> {
    if let Some(value) = source_package.vcs_git() {
        let parsed_vcs: ParsedVcs = value.parse().unwrap();
        let url = parsed_vcs.repo_url.parse().unwrap();
        return Some(PackageVcs::Git {
            url,
            branch: parsed_vcs.branch,
            subpath: parsed_vcs.subpath.map(std::path::PathBuf::from),
        });
    }
    if let Some(value) = source_package.vcs_svn() {
        let url = value.parse().unwrap();
        return Some(PackageVcs::Svn(url));
    }
    if let Some(value) = source_package.vcs_bzr() {
        let url = value.parse().unwrap();
        return Some(PackageVcs::Bzr(url));
    }
    if let Some(value) = source_package.vcs_hg() {
        let parsed_vcs: ParsedVcs = value.parse().unwrap();
        let url = parsed_vcs.repo_url.parse().unwrap();
        return Some(PackageVcs::Hg {
            url,
            branch: parsed_vcs.branch,
            subpath: parsed_vcs.subpath.map(std::path::PathBuf::from),
        });
    }
    if let Some(value) = source_package.vcs_mtn() {
        let url = value.parse().unwrap();
        return Some(PackageVcs::Mtn(url));
    }
    if let Some(value) = source_package.vcs_cvs() {
        return Some(PackageVcs::Cvs(value.clone()));
    }
    if let Some(value) = source_package.vcs_darcs() {
        let url = value.parse().unwrap();
        return Some(PackageVcs::Darcs(url));
    }
    if let Some(value) = source_package.vcs_arch() {
        let url = value.parse().unwrap();
        return Some(PackageVcs::Arch(url));
    }
    if let Some(value) = source_package.vcs_svk() {
        let url = value.parse().unwrap();
        return Some(PackageVcs::Svk(url));
    }
    None
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_source_package_vcs() {
        use super::PackageVcs;
        use debian_control::Control;

        let control: Control = r#"Source: foo
Vcs-Git: https://salsa.debian.org/foo/bar.git
"#
        .parse()
        .unwrap();
        assert_eq!(
            super::source_package_vcs(&control.source().unwrap()),
            Some(PackageVcs::Git {
                url: "https://salsa.debian.org/foo/bar.git".parse().unwrap(),
                branch: None,
                subpath: None
            })
        );

        let control: Control = r#"Source: foo
Vcs-Svn: https://svn.debian.org/svn/foo/bar
"#
        .parse()
        .unwrap();
        assert_eq!(
            super::source_package_vcs(&control.source().unwrap()),
            Some(PackageVcs::Svn(
                "https://svn.debian.org/svn/foo/bar".parse().unwrap()
            ))
        );
    }

    #[test]
    fn test_determine_gitlab_browser_url() {
        use super::determine_gitlab_browser_url;

        assert_eq!(
            determine_gitlab_browser_url("https://salsa.debian.org/foo/bar"),
            "https://salsa.debian.org/foo/bar".parse().unwrap()
        );

        assert_eq!(
            determine_gitlab_browser_url("https://salsa.debian.org/foo/bar.git"),
            "https://salsa.debian.org/foo/bar".parse().unwrap()
        );

        assert_eq!(
            determine_gitlab_browser_url("https://salsa.debian.org/foo/bar/"),
            "https://salsa.debian.org/foo/bar".parse().unwrap()
        );

        assert_eq!(
            determine_gitlab_browser_url("https://salsa.debian.org/foo/bar/.git"),
            "https://salsa.debian.org/foo/bar/".parse().unwrap()
        );

        assert_eq!(
            determine_gitlab_browser_url("https://salsa.debian.org/foo/bar.git -b baz"),
            "https://salsa.debian.org/foo/bar/-/tree/baz"
                .parse()
                .unwrap()
        );

        assert_eq!(
            determine_gitlab_browser_url(
                "https://salsa.debian.org/foo/bar.git/ -b baz [otherpath]"
            ),
            "https://salsa.debian.org/foo/bar/-/tree/baz/otherpath"
                .parse()
                .unwrap()
        );
    }

    #[test]
    fn test_determine_browser_url() {
        use super::determine_browser_url;
        use url::Url;

        assert_eq!(
            determine_browser_url("git", "https://salsa.debian.org/foo/bar", Some(false)),
            Some(Url::parse("https://salsa.debian.org/foo/bar").unwrap())
        );
        assert_eq!(
            determine_browser_url("git", "https://salsa.debian.org/foo/bar.git", Some(false)),
            Some(Url::parse("https://salsa.debian.org/foo/bar").unwrap())
        );
        assert_eq!(
            determine_browser_url("git", "https://salsa.debian.org/foo/bar/", Some(false)),
            Some(Url::parse("https://salsa.debian.org/foo/bar").unwrap())
        );
        assert_eq!(
            determine_browser_url("git", "https://salsa.debian.org/foo/bar/.git", Some(false)),
            Some(Url::parse("https://salsa.debian.org/foo/bar/").unwrap())
        );
        assert_eq!(
            determine_browser_url("git", "https://salsa.debian.org/foo/bar.git/", Some(false)),
            Some(Url::parse("https://salsa.debian.org/foo/bar").unwrap())
        );
        assert_eq!(
            determine_browser_url(
                "git",
                "https://salsa.debian.org/foo/bar.git/.git",
                Some(false)
            ),
            Some(Url::parse("https://salsa.debian.org/foo/bar.git/").unwrap())
        );
        assert_eq!(
            determine_browser_url(
                "git",
                "https://salsa.debian.org/foo/bar.git.git",
                Some(false)
            ),
            Some(Url::parse("https://salsa.debian.org/foo/bar").unwrap())
        );
        assert_eq!(
            determine_browser_url(
                "git",
                "https://salsa.debian.org/foo/bar.git.git/",
                Some(false)
            ),
            Some(Url::parse("https://salsa.debian.org/foo/bar").unwrap())
        );

        assert_eq!(
            Some(Url::parse("https://salsa.debian.org/jelmer/dulwich").unwrap()),
            determine_browser_url(
                "git",
                "https://salsa.debian.org/jelmer/dulwich.git",
                Some(false)
            )
        );

        assert_eq!(
            Some(Url::parse("https://github.com/jelmer/dulwich").unwrap()),
            determine_browser_url("git", "https://github.com/jelmer/dulwich.git", Some(false))
        );
        assert_eq!(
            Some(Url::parse("https://github.com/jelmer/dulwich/tree/master").unwrap()),
            determine_browser_url(
                "git",
                "https://github.com/jelmer/dulwich.git -b master",
                Some(false)
            )
        );
        assert_eq!(
            Some(Url::parse("https://github.com/jelmer/dulwich/tree/master").unwrap()),
            determine_browser_url(
                "git",
                "git://github.com/jelmer/dulwich -b master",
                Some(false)
            ),
        );
        assert_eq!(
            Some(Url::parse("https://github.com/jelmer/dulwich/tree/master/blah").unwrap()),
            determine_browser_url(
                "git",
                "git://github.com/jelmer/dulwich -b master [blah]",
                Some(false)
            ),
        );
        assert_eq!(
            Some(Url::parse("https://github.com/jelmer/dulwich/tree/HEAD/blah").unwrap()),
            determine_browser_url("git", "git://github.com/jelmer/dulwich [blah]", Some(false)),
        );
        assert_eq!(
            Some(Url::parse("https://git.sv.gnu.org/cgit/rcs.git").unwrap()),
            determine_browser_url("git", "https://git.sv.gnu.org/git/rcs.git", Some(false)),
        );
        assert_eq!(
            Some(Url::parse("https://git.savannah.gnu.org/cgit/rcs.git").unwrap()),
            determine_browser_url("git", "git://git.savannah.gnu.org/rcs.git", Some(false)),
        );
        assert_eq!(
            Some(Url::parse("https://sourceforge.net/p/shorewall/debian").unwrap()),
            determine_browser_url(
                "git",
                "git://git.code.sf.net/p/shorewall/debian",
                Some(false)
            ),
        );
        assert_eq!(
            Some(Url::parse("https://sourceforge.net/p/shorewall/debian/ci/foo/tree").unwrap()),
            determine_browser_url(
                "git",
                "git://git.code.sf.net/p/shorewall/debian -b foo",
                Some(false)
            ),
        );
        assert_eq!(
            Some(Url::parse("https://sourceforge.net/p/shorewall/debian/ci/HEAD/tree/sp").unwrap()),
            determine_browser_url(
                "git",
                "git://git.code.sf.net/p/shorewall/debian [sp]",
                Some(false)
            ),
        );
        assert_eq!(
            Some(Url::parse("https://sourceforge.net/p/shorewall/debian/ci/foo/tree/sp").unwrap()),
            determine_browser_url(
                "git",
                "git://git.code.sf.net/p/shorewall/debian -b foo [sp]",
                Some(false)
            ),
        );
    }

    #[test]
    fn test_vcs_field() {
        use debian_control::Control;

        let control: Control = r#"Source: foo
Vcs-Git: https://salsa.debian.org/foo/bar.git
"#
        .parse()
        .unwrap();
        assert_eq!(
            super::vcs_field(&control.source().unwrap()),
            Some((
                "Git".to_string(),
                "https://salsa.debian.org/foo/bar.git".to_string()
            ))
        );
    }
}
