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.repository;
7 
8 import vibe.vibe;
9 
10 import dubregistry.cache;
11 import dubregistry.dbcontroller : DbRepository, DbRepoStats;
12 import std.digest.sha;
13 import std.typecons;
14 
15 
16 Repository getRepository(DbRepository repinfo)
17 @safe {
18 	if( auto pr = repinfo in s_repositories )
19 		return *pr;
20 
21 	logDebug("Returning new repository: %s", repinfo);
22 	auto pf = repinfo.kind in s_repositoryFactories;
23 	enforce(pf, "Unknown repository type: "~repinfo.kind);
24 	auto rep = (*pf)(repinfo);
25 	s_repositories[repinfo] = rep;
26 	return rep;
27 }
28 
29 void addRepositoryFactory(string kind, RepositoryFactory factory)
30 @safe {
31 	assert(kind !in s_repositoryFactories);
32 	s_repositoryFactories[kind] = factory;
33 }
34 
35 bool supportsRepositoryKind(string kind)
36 @safe {
37 	return (kind in s_repositoryFactories) !is null;
38 }
39 
40 
41 alias RepositoryFactory = Repository delegate(DbRepository) @safe;
42 
43 interface Repository {
44 @safe:
45 	RefInfo[] getTags();
46 	RefInfo[] getBranches();
47 	/// Get basic repository information, throws FileNotFoundException when the repo no longer exists.
48 	RepositoryInfo getInfo();
49 	RepositoryFile[] listFiles(string commit_sha, InetPath path);
50 	void readFile(string commit_sha, InetPath path, scope void delegate(scope InputStream) @safe reader);
51 	string getDownloadUrl(string tag_or_branch);
52 	void download(string tag_or_branch, scope void delegate(scope InputStream) @safe del);
53 }
54 
55 struct RepositoryInfo {
56 	bool isFork;
57 	DbRepoStats stats;
58 }
59 
60 struct RefInfo {
61 	string name;
62 	string sha;
63 	BsonDate date;
64 
65 	this(string name, string sha, SysTime date)
66 	@safe {
67 		this.name = name;
68 		this.sha = sha;
69 		this.date = BsonDate(date);
70 	}
71 }
72 
73 struct RepositoryFile {
74 	enum Type {
75 		directory,
76 		file
77 		// submodule
78 	}
79 
80 	/// A commit where this file/directory has the specified properties, not neccessarily the last change.
81 	string commitSha;
82 	/// Absolute path of the file in the repository.
83 	InetPath path;
84 	/// Size of the file or size_t.max if unknown.
85 	size_t size = size_t.max;
86 	/// Type of the entry (directory or file)
87 	Type type;
88 }
89 
90 package Json readJson(string url, bool sanitize = false, bool cache_priority = false)
91 @safe {
92 	import dubregistry.internal.utils : black;
93 
94 	Json ret;
95 	logDiagnostic("Getting JSON response from %s", url.black);
96 	Exception ex;
97 	foreach (i; 0 .. 2) {
98 		try {
99 			downloadCached(url, (scope input){
100 				scope (failure) clearCacheEntry(url);
101 				auto text = input.readAllUTF8(sanitize);
102 				ret = parseJsonString(text);
103 			}, cache_priority);
104 			return ret;
105 		} catch (FileNotFoundException e) {
106 			throw e;
107 		} catch (Exception e) {
108 			logDiagnostic("Failed to parse downloaded JSON document (attempt #%s): %s", i+1, e.msg);
109 			ex = e;
110 		}
111 	}
112 	throw new Exception(format("Failed to read JSON from %s: %s", url.black, ex.msg), __FILE__, __LINE__, ex);
113 }
114 
115 private {
116 	Repository[DbRepository] s_repositories;
117 	RepositoryFactory[string] s_repositoryFactories;
118 }