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 Path.current.should.equal(root.join("my-project")); 48 49 // Ensure that we can find path to config 50 auto config1 = Path.current.searchFileUp("my-conf.conf"); 51 config1.isNull.should.be(false); 52 config1.get.readFileText.should.equal("name = My Project"); 53 54 // Let's change directory to 'some-sub-dir' inside our project, 55 // and try to find our config again 56 root.chdir("my-project", "some-dir", "some-sub-dir"); 57 58 // Ensure that current directory now is my-project/some-dir/some-sub-dir 59 Path.current.should.equal( 60 root.join("my-project", "some-dir", "some-sub-dir")); 61 62 // Ensure that we can find path to config even if we someshere deep inside 63 // our project tree 64 auto config2 = Path.current.searchFileUp("my-conf.conf"); 65 config2.isNull.should.be(false); 66 config2.get.readFileText.should.equal("name = My Project"); 67 } 68 69 70 /// Example of using nullable paths as function parameters 71 unittest { 72 import dshould; 73 74 import std.typecons: Nullable, nullable; 75 76 /* simple function, that will join 'test.conf' to provided path 77 * if provided path is not null, and return null path is provided path 78 * is null 79 */ 80 Nullable!Path test_path_fn(in Nullable!Path p) { 81 if (p.isNull) 82 return Nullable!Path.init; 83 return p.get.join("test.conf").nullable; 84 } 85 86 // Pass value to nullable param 87 auto const p1 = test_path_fn(Path("hello").nullable); 88 p1.isNull.should.be(false); 89 p1.get.segments.should.equal(["hello", "test.conf"]); 90 91 // Pass null to nullable param 92 auto const p2 = test_path_fn(Nullable!Path.init); 93 p2.isNull.should.be(true); 94 } 95 96 97 /// Example of using paths in structs 98 unittest { 99 import dshould; 100 101 struct PStruct { 102 string name; 103 Path path; 104 105 bool check() const { 106 return path.exists; 107 } 108 } 109 110 PStruct p; 111 112 p.name = "test"; 113 114 // Attempt to run operation on uninitialized path will throw error 115 import core.exception: AssertError; 116 p.check.should.throwA!AssertError; 117 118 // Let's initialize path and check it again 119 p.path = Path("some-unexisting-path-to-magic-file"); 120 p.check.should.be(false); 121 }