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.viewutils; 7 8 import std.datetime; 9 import std..string; 10 import vibe.data.json; 11 12 string formatDate(Json date) 13 { 14 return formatDate(date.opt!string); 15 } 16 17 string formatDate(string iso_ext_date) 18 { 19 if (iso_ext_date.length == 0) return "---"; 20 return formatDate(SysTime.fromISOExtString(iso_ext_date)); 21 } 22 23 string formatDate(SysTime st) 24 { 25 return (cast(Date)st).toSimpleString(); 26 } 27 28 string formatDateTime(Json dateTime) 29 { 30 return formatDateTime(dateTime.opt!string); 31 } 32 33 string formatDateTime(string iso_ext_date) 34 { 35 if (iso_ext_date.length == 0) return "---"; 36 return formatDateTime(SysTime.fromISOExtString(iso_ext_date)); 37 } 38 39 string formatDateTime(SysTime st) 40 { 41 return st.toSimpleString(); 42 } 43 44 string formatFuzzyDate(Json dateTime) 45 { 46 return formatFuzzyDate(dateTime.opt!string); 47 } 48 49 string formatFuzzyDate(string iso_ext_date) 50 { 51 if (iso_ext_date.length == 0) return "---"; 52 return formatFuzzyDate(SysTime.fromISOExtString(iso_ext_date)); 53 } 54 55 string formatFuzzyDate(SysTime st) 56 { 57 auto now = Clock.currTime(UTC()); 58 59 // TODO: proper singular forms etc. (probably done together with l8n) 60 auto tm = now - st; 61 if (tm < dur!"seconds"(0)) return "still going to happen"; 62 else if (tm < dur!"seconds"(1)) return "just now"; 63 else if (tm < dur!"minutes"(1)) return "less than a minute ago"; 64 else if (tm < dur!"minutes"(2)) return "a minute ago"; 65 else if (tm < dur!"hours"(1)) return format("%s minutes ago", tm.total!"minutes"()); 66 else if (tm < dur!"hours"(2)) return "an hour ago"; 67 else if (tm < dur!"days"(1)) return format("%s hours ago", tm.total!"hours"()); 68 else if (tm < dur!"days"(2)) return "a day ago"; 69 else if (tm < dur!"weeks"(5)) return format("%s days ago", tm.total!"days"()); 70 else if (tm < dur!"weeks"(52)) { 71 auto m1 = st.month; 72 auto m2 = now.month; 73 auto months = (now.year - st.year) * 12 + m2 - m1; 74 if (months == 1) return "a month ago"; 75 else return format("%s months ago", months); 76 } else if (now.year - st.year <= 1) return "a year ago"; 77 else return format("%s years ago", now.year - st.year); 78 } 79 80 string formatScore(string rootDir, float score) 81 { 82 return format(`<a href="%sdevelop#package-scoring">%.1f</a>`, rootDir, score); 83 } 84 85 string formatPackageStats(Stats)(Stats s) 86 { 87 return format("%.1f\n\n#downloads / m: %s\n#stars: %s\n#watchers: %s\n#forks: %s\n#issues: %s\n", 88 s.score, s.downloads.monthly, s.repo.stars, s.repo.watchers, s.repo.forks, s.repo.issues); 89 } 90 91 /** Takes an input range of version strings and returns the index of the "best" 92 version. 93 94 "Best" version in this case means the highest released version. Numbered 95 versions will be preferred over branch names. 96 97 */ 98 size_t getBestVersionIndex(R)(R versions) 99 { 100 import dub.semver; 101 102 size_t ret = size_t.max; 103 string retv; 104 size_t i = 0; 105 foreach (vstr; versions) { 106 if (ret == size_t.max) { 107 ret = i; 108 retv = vstr; 109 } else { 110 if (retv.startsWith("~")) { 111 if (vstr == "~master" || !vstr.startsWith("~")) { 112 ret = i; 113 retv = vstr; 114 } 115 } else if (!vstr.startsWith("~") && compareVersions(vstr, retv) > 0) { 116 ret = i; 117 retv = vstr; 118 } 119 } 120 i++; 121 } 122 return ret; 123 } 124 125 unittest { 126 assert(getBestVersionIndex(["~master", "0.0.1", "1.0.0"]) == 2); 127 assert(getBestVersionIndex(["~master", "0.0.1-alpha"]) == 1); 128 assert(getBestVersionIndex(["~somebranch", "~master"]) == 1); 129 assert(getBestVersionIndex(["~master", "~somebranch"]) == 0); 130 assert(getBestVersionIndex(["1.0.0", "1.0.1-alpha"]) == 1); 131 }