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 }