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 }