1 module dub.packagesuppliers.filesystem; 2 3 import dub.packagesuppliers.packagesupplier; 4 5 /** 6 File system based package supplier. 7 8 This package supplier searches a certain directory for files with names of 9 the form "[package name]-[version].zip". 10 */ 11 class FileSystemPackageSupplier : PackageSupplier { 12 import dub.internal.vibecompat.core.log; 13 version (Have_vibe_core) import dub.internal.vibecompat.inet.path : toNativeString; 14 import std.exception : enforce; 15 private { 16 NativePath m_path; 17 } 18 19 this(NativePath root) { m_path = root; } 20 21 override @property string description() { return "file repository at "~m_path.toNativeString(); } 22 23 Version[] getVersions(string package_id) 24 { 25 import std.algorithm.sorting : sort; 26 import std.file : dirEntries, DirEntry, SpanMode; 27 import std.conv : to; 28 Version[] ret; 29 foreach (DirEntry d; dirEntries(m_path.toNativeString(), package_id~"*", SpanMode.shallow)) { 30 NativePath p = NativePath(d.name); 31 logDebug("Entry: %s", p); 32 enforce(to!string(p.head)[$-4..$] == ".zip"); 33 auto vers = p.head.name[package_id.length+1..$-4]; 34 logDebug("Version: %s", vers); 35 ret ~= Version(vers); 36 } 37 ret.sort(); 38 return ret; 39 } 40 41 void fetchPackage(NativePath path, string packageId, Dependency dep, bool pre_release) 42 { 43 import dub.internal.vibecompat.core.file : copyFile, existsFile; 44 enforce(path.absolute); 45 logInfo("Storing package '"~packageId~"', version requirements: %s", dep); 46 auto filename = bestPackageFile(packageId, dep, pre_release); 47 enforce(existsFile(filename)); 48 copyFile(filename, path); 49 } 50 51 Json fetchPackageRecipe(string packageId, Dependency dep, bool pre_release) 52 { 53 import std.array : split; 54 import std.path : stripExtension; 55 import std.algorithm : startsWith, endsWith; 56 import dub.internal.utils : packageInfoFileFromZip; 57 import dub.recipe.io : parsePackageRecipe; 58 import dub.recipe.json : toJson; 59 60 auto filePath = bestPackageFile(packageId, dep, pre_release); 61 string packageFileName; 62 string packageFileContent = packageInfoFileFromZip(filePath, packageFileName); 63 auto recipe = parsePackageRecipe(packageFileContent, packageFileName); 64 Json json = toJson(recipe); 65 auto basename = filePath.head.name; 66 enforce(basename.endsWith(".zip"), "Malformed package filename: " ~ filePath.toNativeString); 67 enforce(basename.startsWith(packageId), "Malformed package filename: " ~ filePath.toNativeString); 68 json["version"] = basename[packageId.length + 1 .. $-4]; 69 return json; 70 } 71 72 SearchResult[] searchPackages(string query) 73 { 74 // TODO! 75 return null; 76 } 77 78 private NativePath bestPackageFile(string packageId, Dependency dep, bool pre_release) 79 { 80 import std.algorithm.iteration : filter; 81 import std.array : array; 82 import std.format : format; 83 NativePath toPath(Version ver) { 84 return m_path ~ (packageId ~ "-" ~ ver.toString() ~ ".zip"); 85 } 86 auto versions = getVersions(packageId).filter!(v => dep.matches(v)).array; 87 enforce(versions.length > 0, format("No package %s found matching %s", packageId, dep)); 88 foreach_reverse (ver; versions) { 89 if (pre_release || !ver.isPreRelease) 90 return toPath(ver); 91 } 92 return toPath(versions[$-1]); 93 } 94 }