1 module filequery; 2 3 import core.stdc.errno; 4 import std.typecons : Flag, Yes, No; 5 import std.file : FileException; 6 import std.traits : isNarrowString; 7 8 //////////////////////////////////////////////////////////////////////////////// 9 // NOTE: this was copied from file.d 10 //////////////////////////////////////////////////////////////////////////////// 11 // Character type used for operating system filesystem APIs 12 version (Windows) 13 { 14 private alias FSChar = wchar; 15 } 16 else version (Posix) 17 { 18 private alias FSChar = char; 19 } 20 else 21 static assert(0); 22 private T cenforce(T)(T condition, lazy const(char)[] name, string file = __FILE__, size_t line = __LINE__) 23 { 24 if (condition) 25 return condition; 26 version (Windows) 27 { 28 throw new FileException(name, .GetLastError(), file, line); 29 } 30 else version (Posix) 31 { 32 throw new FileException(name, .errno, file, line); 33 } 34 } 35 version (Windows) 36 @trusted 37 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez, 38 string file = __FILE__, size_t line = __LINE__) 39 { 40 if (condition) 41 return condition; 42 if (!name) 43 { 44 import core.stdc.wchar_ : wcslen; 45 import std.conv : to; 46 47 auto len = namez ? wcslen(namez) : 0; 48 name = to!string(namez[0 .. len]); 49 } 50 throw new FileException(name, .GetLastError(), file, line); 51 } 52 53 version (Posix) 54 @trusted 55 private T cenforce(T)(T condition, const(char)[] name, const(FSChar)* namez, 56 string file = __FILE__, size_t line = __LINE__) 57 { 58 if (condition) 59 return condition; 60 if (!name) 61 { 62 import core.stdc.string : strlen; 63 64 auto len = namez ? strlen(namez) : 0; 65 name = namez[0 .. len].idup; 66 } 67 throw new FileException(name, .errno, file, line); 68 } 69 //////////////////////////////////////////////////////////////////////////////// 70 //////////////////////////////////////////////////////////////////////////////// 71 //////////////////////////////////////////////////////////////////////////////// 72 73 74 static struct AllSupportedOps 75 { 76 // 77 // The following settings are named by the operations that we would want to support on the FileInfo. 78 // 79 /++ 80 If true, will cause queryFile to throw an exception if the file does not exist and 81 modifies FileInfo to not have any representation for a non-existing file. 82 +/ 83 enum bool exists = true; 84 enum bool isFile = true; 85 86 // Windows: GetFileAttributes FILE_ATTRIBUTE_DIRECTTORY 87 // Posix: ... 88 enum bool isDir = true; 89 // Windows: GetFileAttributes FILE_ATTRIBUTE_REPARSE_POINT 90 enum bool isSymlink = true; 91 // Windows: ? 92 // Posix: stat/lstat st_size 93 enum bool getSize = true; 94 95 // Windows: ? 96 // Posix: stat/lstat st_mtime 97 enum bool timeLastModified = true; 98 99 // Windows Specific 100 enum bool isArchive = true; // See GetFileAttributes FILE_ATTRIBUTE_ARCHIVE 101 enum bool isCompressed = true; // See GetFileAttributes FILE_ATTRIBUTE_COMPRESSED 102 enum bool isHidden = true; // See GetFileAttributes FILE_ATTRIBUTE_HIDDEN 103 enum bool isNormal = true; // See GetFileAttributes FILE_ATTRIBUTE_NORMAL 104 enum bool isReadOnly = true; // See GetFileAttributes FILE_ATTRIBUTE_READONLY 105 106 // Posix Specific 107 enum bool getDeviceID = true; // See stat.st_dev 108 enum bool getInode = true; // See stat.st_ino 109 enum bool getMode = true; // See stat.st_mode 110 enum bool getHardLinkCount = true; // See stat.st_nlink 111 enum bool getUid = true; // See stat.st_uid 112 enum bool getGid = true; // See stat.st_gid 113 enum bool getSpecialDeviceID = true; // See stat.st_rdev 114 } 115 116 struct FileQueryEverythingEnabledExamplePolicy 117 { 118 // Posix: with the stat function, you can get the info on the symbol link or on it's file. 119 // You do this by either calling `stat` or `lstat`. 120 enum followSymlink = true; 121 alias SupportedOps = AllSupportedOps; 122 } 123 124 125 126 template fileQueryTemplate(Policy) 127 { 128 // TODO: static assert(isValidFileQueryPolicy(Policy)); 129 private bool supportOp(string name) 130 { 131 foreach(op; Policy.SupportedOps) 132 { 133 if (op == name) 134 { 135 return true; 136 } 137 } 138 return false; 139 } 140 141 version(Windows) 142 { 143 private enum oneOrMoreAttributesSupported = 144 (supportOp("isFile") || 145 supportOp("isDir") || 146 supportOp("isSymlink") || 147 supportOp("isArchive") || 148 supportOp("isCompressed") || 149 supportOp("isHidden") || 150 supportOp("isNormal") || 151 supportOp("isReadOnly")); 152 153 static if (oneOrMoreAttributesSupported) 154 { 155 enum useGetFileAttributes = oneOrMoreAttributesSupported; 156 } 157 else static if (supportOp("exists")) 158 { 159 // TODO: maybe in this case we should just use the PathFileExists function istead? 160 enum useGetFileAttributes = true; 161 } 162 else 163 { 164 enum useGetFileAttributes = false; 165 } 166 } 167 else version(Posix) 168 { 169 170 // TODO: determine if we are going to use the stat function 171 enum useStat = true; 172 173 static if (useStat) 174 { 175 import core.sys.posix.sys.stat; 176 static if (supportOp("exists")) 177 enum saveStatSucceeded = true; 178 else 179 enum saveStatSucceeded = false; 180 } 181 } 182 else static assert(0); 183 184 struct FileInfo 185 { 186 version(Windows) 187 { 188 static if (useGetFileAttributes) 189 { 190 DWORD getFileAttributesResult; 191 } 192 } 193 else 194 { 195 static if (useStat) 196 { 197 stat_t statbuf; 198 static if (saveStatSucceeded) 199 { 200 bool statSucceeded; 201 } 202 } 203 else static assert(0, "not implemented"); 204 } 205 206 static if (supportOp("exists")) 207 { 208 @property bool exists() 209 { 210 version(Windows) 211 { 212 static if (useGetFileAttributes) 213 { 214 return getFileAttributesResult != INVALID_FILE_ATTRIBUTES; 215 } 216 else static assert(0); 217 } 218 else version(Posix) 219 { 220 static if (useStat) 221 { 222 return statSucceeded; 223 } 224 else static assert(0, "not implemented"); 225 } 226 else static assert(0, "not implemented"); 227 } 228 } 229 static if (supportOp("isFile")) 230 { 231 @property bool isFile() 232 { 233 version(Windows) 234 { 235 static assert(0, "not implemented"); 236 } 237 else version(Posix) 238 { 239 static if (useStat) 240 { 241 return (statbuf.st_mode & S_IFMT) == S_IFREG; 242 } 243 else static assert(0, "not implemented"); 244 } 245 else static assert(0, "not implemented"); 246 } 247 } 248 } 249 250 251 /* 252 /// Note: If a symlink is passed in, this function will query the underlying file instead. 253 /// You can use querySymlink to query the symlink itself. 254 FileInfo query(R)(R name) 255 { 256 FileInfo info = void; 257 query(&info, name); 258 return info; 259 } 260 */ 261 /// ditto 262 auto query(Policy, R)(FileInfo* outInfo, R name) 263 { 264 import std.internal.cstring : tempCString; 265 version(Windows) 266 { 267 static assert(0, "not implemented"); 268 } 269 else version(Posix) 270 { 271 static if (useStat) 272 { 273 import core.sys.posix.sys.stat : stat; 274 auto namez = name.tempCString(); 275 bool statSucceeded = (0 == stat(namez, &outInfo.statbuf)); 276 277 static if(saveStatSucceeded) 278 { 279 outInfo.statSucceeded = statSucceeded; 280 } 281 else 282 { 283 static if (isNarrowString!R && is(Unqual!(ElementEncodingType!R) == char)) 284 alias names = name; 285 else 286 string names = null; 287 cenforce(statSucceeded, names, namez); 288 } 289 if (!statSucceeded) 290 { 291 static if (Policy.enforceExists) 292 { 293 294 295 } 296 } 297 } 298 } 299 else assert(0, "not implemented"); 300 } 301 version(Posix) 302 { 303 // query the symlink itself instead of the underlying file 304 FileInfo querySymlink(R)(R name) 305 { 306 assert(0, "not implemented"); 307 } 308 } 309 } 310 /+ 311 struct SplitString 312 { 313 string str; 314 size_t splitStart; 315 size_t splitEnd; 316 @property string left() const { return str[0 .. splitStart]; } 317 @property string right() const { return str[splitEnd .. $]; } 318 } 319 SplitString splitString(string str, char delimiter) pure 320 { 321 size_t index; 322 for (index = 0; index < str.length; index++) 323 { 324 if (str[index] == delimiter) 325 { 326 return SplitString(str, index, index + 1); 327 } 328 } 329 return SplitString(str, index, index); 330 } 331 332 template tuple(T...) 333 { 334 alias tuple = T; 335 } 336 template OpTuple(string ops) 337 { 338 static if (ops.length == 0) 339 { 340 alias OpTuple = tuple!(); 341 } 342 else 343 { 344 private enum splitOps = splitString(ops, ','); 345 alias OpTuple = tuple!(splitOps.left, OpTuple!(splitOps.right)); 346 } 347 } 348 +/ 349 mixin template makeFileQuery(string[] options) 350 { 351 static struct FileQueryPolicy 352 { 353 //alias SupportedOps = OpTuple!options; 354 alias SupportedOps = options; 355 } 356 alias fileQuery = fileQueryTemplate!FileQueryPolicy; 357 } 358 359 unittest 360 { 361 import std.stdio; 362 { 363 mixin makeFileQuery!(["exists"]); 364 auto helloInfo = fileQuery.query("hello"); 365 writefln("helloInfo.exists = %s", helloInfo.exists); 366 } 367 { 368 mixin makeFileQuery!(["isFile"]); 369 auto helloInfo = fileQuery.query("hello"); 370 writefln("helloInfo.isFile = %s", helloInfo.isFile); 371 } 372 }