1 /** ThePath - easy way to work with paths and files 2 * 3 * Yet another attempt to implement high-level object-oriented interface 4 * to manage path and files in D. 5 * Inspired by [Python's pathlib](https://docs.python.org/3/library/pathlib.html) 6 * and [D port of pathlib](https://code.dlang.org/packages/pathlib) but 7 * implementing it in different way. 8 * 9 **/ 10 module thepath; 11 12 public import thepath.path: Path; 13 public import thepath.utils: createTempDirectory, createTempPath; 14 public import thepath.exception: PathException; 15 16 /// Example to find configuration of current project 17 unittest { 18 import dshould; 19 20 Path root = createTempPath(); 21 scope(exit) root.remove(); 22 23 // Save current directory 24 auto const cdir = Path.current; 25 scope(exit) cdir.chdir; 26 27 // Create directory structure 28 root.join("my-project", "some-dir", "some-sub-dir").mkdir(true); 29 root.join("my-project", "utils", "some-utility").mkdir(true); 30 root.join("my-project", "tools", "tool42", "s31").mkdir(true); 31 32 // Create some project config file 33 root.join("my-project", "my-conf.conf").writeFile("name = My Project"); 34 35 // Let's change current working directory to test root 36 root.chdir; 37 38 // Let's try to find project config, and expect that no config found, 39 // because our current working directory is not inside project 40 Path.current.searchFileUp("my-conf.conf").isNull.should.be(true); 41 42 // Let's change directory to our project directory, 43 // and try to find our config 44 root.chdir("my-project"); 45 46 // Ensure that current directory now is my-project 47 version(OSX) { 48 Path.current.should.equal( 49 root.join("my-project").realPath); 50 } else { 51 Path.current.should.equal(root.join("my-project")); 52 } 53 54 // Ensure that we can find path to config 55 auto config1 = Path.current.searchFileUp("my-conf.conf"); 56 config1.isNull.should.be(false); 57 config1.get.readFileText.should.equal("name = My Project"); 58 59 // Let's change directory to 'some-sub-dir' inside our project, 60 // and try to find our config again 61 root.chdir("my-project", "some-dir", "some-sub-dir"); 62 63 // Ensure that current directory now is my-project/some-dir/some-sub-dir 64 version(OSX) { 65 Path.current.should.equal( 66 root.join("my-project", "some-dir", "some-sub-dir").realPath); 67 } else { 68 Path.current.should.equal( 69 root.join("my-project", "some-dir", "some-sub-dir")); 70 } 71 72 // Ensure that we can find path to config even if we someshere deep inside 73 // our project tree 74 auto config2 = Path.current.searchFileUp("my-conf.conf"); 75 config2.isNull.should.be(false); 76 config2.get.readFileText.should.equal("name = My Project"); 77 } 78 79 80 /// Example of using nullable paths as function parameters 81 unittest { 82 import dshould; 83 84 import std.typecons: Nullable, nullable; 85 86 /* simple function, that will join 'test.conf' to provided path 87 * if provided path is not null, and return null path is provided path 88 * is null 89 */ 90 Nullable!Path test_path_fn(in Nullable!Path p) { 91 if (p.isNull) 92 return Nullable!Path.init; 93 return p.get.join("test.conf").nullable; 94 } 95 96 // Pass value to nullable param 97 auto const p1 = test_path_fn(Path("hello").nullable); 98 p1.isNull.should.be(false); 99 p1.get.segments.should.equal(["hello", "test.conf"]); 100 101 // Pass null to nullable param 102 auto const p2 = test_path_fn(Nullable!Path.init); 103 p2.isNull.should.be(true); 104 } 105 106 107 /// Example of using paths in structs 108 unittest { 109 import dshould; 110 111 struct PStruct { 112 string name; 113 Path path; 114 115 bool check() const { 116 return path.exists; 117 } 118 } 119 120 PStruct p; 121 122 p.name = "test"; 123 124 // Attempt to run operation on uninitialized path will throw error 125 import core.exception: AssertError; 126 p.check.should.throwA!AssertError; 127 128 // Let's initialize path and check it again 129 p.path = Path("some-unexisting-path-to-magic-file"); 130 p.check.should.be(false); 131 }