use std::str;
use crate::serde_json;
use super::*;
pub const SERVICE_DOMAIN: &'static str = "bitbucket.org";
pub const SERVICE_API_DOMAIN: &'static str = "api.bitbucket.org";
#[derive(Clone, Debug, Deserialize)]
struct Tag
{
name: String,
}
#[derive(Clone, Debug, Deserialize)]
struct Tags
{
values: Vec<Tag>,
}
#[derive(Clone)]
pub struct BitbucketSrc
{
name: PkgName,
old_name: Option<PkgName>,
home_dir: PathBuf,
work_dir: PathBuf,
printer: Arc<dyn Print + Send + Sync>,
versions: Option<BTreeSet<Version>>,
current_version: Option<Version>,
dir: Option<PathBuf>,
}
impl BitbucketSrc
{
pub fn new(name: PkgName, old_name: Option<PkgName>, home_dir: PathBuf, work_dir: PathBuf, printer: Arc<dyn Print + Send + Sync>) -> Option<Self>
{
let original_name = old_name.as_ref().unwrap_or(&name);
if original_name.name().split('/').count() != 3 {
return None;
}
match original_name.name().split_once('/') {
Some((domain, _)) if domain == SERVICE_DOMAIN => {
Some(BitbucketSrc {
name,
old_name,
home_dir,
work_dir,
printer,
versions: None,
current_version: None,
dir: None,
})
},
_ => None,
}
}
pub fn name(&self) -> &PkgName
{ &self.name }
pub fn old_name(&self) -> Option<&PkgName>
{
match &self.old_name {
Some(old_name) => Some(old_name),
None => None,
}
}
pub fn home_dir(&self) -> &Path
{ self.home_dir.as_path() }
pub fn work_dir(&self) -> &Path
{ self.work_dir.as_path() }
pub fn printer(&self) -> &Arc<dyn Print + Send + Sync>
{ &self.printer }
pub fn current_version(&self) -> Option<&Version>
{
match &self.current_version {
Some(current_version) => Some(current_version),
None => None,
}
}
fn update_versions(&self, is_update: bool) -> Result<BTreeSet<Version>>
{
let original_name = self.old_name.as_ref().unwrap_or(&self.name);
let repo_path = match original_name.name().split_once('/') {
Some((_, tmp_repo_path)) => tmp_repo_path,
None => return Err(Error::PkgName(self.name.clone(), String::from("no package repository path"))),
};
update_pkg_versions(&self.name, &self.old_name, self.home_dir.as_path(), is_update, &self.printer, || {
let mut easy = curl::easy::Easy::new();
easy.url(format!("https://{}/2.0/repositories/{}/refs/tags", SERVICE_API_DOMAIN, str_to_url_name(repo_path, true)).as_str())?;
let mut http_headers = List::new();
http_headers.append(USER_AGENT_HTTP_HEADER)?;
easy.http_headers(http_headers)?;
easy.follow_location(true)?;
Ok(easy)
}, |data| {
let s = match str::from_utf8(data) {
Ok(tmp_s) => tmp_s,
Err(_) => return Err(Error::PkgName(self.name.clone(), String::from("data contains invalid UTF-8 character"))),
};
let tags: Tags = match serde_json::from_str(s) {
Ok(tmp_refs) => tmp_refs,
Err(err) => return Err(Error::SerdeJson(err)),
};
let mut versions: BTreeSet<Version> = BTreeSet::new();
for tag in &tags.values {
match tag_name_to_version(tag.name.as_str()) {
Some(version) => {
versions.insert(version);
},
None => (),
}
}
Ok(versions)
})
}
}
impl Source for BitbucketSrc
{
fn update(&mut self) -> Result<()>
{
self.versions = Some(self.update_versions(true)?);
Ok(())
}
fn versions(&mut self) -> Result<&BTreeSet<Version>>
{
if self.versions.is_none() {
self.versions = Some(self.update_versions(false)?);
}
match &self.versions {
Some(versions) => Ok(versions),
None => return Err(Error::PkgName(self.name.clone(), String::from("no package versions"))),
}
}
fn set_current_version(&mut self, version: Version)
{ self.current_version = Some(version); }
fn dir(&mut self) -> Result<&Path>
{
if self.dir.is_none() {
match &self.current_version {
Some(current_version) => {
self.dir = Some(extract_pkg_file(&self.name, current_version, &self.work_dir, &self.printer, || {
let original_name = self.old_name.as_ref().unwrap_or(&self.name);
let tag_name = version_to_tag_name(current_version);
let url = format!("https://{}/get/{}.zip", str_to_url_name(original_name.name(), true), str_to_url_name(tag_name.as_str(), false));
download_pkg_file(&self.name, &self.old_name, current_version, url.as_str(), &self.home_dir, &self.printer)
})?)
},
None => return Err(Error::PkgName(self.name.clone(), String::from("no current package version"))),
}
}
match &self.dir {
Some(versions) => Ok(versions),
None => return Err(Error::PkgName(self.name.clone(), String::from("no package directory"))),
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct BitbucketSrcFactory;
impl BitbucketSrcFactory
{
pub fn new() -> Self
{ BitbucketSrcFactory }
}
impl SourceCreate for BitbucketSrcFactory
{
fn create(&self, name: PkgName, old_name: Option<PkgName>, home_dir: PathBuf, work_dir: PathBuf, printer: Arc<dyn Print + Send + Sync>) -> Option<Box<dyn Source + Send + Sync>>
{
match BitbucketSrc::new(name, old_name, home_dir, work_dir, printer) {
Some(src) => Some(Box::new(src)),
None => None,
}
}
}