1 module pkm.search; 2 3 import std.process: execute, environment, executeShell, Config, spawnProcess, wait; 4 import std.file: tempDir, remove, readText; 5 // import std.file; 6 import std.stdio; 7 import std.regex; 8 import std.path: buildNormalizedPath, absolutePath; 9 import std.array: split; 10 import std.conv: to; 11 import std.range: repeat; 12 import std.algorithm: canFind, sort; 13 import std.numeric: gapWeightedSimilarityNormalized; 14 15 import core.sys.posix.sys.ioctl; 16 17 import pkm.pkg; 18 import sily.bashfmt; 19 20 // yay regex: 21 // (.*?)\/(.*?)\s(.*?)\s\((.*?)\)(?:\s\[(.*)\])?(?:\s\((Orphaned)\))?(?:\s\(Out-of-date:\s(.*?)\))?(?:\s\((Installed)(?:\:\s(.*?))?\))?(?:\s{6}|\s{5})(.*)(?:\r|\n|\z) 22 // 1 2 3 4 5 6 7 8 9 10 23 // repo/name version (size|aur-votes) [group]? (orphaned) (outofdate) (installed: (version)) \n (description) 24 private auto reg = regex( 25 r"(.*?)\/(.*?)\s(.*?)\s\((.*?)\)(?:\s\[(.*)\])?" ~ 26 r"(?:\s\((Orphaned)\))?(?:\s\(Out-of-date:\s(.*?)\))?" ~ 27 r"(?:\s\((Installed)(?:\:\s(.*?))?\))?(?:\s{6}|\s{5})(.*)(?:\r|\n|\z)", "gm"); 28 29 int search(string yay, string[] terms, bool color) { 30 // string yay = "/usr/bin/yay"; 31 string tmpFile = tempDir ~ "/" ~ "pkm-yay-search-output.txt"; 32 tmpFile = tmpFile.buildNormalizedPath.absolutePath; 33 34 auto processOut = File(tmpFile, "w+"); 35 36 auto pidErr = wait(spawnProcess([yay, "-Ss"] ~ terms, std.stdio.stdin, processOut)); 37 38 processOut.close(); 39 40 if (pidErr) { 41 remove(tmpFile); 42 writefln("yay exited with code \"%d\"", pidErr); 43 return pidErr; 44 } 45 46 printPackages(tmpFile, terms, color); 47 48 remove(tmpFile); 49 50 return 0; 51 } 52 53 54 // 1 2 3 4 5 6 55 // repo/name version (size|aur-votes) [group]? (orphaned) 56 // 7 8 9 10 57 // (outofdate) (installed: (version)) \n (description) 58 void printPackages(string tmpFile, string[] searchTerms, bool color) { 59 string contents = readText(tmpFile); 60 61 Pkg[] pkgs = []; 62 63 auto packages = matchAll(contents, reg); 64 65 foreach (pkg; packages) { 66 string pkgsize; 67 string inssize; 68 69 if (pkg[1] == "aur") { 70 string[] _size = pkg[4].split(' '); 71 pkgsize = _size[0]; 72 inssize = _size[1]; 73 } else { 74 string[] _size = pkg[4].split(' '); 75 pkgsize = _size[0] ~ " " ~ _size[1]; 76 inssize = _size[2] ~ " " ~ _size[3]; 77 } 78 79 pkgs ~= Pkg( 80 pkg[1], // repo 81 pkg[2], // name 82 pkg[3], // version 83 pkgsize, // package size / aur votes 84 inssize, // installation size / aur popularity 85 pkg[5], // group 86 pkg[6] != "" ? true : false, // is orphaned 87 pkg[7] != "" ? true : false, // is out of date 88 pkg[7], // out of date date 89 pkg[8] != "" ? true : false, // is installed 90 pkg[9], // installed version 91 pkg[10] // description 92 ); 93 } 94 95 sort!((a,b) { 96 return gapWeightedSimilarityNormalized(a.name.split("-"), searchTerms, 0) < 97 gapWeightedSimilarityNormalized(b.name.split("-"), searchTerms, 0); 98 })(pkgs); 99 100 foreach (pkg; pkgs) { 101 printPackage(pkg, color); 102 } 103 } 104 105 void printPackage(Pkg pkg, bool color) { 106 // if (!(pkg.isOrphaned || pkg.isInstalled || pkg.isOutdated)) return; 107 winsize w; 108 ioctl(0, TIOCGWINSZ, &w); 109 int terminalWidth = w.ws_col; 110 111 string installstr = color || pkg.isInstalled ? "[i]" : "[ ]"; 112 string orphanedstr = color || pkg.isOrphaned ? "[a]" : "[ ]"; 113 string outdatedstr = color || pkg.isOutdated ? "[o]" : "[ ]"; 114 ulong flagsLength = installstr.length + orphanedstr.length + outdatedstr.length + 2; 115 116 ulong installPos = 1; 117 if (pkg.name.length + flagsLength < terminalWidth / 2) { 118 installPos = (terminalWidth / 2) - pkg.name.length - flagsLength; 119 } 120 write(pkg.name); 121 122 write(' '.repeat(installPos)); 123 124 if (pkg.isOrphaned) { 125 writecol(FG.ltred, color,orphanedstr); 126 } else { 127 // write(' '.repeat(orphanedstr.length)); 128 writecol(FG.dkgray, color, orphanedstr); 129 } 130 131 write(' '); 132 133 if (pkg.isOutdated) { 134 writecol(FG.ltred, color, outdatedstr); 135 } else { 136 // write(' '.repeat(outdatedstr.length)); 137 writecol(FG.dkgray, color, outdatedstr); 138 } 139 140 write(' '); 141 142 if (pkg.isInstalled) { 143 writecol(FG.ltgreen, color, installstr); 144 } else { 145 // write(' '.repeat(installstr.length)); 146 writecol(FG.dkgray, color, installstr); 147 } 148 149 write(' '); 150 151 ulong verlen = pkg.installedVersion != "" ? pkg.installedVersion.length + (color ? 0 : 1) : pkg.ver.length; 152 if (pkg.installedVersion != "") { 153 writecol(FG.ltmagenta, color, (color ? "" : "@") ~ pkg.installedVersion); 154 } else { 155 writecol(FG.cyan, color, pkg.ver); 156 } 157 158 ulong reposize = "[aur]".length + 1; 159 ulong sizelen = pkg.pkgsize.length + pkg.inssize.length + 1; 160 161 ulong wantedlen = verlen + sizelen + 1 + reposize; 162 ulong rside = 1; 163 if (wantedlen < terminalWidth / 2) { 164 rside = (terminalWidth / 2) - wantedlen; 165 } 166 167 write(' '.repeat(rside)); 168 169 if (pkg.repo == "aur") { 170 float votes = pkg.aurvotes.to!float; 171 FG colvot = FG.dkgray; 172 if (votes >= 100.0) colvot = FG.ltred; 173 if (votes >= 250.0) colvot = FG.ltyellow; 174 if (votes >= 500.0) colvot = FG.ltcyan; 175 if (votes >= 750.0) colvot = FG.ltgreen; 176 if (votes >= 1000.0) colvot = FG.ltblue; 177 178 float popul = pkg.aurpopul.to!float; 179 FG colpop = FG.dkgray; 180 if (popul >= 1.0) colpop = FG.ltred; 181 if (popul >= 10.0) colpop = FG.ltyellow; 182 if (popul >= 20.0) colpop = FG.ltcyan; 183 if (popul >= 30.0) colpop = FG.ltgreen; 184 if (popul >= 40.0) colpop = FG.ltblue; 185 writecol(colvot, color, pkg.aurvotes); 186 write(' '); 187 writecol(colpop, color, pkg.aurpopul); 188 } else { 189 writecol(FG.reset, color, pkg.pkgsize); 190 write(' '); 191 writecol(FG.reset, color, pkg.inssize); 192 } 193 write(' '); 194 195 FG repocol = FG.reset; 196 switch (pkg.repo) { 197 case "aur": repocol = FG.ltblue; break; 198 case "core": repocol = FG.ltyellow; break; 199 case "extra": repocol = FG.ltgreen; break; 200 case "community": repocol = FG.ltmagenta; break; 201 default: 202 if (pkg.repo.canFind("testing")) { 203 repocol = FG.ltred; 204 } else { 205 repocol = FG.ltcyan; 206 } 207 break; 208 } 209 210 writecol(repocol, color, "[" ~ pkg.repo[0..3] ~ "]"); 211 writeln(); 212 writeln(" " ~ pkg.description); 213 214 // writeln(); 215 } 216 217 void writecol(FG col, bool enabled, string args) { 218 if (enabled) { 219 write(col ~ args ~ FG.reset); 220 } else { 221 write(args); 222 } 223 } 224 225 void writelncol(FG col, bool enabled, string args) { 226 writecol(col, enabled, args ~ "\n"); 227 }