1 /**
2 	Copyright: © 2013 rejectedsoftware e.K.
3 	License: Subject to the terms of the GNU GPLv3 license, as written in the included LICENSE.txt file.
4 	Authors: Sönke Ludwig
5 */
6 module dubregistry.repositories.bitbucket;
7 
8 import dubregistry.cache;
9 import dubregistry.repositories.repository;
10 import std.string : format, startsWith;
11 import std.typecons;
12 import vibe.core.log;
13 import vibe.core.stream;
14 import vibe.data.json;
15 import vibe.inet.url;
16 
17 
18 class BitbucketRepository : Repository {
19 	private {
20 		string m_owner;
21 		string m_project;
22 	}
23 
24 	static void register()
25 	{
26 		Repository factory(Json info){
27 			return new BitbucketRepository(info.owner.get!string, info.project.get!string);
28 		}
29 		addRepositoryFactory("bitbucket", &factory);
30 	}
31 
32 	this(string owner, string project)
33 	{
34 		m_owner = owner;
35 		m_project = project;
36 	}
37 
38 	RefInfo[] getTags()
39 	{
40 		Json tags;
41 		try tags = readJson("https://api.bitbucket.org/1.0/repositories/"~m_owner~"/"~m_project~"/tags");
42 		catch( Exception e ) { throw new Exception("Failed to get tags: "~e.msg); }
43 		RefInfo[] ret;
44 		foreach( string tagname, tag; tags ){
45 			try {
46 				auto commit_hash = tag.raw_node.get!string();
47 				auto commit_date = bbToIsoDate(tag.utctimestamp.get!string());
48 				ret ~= RefInfo(tagname, commit_hash, commit_date);
49 				logDebug("Found tag for %s/%s: %s", m_owner, m_project, tagname);
50 			} catch( Exception e ){
51 				throw new Exception("Failed to process tag "~tag.name.get!string~": "~e.msg);
52 			}
53 		}
54 		return ret;
55 	}
56 
57 	RefInfo[] getBranches()
58 	{
59 		Json branches = readJson("https://api.bitbucket.org/1.0/repositories/"~m_owner~"/"~m_project~"/branches");
60 		RefInfo[] ret;
61 		foreach( string branchname, branch; branches ){
62 			auto commit_hash = branch.raw_node.get!string();
63 			auto commit_date = bbToIsoDate(branch.utctimestamp.get!string());
64 			ret ~= RefInfo(branchname, commit_hash, commit_date);
65 			logDebug("Found branch for %s/%s: %s", m_owner, m_project, branchname);
66 		}
67 		return ret;
68 	}
69 
70 	void readFile(string commit_sha, Path path, scope void delegate(scope InputStream) reader)
71 	{
72 		assert(path.absolute, "Passed relative path to readFile.");
73 		auto url = "https://bitbucket.org/api/1.0/repositories/"~m_owner~"/"~m_project~"/raw/"~commit_sha~path.toString();
74 		downloadCached(url, (scope input) {
75 			reader(input);
76 		}, true);
77 	}
78 
79 	string getDownloadUrl(string ver)
80 	{
81 		if( ver.startsWith("~") ) ver = ver[1 .. $];
82 		else ver = ver;
83 		return "https://bitbucket.org/"~m_owner~"/"~m_project~"/get/"~ver~".zip";
84 	}
85 
86 	void download(string ver, scope void delegate(scope InputStream) del)
87 	{
88 		downloadCached(getDownloadUrl(ver), del);
89 	}
90 }
91 
92 private auto bbToIsoDate(string bbdate)
93 {
94 	import std.array, std.datetime : SysTime;
95 	auto ttz = bbdate.split("+");
96 	if( ttz.length < 2 ) ttz ~= "00:00";
97 	auto parts = ttz[0].split("-");
98 	parts = parts[0 .. $-1] ~ parts[$-1].split(" ");
99 	parts = parts[0 .. $-1] ~ parts[$-1].split(":");
100 
101 	return SysTime.fromISOString(format("%s%s%sT%s%s%s+%s", parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], ttz[1]));
102 }