1 // Scriptlike: Utility to aid in script-like programs. 2 // Written in the D programming language. 3 4 /// Copyright: Copyright (C) 2014-2015 Nick Sabalausky 5 /// License: zlib/libpng 6 /// Authors: Nick Sabalausky 7 8 module scriptlike.file; 9 10 import std.algorithm; 11 import std.conv; 12 import std.datetime; 13 import std.string; 14 import std.traits; 15 import std.typecons; 16 17 static import std.file; 18 public import std.file : FileException, SpanMode, 19 attrIsDir, attrIsFile, attrIsSymlink; 20 static import std.path; 21 22 import scriptlike.path; 23 24 /// If true, all commands will be echoed. By default, they will be 25 /// echoed to stdout, but you can override this with scriptlikeCustomEcho. 26 bool scriptlikeEcho = false; 27 28 /// Alias for backwards-compatibility. This will be deprecated in the future. 29 /// You should use scriptlikeEcho insetad. 30 alias scriptlikeTraceCommands = scriptlikeEcho; 31 32 /++ 33 If true, then run, tryRun, file write, file append, and all the echoable 34 commands that modify the filesystem will be echoed to stdout (regardless 35 of scriptlikeEcho) and NOT actually executed. 36 37 Warning! This is NOT a "set it and forget it" switch. You must still take 38 care to write your script in a way that's dryrun-safe. Two things to remember: 39 40 1. ONLY Scriptlike's functions will obey this setting. Calling Phobos 41 functions directly will BYPASS this setting. 42 43 2. If part of your script relies on a command having ACTUALLY been run, then 44 that command will fail. You must avoid that situation or work around it. 45 For example: 46 47 --------------------- 48 run(`date > tempfile`); 49 50 // The following will FAIL or behave INCORRECTLY in dryrun mode: 51 auto data = cast(string)read("tempfile"); 52 run("echo "~data); 53 --------------------- 54 55 That may be an unrealistic example, but it demonstrates the problem: Normally, 56 the code above should run fine (at least on posix). But in dryrun mode, 57 "date" will not actually be run. Therefore, tempfile will neither be created 58 nor overwritten. Result: Either an exception reading a non-existent file, 59 or outdated information will be displayed. 60 61 Scriptlike cannot anticipate or handle such situations. So it's up to you to 62 make sure your script is dryrun-safe. 63 +/ 64 bool scriptlikeDryRun = false; 65 66 /++ 67 By default, scriptlikeEcho and scriptlikeDryRun echo to stdout. 68 You can override this behavior by setting scriptlikeCustomEcho to your own 69 sink delegate. Set this to null to go back to Scriptlike's default 70 of "echo to stdout" again. 71 72 Note, setting this does not automatically enable echoing. You still need to 73 set either scriptlikeEcho or scriptlikeDryRun to true. 74 +/ 75 void delegate(string) scriptlikeCustomEcho; 76 77 /// Output a (lazy) string through scriptlike's echo logger. 78 /// Does nothing if scriptlikeEcho and scriptlikeDryRun are both false. 79 void echoCommand(lazy string msg) 80 { 81 import std.stdio; 82 83 if(scriptlikeEcho || scriptlikeDryRun) 84 { 85 if(scriptlikeCustomEcho) 86 scriptlikeCustomEcho(msg); 87 else 88 writeln(msg); 89 } 90 } 91 92 /// Checks if the path exists as a directory. 93 /// 94 /// This is like std.file.isDir, but returns false instead of throwing if the 95 /// path doesn't exist. 96 bool existsAsDir(in string path) @trusted 97 { 98 return std.file.exists(path) && std.file.isDir(path); 99 } 100 ///ditto 101 bool existsAsDir(in Path path) @trusted 102 { 103 return existsAsDir(path.toRawString().to!string()); 104 } 105 106 /// Checks if the path exists as a file. 107 /// 108 /// This is like std.file.isFile, but returns false instead of throwing if the 109 /// path doesn't exist. 110 bool existsAsFile(in string path) @trusted 111 { 112 return std.file.exists(path) && std.file.isFile(path); 113 } 114 ///ditto 115 bool existsAsFile(in Path path) @trusted 116 { 117 return existsAsFile(path.toRawString().to!string()); 118 } 119 120 /// Checks if the path exists as a symlink. 121 /// 122 /// This is like std.file.isSymlink, but returns false instead of throwing if the 123 /// path doesn't exist. 124 bool existsAsSymlink()(in string path) @trusted 125 { 126 return std.file.exists(path) && std.file.isSymlink(path); 127 } 128 ///ditto 129 bool existsAsSymlink(in Path path) @trusted 130 { 131 return existsAsSymlink(path.toRawString().to!string()); 132 } 133 134 // -- std.path wrappers to support Path type, scriptlikeEcho and scriptlikeDryRun -- 135 136 /// Just like std.file.read, but takes a Path. 137 void[] read(in Path name, size_t upTo = size_t.max) 138 { 139 return std.file.read(name.toRawString().to!string(), upTo); 140 } 141 142 /// Just like std.file.readText, but takes a Path. 143 S readText(S = string)(in Path name) 144 { 145 return std.file.readText(name.toRawString().to!string()); 146 } 147 148 /// Just like std.file.write, but optionally takes a Path, 149 /// and obeys scriptlikeEcho and scriptlikeDryRun. 150 void write(in Path name, const void[] buffer) 151 { 152 write(name.toRawString().to!string(), buffer); 153 } 154 155 ///ditto 156 void write(in string name, const void[] buffer) 157 { 158 echoCommand(text("Write ", name)); 159 160 if(!scriptlikeDryRun) 161 std.file.write(name, buffer); 162 } 163 164 /// Just like std.file.append, but optionally takes a Path, 165 /// and obeys scriptlikeEcho and scriptlikeDryRun. 166 void append(in Path name, in void[] buffer) 167 { 168 append(name.toRawString().to!string(), buffer); 169 } 170 171 ///ditto 172 void append(in string name, in void[] buffer) 173 { 174 echoCommand(text("Append ", name)); 175 176 if(!scriptlikeDryRun) 177 std.file.append(name, buffer); 178 } 179 180 /// Just like std.file.rename, but optionally takes Path, 181 /// and obeys scriptlikeEcho and scriptlikeDryRun. 182 void rename(in Path from, in Path to) 183 { 184 rename(from.toRawString().to!string(), to.toRawString().to!string()); 185 } 186 187 ///ditto 188 void rename(in string from, in Path to) 189 { 190 rename(from, to.toRawString().to!string()); 191 } 192 193 ///ditto 194 void rename(in Path from, in string to) 195 { 196 rename(from.toRawString().to!string(), to); 197 } 198 199 ///ditto 200 void rename(in string from, in string to) 201 { 202 echoCommand("rename: "~from.escapeShellArg()~" -> "~to.escapeShellArg()); 203 204 if(!scriptlikeDryRun) 205 std.file.rename(from, to); 206 } 207 208 /// If 'from' exists, then rename. Otherwise do nothing. 209 /// Obeys scriptlikeEcho and scriptlikeDryRun. 210 /// Returns: Success? 211 bool tryRename(T1, T2)(T1 from, T2 to) 212 if( 213 (is(T1==string) || is(T1==Path)) && 214 (is(T2==string) || is(T2==Path)) 215 ) 216 { 217 if(from.exists()) 218 { 219 rename(from, to); 220 return true; 221 } 222 223 return false; 224 } 225 226 /// Just like std.file.remove, but optionally takes a Path, 227 /// and obeys scriptlikeEcho and scriptlikeDryRun. 228 void remove(in Path name) 229 { 230 remove(name.toRawString().to!string()); 231 } 232 233 ///ditto 234 void remove(in string name) 235 { 236 echoCommand("remove: "~name.escapeShellArg()); 237 238 if(!scriptlikeDryRun) 239 std.file.remove(name); 240 } 241 242 /// If 'name' exists, then remove. Otherwise do nothing. 243 /// Obeys scriptlikeEcho and scriptlikeDryRun. 244 /// Returns: Success? 245 bool tryRemove(T)(T name) if(is(T==string) || is(T==Path)) 246 { 247 if(name.exists()) 248 { 249 remove(name); 250 return true; 251 } 252 253 return false; 254 } 255 256 /// Just like std.file.getSize, but takes a Path. 257 ulong getSize(in Path name) 258 { 259 return std.file.getSize(name.toRawString().to!string()); 260 } 261 262 /// Just like std.file.getTimes, but takes a Path. 263 void getTimes(in Path name, 264 out SysTime accessTime, 265 out SysTime modificationTime) 266 { 267 std.file.getTimes(name.toRawString().to!string(), accessTime, modificationTime); 268 } 269 270 version(ddoc_scriptlike_d) 271 { 272 /// Windows-only. Just like std.file.getTimesWin, but takes a Path. 273 void getTimesWin(in Path name, 274 out SysTime fileCreationTime, 275 out SysTime fileAccessTime, 276 out SysTime fileModificationTime); 277 } 278 else version(Windows) void getTimesWin(in Path name, 279 out SysTime fileCreationTime, 280 out SysTime fileAccessTime, 281 out SysTime fileModificationTime) 282 { 283 std.file.getTimesWin(name.toRawString().to!string(), fileCreationTime, fileAccessTime, fileModificationTime); 284 } 285 286 /// Just like std.file.setTimes, but optionally takes a Path, 287 /// and obeys scriptlikeEcho and scriptlikeDryRun. 288 void setTimes(in Path name, 289 SysTime accessTime, 290 SysTime modificationTime) 291 { 292 setTimes(name.toRawString().to!string(), accessTime, modificationTime); 293 } 294 295 ///ditto 296 void setTimes(in string name, 297 SysTime accessTime, 298 SysTime modificationTime) 299 { 300 echoCommand(text( 301 "setTimes: ", name.escapeShellArg(), 302 " Accessed ", accessTime, "; Modified ", modificationTime 303 )); 304 305 if(!scriptlikeDryRun) 306 std.file.setTimes(name, accessTime, modificationTime); 307 } 308 309 /// Just like std.file.timeLastModified, but takes a Path. 310 SysTime timeLastModified(in Path name) 311 { 312 return std.file.timeLastModified(name.toRawString().to!string()); 313 } 314 315 /// Just like std.file.timeLastModified, but takes a Path. 316 SysTime timeLastModified(in Path name, SysTime returnIfMissing) 317 { 318 return std.file.timeLastModified(name.toRawString().to!string(), returnIfMissing); 319 } 320 321 /// Just like std.file.exists, but takes a Path. 322 bool exists(in Path name) @trusted 323 { 324 return std.file.exists(name.toRawString().to!string()); 325 } 326 327 /// Just like std.file.getAttributes, but takes a Path. 328 uint getAttributes(in Path name) 329 { 330 return std.file.getAttributes(name.toRawString().to!string()); 331 } 332 333 /// Just like std.file.getLinkAttributes, but takes a Path. 334 uint getLinkAttributes(in Path name) 335 { 336 return std.file.getLinkAttributes(name.toRawString().to!string()); 337 } 338 339 /// Just like std.file.isDir, but takes a Path. 340 @property bool isDir(in Path name) 341 { 342 return std.file.isDir(name.toRawString().to!string()); 343 } 344 345 /// Just like std.file.isFile, but takes a Path. 346 @property bool isFile(in Path name) 347 { 348 return std.file.isFile(name.toRawString().to!string()); 349 } 350 351 /// Just like std.file.isSymlink, but takes a Path. 352 @property bool isSymlink(Path name) 353 { 354 return std.file.isSymlink(name.toRawString().to!string()); 355 } 356 357 /// Just like std.file.getcwd, but returns a Path. 358 Path getcwd() 359 { 360 return Path( std.file.getcwd() ); 361 } 362 363 /// Just like std.file.chdir, but takes a Path, and echoes if scriptlikeEcho is true. 364 void chdir(in Path pathname) 365 { 366 chdir(pathname.toRawString().to!string()); 367 } 368 369 /// Just like std.file.chdir, but echoes if scriptlikeEcho is true. 370 void chdir(in string pathname) 371 { 372 echoCommand("chdir: "~pathname.escapeShellArg()); 373 std.file.chdir(pathname); 374 } 375 376 /// Just like std.file.mkdir, but optionally takes a Path, 377 /// and obeys scriptlikeEcho and scriptlikeDryRun. 378 void mkdir(in Path pathname) 379 { 380 mkdir(pathname.toRawString().to!string()); 381 } 382 383 ///ditto 384 void mkdir(in string pathname) 385 { 386 echoCommand("mkdir: "~pathname.escapeShellArg()); 387 388 if(!scriptlikeDryRun) 389 std.file.mkdir(pathname); 390 } 391 392 /// If 'name' doesn't already exist, then mkdir. Otherwise do nothing. 393 /// Obeys scriptlikeEcho and scriptlikeDryRun. 394 /// Returns: Success? 395 bool tryMkdir(T)(T name) if(is(T==string) || is(T==Path)) 396 { 397 if(!name.exists()) 398 { 399 mkdir(name); 400 return true; 401 } 402 403 return false; 404 } 405 406 /// Just like std.file.mkdirRecurse, but optionally takes a Path, 407 /// and obeys scriptlikeEcho and scriptlikeDryRun. 408 void mkdirRecurse(in Path pathname) 409 { 410 mkdirRecurse(pathname.toRawString().to!string()); 411 } 412 413 ///ditto 414 void mkdirRecurse(in string pathname) 415 { 416 echoCommand("mkdirRecurse: "~pathname.escapeShellArg()); 417 418 if(!scriptlikeDryRun) 419 std.file.mkdirRecurse(pathname); 420 } 421 422 /// If 'name' doesn't already exist, then mkdirRecurse. Otherwise do nothing. 423 /// Obeys scriptlikeEcho and scriptlikeDryRun. 424 /// Returns: Success? 425 bool tryMkdirRecurse(T)(T name) if(is(T==string) || is(T==Path)) 426 { 427 if(!name.exists()) 428 { 429 mkdirRecurse(name); 430 return true; 431 } 432 433 return false; 434 } 435 436 /// Just like std.file.rmdir, but optionally takes a Path, 437 /// and obeys scriptlikeEcho and scriptlikeDryRun. 438 void rmdir(in Path pathname) 439 { 440 rmdir(pathname.toRawString().to!string()); 441 } 442 443 ///ditto 444 void rmdir(in string pathname) 445 { 446 echoCommand("rmdir: "~pathname.escapeShellArg()); 447 448 if(!scriptlikeDryRun) 449 std.file.rmdir(pathname); 450 } 451 452 /// If 'name' exists, then rmdir. Otherwise do nothing. 453 /// Obeys scriptlikeEcho and scriptlikeDryRun. 454 /// Returns: Success? 455 bool tryRmdir(T)(T name) if(is(T==string) || is(T==Path)) 456 { 457 if(name.exists()) 458 { 459 rmdir(name); 460 return true; 461 } 462 463 return false; 464 } 465 466 version(ddoc_scriptlike_d) 467 { 468 /// Posix-only. Just like std.file.symlink, but optionally takes Path, 469 /// and obeys scriptlikeEcho and scriptlikeDryRun. 470 void symlink(Path original, Path link); 471 472 ///ditto 473 void symlink(string original, Path link); 474 475 ///ditto 476 void symlink(Path original, string link); 477 478 ///ditto 479 void symlink(string original, string link); 480 481 /// Posix-only. If 'original' exists, then symlink. Otherwise do nothing. 482 /// Obeys scriptlikeEcho and scriptlikeDryRun. 483 /// Returns: Success? 484 bool trySymlink(T1, T2)(T1 original, T2 link) 485 if( 486 (is(T1==string) || is(T1==Path)) && 487 (is(T2==string) || is(T2==Path)) 488 ); 489 490 /// Posix-only. Just like std.file.readLink, but operates on Path. 491 Path readLink(Path link); 492 } 493 else version(Posix) 494 { 495 void symlink(Path original, Path link) 496 { 497 symlink(original.toRawString().to!string(), link.toRawString().to!string()); 498 } 499 500 void symlink(string original, Path link) 501 { 502 symlink(original, link.toRawString().to!string()); 503 } 504 505 void symlink(Path original, string link) 506 { 507 symlink(original.toRawString().to!string(), link); 508 } 509 510 void symlink(string original, string link) 511 { 512 echoCommand("symlink: [original] "~original.escapeShellArg()~" : [symlink] "~link.escapeShellArg()); 513 514 if(!scriptlikeDryRun) 515 std.file.symlink(original, link); 516 } 517 518 bool trySymlink(T1, T2)(T1 original, T2 link) 519 if( 520 (is(T1==string) || is(T1==Path)) && 521 (is(T2==string) || is(T2==Path)) 522 ) 523 { 524 if(original.exists()) 525 { 526 symlink(original, link); 527 return true; 528 } 529 530 return false; 531 } 532 533 Path readLink(Path link) 534 { 535 return Path( std.file.readLink(link.toRawString().to!string()) ); 536 } 537 } 538 539 /// Just like std.file.copy, but optionally takes Path, 540 /// and obeys scriptlikeEcho and scriptlikeDryRun. 541 void copy(in Path from, in Path to) 542 { 543 copy(from.toRawString().to!string(), to.toRawString().to!string()); 544 } 545 546 ///ditto 547 void copy(in string from, in Path to) 548 { 549 copy(from, to.toRawString().to!string()); 550 } 551 552 ///ditto 553 void copy(in Path from, in string to) 554 { 555 copy(from.toRawString().to!string(), to); 556 } 557 558 ///ditto 559 void copy(in string from, in string to) 560 { 561 echoCommand("copy: "~from.escapeShellArg()~" -> "~to.escapeShellArg()); 562 563 if(!scriptlikeDryRun) 564 std.file.copy(from, to); 565 } 566 567 /// If 'from' exists, then copy. Otherwise do nothing. 568 /// Obeys scriptlikeEcho and scriptlikeDryRun. 569 /// Returns: Success? 570 bool tryCopy(T1, T2)(T1 from, T2 to) 571 if( 572 (is(T1==string) || is(T1==Path)) && 573 (is(T2==string) || is(T2==Path)) 574 ) 575 { 576 if(from.exists()) 577 { 578 copy(from, to); 579 return true; 580 } 581 582 return false; 583 } 584 585 /// Just like std.file.rmdirRecurse, but optionally takes a Path, 586 /// and obeys scriptlikeEcho and scriptlikeDryRun. 587 void rmdirRecurse(in Path pathname) 588 { 589 rmdirRecurse(pathname.toRawString().to!string()); 590 } 591 592 ///ditto 593 void rmdirRecurse(in string pathname) 594 { 595 echoCommand("rmdirRecurse: "~pathname.escapeShellArg()); 596 597 if(!scriptlikeDryRun) 598 std.file.rmdirRecurse(pathname); 599 } 600 601 /// If 'name' exists, then rmdirRecurse. Otherwise do nothing. 602 /// Obeys scriptlikeEcho and scriptlikeDryRun. 603 /// Returns: Success? 604 bool tryRmdirRecurse(T)(T name) if(is(T==string) || is(T==Path)) 605 { 606 if(name.exists()) 607 { 608 rmdirRecurse(name); 609 return true; 610 } 611 612 return false; 613 } 614 615 /// Just like std.file.dirEntries, but takes a Path. 616 auto dirEntries(Path path, SpanMode mode, bool followSymlink = true) 617 { 618 return std.file.dirEntries(path.toRawString().to!string(), mode, followSymlink); 619 } 620 621 /// Just like std.file.dirEntries, but takes a Path. 622 auto dirEntries(Path path, string pattern, SpanMode mode, 623 bool followSymlink = true) 624 { 625 return std.file.dirEntries(path.toRawString().to!string(), pattern, mode, followSymlink); 626 } 627 628 /// Just like std.file.slurp, but takes a Path. 629 template slurp(Types...) 630 { 631 auto slurp(Path filename, in string format) 632 { 633 return std.file.slurp!Types(filename.toRawString().to!string(), format); 634 } 635 } 636 637 /// Just like std.file.thisExePath, but returns a Path. 638 @trusted Path thisExePath() 639 { 640 return Path( std.file.thisExePath() ); 641 } 642 643 /// Just like std.file.tempDir, but returns a Path. 644 @trusted Path tempDir() 645 { 646 return Path( std.file.tempDir() ); 647 } 648 649 version(unittest_scriptlike_d) 650 unittest 651 { 652 // std.file.slurp seems to randomly trigger an internal std.algorithm 653 // assert failure on DMD 2.064.2, so don't test it there. Seems 654 // to be fixed in DMD 2.065. 655 static import std.compiler; 656 static if( 657 std.compiler.vendor == std.compiler.Vendor.digitalMars || 658 (std.compiler.version_major == 2 && std.compiler.version_minor == 64) 659 ) 660 enum testSlurp = false; 661 else 662 enum testSlurp = true; 663 664 import std.stdio : writeln; 665 import std.process : thisProcessID; 666 import core.thread; 667 alias copy = scriptlike.path.copy; 668 669 writeln("Running Scriptlike unittests: std.file wrappers"); 670 671 immutable tempname = std.path.buildPath(std.file.tempDir(), "deleteme.script like.unit test.pid" ~ to!string(thisProcessID)); 672 immutable tempname2 = std.path.buildPath(std.file.tempDir(), "deleteme.script like.unit test2.pid" ~ to!string(thisProcessID)); 673 immutable tempname3 = std.path.buildPath(std.file.tempDir(), "deleteme.script like.unit test3.pid" ~ to!string(thisProcessID), "somefile"); 674 auto tempPath = Path(tempname); 675 auto tempPath2 = Path(tempname2); 676 auto tempPath3 = Path(tempname3); 677 assert(!std.file.exists(tempname)); 678 assert(!std.file.exists(tempname2)); 679 assert(!std.file.exists( std.path.dirName(tempname3) )); 680 assert(!std.file.exists(tempname3)); 681 682 { 683 scope(exit) 684 { 685 if(std.file.exists(tempname)) std.file.remove(tempname); 686 } 687 688 tempPath.write("stuff"); 689 690 tempPath.append(" more"); 691 assert(tempPath.read(3) == "stu"); 692 assert(tempPath.read() == "stuff more"); 693 assert(tempPath.readText() == "stuff more"); 694 assert(tempPath.getSize() == 10); 695 696 if(testSlurp) 697 { 698 auto parsed = tempPath.slurp!(string, string)("%s %s"); 699 assert(equal(parsed, [tuple("stuff", "more")])); 700 } 701 702 SysTime timeA, timeB, timeC; 703 tempPath.getTimes(timeA, timeB); 704 version(Windows) 705 tempPath.getTimesWin(timeA, timeB, timeC); 706 tempPath.setTimes(timeA, timeB); 707 timeA = tempPath.timeLastModified(); 708 timeA = tempPath.timeLastModified(timeB); 709 710 uint attr; 711 attr = tempPath.getAttributes(); 712 attr = tempPath.getLinkAttributes(); 713 714 assert(tempPath.exists()); 715 assert(tempPath.isFile()); 716 assert(tempPath.existsAsFile()); 717 assert(!tempPath.isDir()); 718 assert(!tempPath.existsAsDir()); 719 assert(!tempPath.isSymlink()); 720 assert(!tempPath.existsAsSymlink()); 721 tempPath.remove(); 722 assert(!tempPath.exists()); 723 assert(!tempPath.existsAsFile()); 724 assert(!tempPath.existsAsDir()); 725 assert(!tempPath.existsAsSymlink()); 726 } 727 728 { 729 assert(!tempPath.exists()); 730 assert(!tempPath2.exists()); 731 732 scope(exit) 733 { 734 if(std.file.exists(tempname)) std.file.remove(tempname); 735 if(std.file.exists(tempname2)) std.file.remove(tempname2); 736 } 737 tempPath.write("ABC"); 738 739 assert(tempPath.existsAsFile()); 740 assert(!tempPath2.exists()); 741 742 tempPath.rename(tempPath2); 743 744 assert(!tempPath.exists()); 745 assert(tempPath2.existsAsFile()); 746 747 tempPath2.copy(tempPath); 748 749 assert(tempPath.existsAsFile()); 750 assert(tempPath2.existsAsFile()); 751 } 752 753 { 754 scope(exit) 755 { 756 if(std.file.exists(tempname)) std.file.rmdir(tempname); 757 if(std.file.exists(tempname3)) std.file.rmdir(tempname3); 758 if(std.file.exists( std.path.dirName(tempname3) )) std.file.rmdir( std.path.dirName(tempname3) ); 759 } 760 761 assert(!tempPath.exists()); 762 assert(!tempPath3.exists()); 763 764 tempPath.mkdir(); 765 assert(tempPath.exists()); 766 assert(!tempPath.isFile()); 767 assert(!tempPath.existsAsFile()); 768 assert(tempPath.isDir()); 769 assert(tempPath.existsAsDir()); 770 assert(!tempPath.isSymlink()); 771 assert(!tempPath.existsAsSymlink()); 772 773 tempPath3.mkdirRecurse(); 774 assert(tempPath3.exists()); 775 assert(!tempPath3.isFile()); 776 assert(!tempPath3.existsAsFile()); 777 assert(tempPath3.isDir()); 778 assert(tempPath3.existsAsDir()); 779 assert(!tempPath3.isSymlink()); 780 assert(!tempPath3.existsAsSymlink()); 781 782 auto saveDirName = std.file.getcwd(); 783 auto saveDir = Path(saveDirName); 784 scope(exit) chdir(saveDirName); 785 786 tempPath.chdir(); 787 assert(getcwd() == tempname); 788 saveDir.chdir(); 789 assert(getcwd() == saveDirName); 790 791 auto entries1 = (tempPath3~"..").dirEntries(SpanMode.shallow); 792 assert(!entries1.empty); 793 auto entries2 = (tempPath3~"..").dirEntries("*", SpanMode.shallow); 794 assert(!entries2.empty); 795 auto entries3 = (tempPath3~"..").dirEntries("TUNA TUNA THIS DOES NOT EXIST TUNA WHEE", SpanMode.shallow); 796 assert(entries3.empty); 797 798 tempPath.rmdir(); 799 assert(!tempPath.exists()); 800 assert(!tempPath.existsAsFile()); 801 assert(!tempPath.existsAsDir()); 802 assert(!tempPath.existsAsSymlink()); 803 804 tempPath3.rmdirRecurse(); 805 assert(!tempPath.exists()); 806 assert(!tempPath.existsAsFile()); 807 assert(!tempPath.existsAsDir()); 808 assert(!tempPath.existsAsSymlink()); 809 } 810 811 { 812 version(Posix) 813 { 814 assert(!tempPath.exists()); 815 assert(!tempPath2.exists()); 816 817 scope(exit) 818 { 819 if(std.file.exists(tempname2)) std.file.remove(tempname2); 820 if(std.file.exists(tempname)) std.file.remove(tempname); 821 } 822 tempPath.write("DEF"); 823 824 tempPath.symlink(tempPath2); 825 assert(tempPath2.exists()); 826 assert(tempPath2.isFile()); 827 assert(tempPath2.existsAsFile()); 828 assert(!tempPath2.isDir()); 829 assert(!tempPath2.existsAsDir()); 830 assert(tempPath2.isSymlink()); 831 assert(tempPath2.existsAsSymlink()); 832 833 auto linkTarget = tempPath2.readLink(); 834 assert(linkTarget.toRawString() == tempname); 835 } 836 } 837 838 { 839 assert(!tempPath.exists()); 840 841 scope(exit) 842 { 843 if(std.file.exists(tempname)) std.file.remove(tempname); 844 } 845 846 import scriptlike.process; 847 run(`echo TestScriptStuff > `~tempPath.to!string()); 848 assert(tempPath.exists()); 849 assert(tempPath.isFile()); 850 assert((cast(string)tempPath.read()).strip() == "TestScriptStuff"); 851 tempPath.remove(); 852 assert(!tempPath.exists()); 853 854 auto errlevel = tryRun(`echo TestScriptStuff > `~tempPath.to!string()); 855 assert(tempPath.exists()); 856 assert(tempPath.isFile()); 857 assert((cast(string)tempPath.read()).strip() == "TestScriptStuff"); 858 assert(errlevel == 0); 859 tempPath.remove(); 860 assert(!tempPath.exists()); 861 862 import scriptlike.process; 863 getcwd().run(`echo TestScriptStuff > `~tempPath.to!string()); 864 getcwd().tryRun(`echo TestScriptStuff > `~tempPath.to!string()); 865 } 866 867 { 868 assert(!tempPath3.exists()); 869 assert(!tempPath3.up.exists()); 870 871 scope(exit) 872 { 873 if(std.file.exists(tempname3)) std.file.remove(tempname3); 874 if(std.file.exists( std.path.dirName(tempname3) )) std.file.rmdir( std.path.dirName(tempname3) ); 875 } 876 877 tempPath3.up.mkdir(); 878 assert(tempPath3.up.exists()); 879 assert(tempPath3.up.isDir()); 880 881 import scriptlike.process; 882 tempPath3.up.run(`echo MoreTestStuff > `~tempPath3.baseName().to!string()); 883 assert(tempPath3.exists()); 884 assert(tempPath3.isFile()); 885 assert((cast(string)tempPath3.read()).strip() == "MoreTestStuff"); 886 } 887 888 { 889 scope(exit) 890 { 891 if(std.file.exists(tempname)) std.file.rmdir(tempname); 892 if(std.file.exists(tempname3)) std.file.rmdir(tempname3); 893 if(std.file.exists( std.path.dirName(tempname3) )) std.file.rmdir( std.path.dirName(tempname3) ); 894 } 895 896 assert(!tempPath.exists()); 897 assert(!tempPath3.exists()); 898 899 assert(!tempPath.tryRmdir()); 900 assert(!tempPath.tryRmdirRecurse()); 901 assert(!tempPath.tryRemove()); 902 assert(!tempPath.tryRename(tempPath3)); 903 version(Posix) assert(!tempPath.trySymlink(tempPath3)); 904 assert(!tempPath.tryCopy(tempPath3)); 905 906 assert(tempPath.tryMkdir()); 907 assert(tempPath.exists()); 908 assert(!tempPath.tryMkdir()); 909 assert(!tempPath.tryMkdirRecurse()); 910 911 assert(tempPath.tryRmdir()); 912 assert(!tempPath.exists()); 913 914 assert(tempPath.tryMkdirRecurse()); 915 assert(tempPath.exists()); 916 assert(!tempPath.tryMkdirRecurse()); 917 } 918 }