1 /++
2 $(H2 Scriptlike $(SCRIPTLIKE_VERSION))
3 
4 Wrappers for $(MODULE_STD_FILE) that add support for Scriptlike's
5 $(API_PATH_EXTR Path), command echoing and dry-run features.
6 
7 Copyright: Copyright (C) 2014-2017 Nick Sabalausky
8 License:   zlib/libpng
9 Authors:   Nick Sabalausky
10 +/
11 module scriptlike.file.wrappers;
12 
13 import std.algorithm;
14 import std.conv;
15 import std.datetime;
16 import std..string;
17 import std.traits;
18 import std.typecons;
19 
20 static import std.file;
21 public import std.file : FileException, SpanMode,
22 	attrIsDir, attrIsFile, attrIsSymlink;
23 static import std.path;
24 
25 import scriptlike.core;
26 import scriptlike.path.extras;
27 
28 /// Like $(FULL_STD_FILE read), but supports Path and command echoing.
29 void[] read(in Path name, size_t upTo = size_t.max)
30 {
31 	return read(name.raw, upTo);
32 }
33 
34 ///ditto
35 void[] read(in string name, size_t upTo = size_t.max)
36 {
37 	yapFunc(name);
38 	return std.file.read(name, upTo);
39 }
40 
41 /// Alias of read, included to provide naming symmetry with writeFile, which
42 /// helps avoid naming conflicts with
43 /// $(D_INLINECODE $(LINK2 http://dlang.org/phobos/std_stdio.html#.write, std.stdio.write)).
44 alias readFile = read;
45 
46 version(unittest_scriptlike_d)
47 unittest
48 {
49 	string file;
50 
51 	testFileOperation!("read", "string")(() {
52 		mixin(useTmpName!"file");
53 		std.file.write(file, "abc123");
54 
55 		assert(cast(string) read(file) == "abc123");
56 	});
57 
58 	testFileOperation!("read", "Path")(() {
59 		mixin(useTmpName!"file");
60 		std.file.write(file, "abc123");
61 
62 		assert(cast(string) read(Path(file)) == "abc123");
63 	});
64 }
65 
66 /// Like $(FULL_STD_FILE readText), but supports Path and command echoing.
67 S readText(S = string)(in Path name)
68 {
69 	return readText(name.raw);
70 }
71 
72 ///ditto
73 S readText(S = string)(in string name)
74 {
75 	yapFunc(name);
76 	return std.file.readText(name);
77 }
78 
79 version(unittest_scriptlike_d)
80 unittest
81 {
82 	string file;
83 
84 	testFileOperation!("readText", "string")(() {
85 		mixin(useTmpName!"file");
86 		std.file.write(file, "abc123");
87 
88 		assert(cast(string) readText(file) == "abc123");
89 	});
90 
91 	testFileOperation!("readText", "Path")(() {
92 		mixin(useTmpName!"file");
93 		std.file.write(file, "abc123");
94 
95 		assert(cast(string) readText(Path(file)) == "abc123");
96 	});
97 }
98 
99 /// Like $(FULL_STD_FILE write), but supports Path, command echoing and dryrun.
100 ///
101 /// To avoid naming conflicts with
102 /// $(D_INLINECODE $(LINK2 http://dlang.org/phobos/std_stdio.html#.write, std.stdio.write)),
103 /// you may wish to use the writeFile alias instead. A readFile is also provided
104 /// for symmetry with writeFile.
105 void write(in Path name, const void[] buffer)
106 {
107 	write(name.raw, buffer);
108 }
109 
110 ///ditto
111 void write(in string name, const void[] buffer)
112 {
113 	yapFunc(name.escapeShellArg());
114 	
115 	if(!scriptlikeDryRun)
116 		std.file.write(name, buffer);
117 }
118 
119 /// Alias of write to help avoid naming conflicts with
120 /// $(D_INLINECODE $(LINK2 http://dlang.org/phobos/std_stdio.html#.write, std.stdio.write)).
121 /// A readFile is also provided for symmetry with writeFile.
122 alias writeFile = write;
123 
124 version(unittest_scriptlike_d)
125 unittest
126 {
127 	string file;
128 	void checkPre()
129 	{
130 		assert(!std.file.exists(file));
131 	}
132 
133 	void checkPost()
134 	{
135 		assert(std.file.exists(file));
136 		assert(std.file.isFile(file));
137 		assert(cast(string) std.file.read(file) == "abc123");
138 	}
139 
140 	// Create
141 	testFileOperation!("write", "Create: string")(() {
142 		mixin(useTmpName!"file");
143 
144 		checkPre();
145 		write(file, "abc123");
146 		mixin(checkResult);
147 	});
148 
149 	testFileOperation!("write", "Create: Path")(() {
150 		mixin(useTmpName!"file");
151 
152 		checkPre();
153 		write(Path(file), "abc123");
154 		mixin(checkResult);
155 	});
156 
157 	// Overwrite
158 	testFileOperation!("write", "Overwrite: string")(() {
159 		mixin(useTmpName!"file");
160 
161 		checkPre();
162 		write(file, "hello");
163 		write(file, "abc123");
164 		mixin(checkResult);
165 	});
166 
167 	testFileOperation!("write", "Overwrite: Path")(() {
168 		mixin(useTmpName!"file");
169 
170 		checkPre();
171 		write(Path(file), "hello");
172 		write(Path(file), "abc123");
173 		mixin(checkResult);
174 	});
175 }
176 
177 /// Like $(FULL_STD_FILE append), but supports Path, command echoing and dryrun.
178 void append(in Path name, in void[] buffer)
179 {
180 	append(name.raw, buffer);
181 }
182 
183 ///ditto
184 void append(in string name, in void[] buffer)
185 {
186 	yapFunc(name.escapeShellArg());
187 
188 	if(!scriptlikeDryRun)
189 		std.file.append(name, buffer);
190 }
191 
192 version(unittest_scriptlike_d)
193 unittest
194 {
195 	string file;
196 	void checkPre()
197 	{
198 		assert(std.file.exists(file));
199 		assert(std.file.isFile(file));
200 		assert(cast(string) std.file.read(file) == "abc123");
201 	}
202 
203 	void checkPost()
204 	{
205 		assert(std.file.exists(file));
206 		assert(std.file.isFile(file));
207 		assert(cast(string) std.file.read(file) == "abc123hello");
208 	}
209 
210 	testFileOperation!("append", "string")(() {
211 		mixin(useTmpName!"file");
212 		std.file.write(file, "abc123");
213 
214 		checkPre();
215 		append(file, "hello");
216 		mixin(checkResult);
217 	});
218 
219 	testFileOperation!("append", "Path")(() {
220 		mixin(useTmpName!"file");
221 		std.file.write(file, "abc123");
222 
223 		checkPre();
224 		append(Path(file), "hello");
225 		mixin(checkResult);
226 	});
227 }
228 
229 /// Like $(FULL_STD_FILE rename), but supports Path, command echoing and dryrun.
230 void rename(in Path from, in Path to)
231 {
232 	rename(from.raw, to.raw);
233 }
234 
235 ///ditto
236 void rename(in string from, in Path to)
237 {
238 	rename(from, to.raw);
239 }
240 
241 ///ditto
242 void rename(in Path from, in string to)
243 {
244 	rename(from.raw, to);
245 }
246 
247 ///ditto
248 void rename(in string from, in string to)
249 {
250 	yapFunc(from.escapeShellArg(), " -> ", to.escapeShellArg());
251 
252 	if(!scriptlikeDryRun)
253 		std.file.rename(from, to);
254 }
255 
256 version(unittest_scriptlike_d)
257 unittest
258 {
259 	string file1;
260 	string file2;
261 	void checkPre()
262 	{
263 		assert(!std.file.exists(file2));
264 		assert(std.file.exists(file1));
265 		assert(std.file.isFile(file1));
266 		assert(cast(string) std.file.read(file1) == "abc");
267 	}
268 
269 	void checkPost()
270 	{
271 		assert(!std.file.exists(file1));
272 		assert(std.file.exists(file2));
273 		assert(std.file.isFile(file2));
274 		assert(cast(string) std.file.read(file2) == "abc");
275 	}
276 
277 	testFileOperation!("rename", "string,string")(() {
278 		mixin(useTmpName!"file1");
279 		mixin(useTmpName!"file2");
280 		std.file.write(file1, "abc");
281 
282 		checkPre();
283 		rename(file1, file2);
284 		mixin(checkResult);
285 	});
286 
287 	testFileOperation!("rename", "string,Path")(() {
288 		mixin(useTmpName!"file1");
289 		mixin(useTmpName!"file2");
290 		std.file.write(file1, "abc");
291 
292 		checkPre();
293 		rename(file1, Path(file2));
294 		mixin(checkResult);
295 	});
296 
297 	testFileOperation!("rename", "Path,string")(() {
298 		mixin(useTmpName!"file1");
299 		mixin(useTmpName!"file2");
300 		std.file.write(file1, "abc");
301 
302 		checkPre();
303 		rename(Path(file1), file2);
304 		mixin(checkResult);
305 	});
306 
307 	testFileOperation!("rename", "Path,Path")(() {
308 		mixin(useTmpName!"file1");
309 		mixin(useTmpName!"file2");
310 		std.file.write(file1, "abc");
311 
312 		checkPre();
313 		rename(Path(file1), Path(file2));
314 		mixin(checkResult);
315 	});
316 }
317 
318 /// Like $(FULL_STD_FILE remove), but supports Path, command echoing and dryrun.
319 void remove(in Path name)
320 {
321 	remove(name.raw);
322 }
323 
324 ///ditto
325 void remove(in string name)
326 {
327 	yapFunc(name.escapeShellArg());
328 
329 	if(!scriptlikeDryRun)
330 		std.file.remove(name);
331 }
332 
333 version(unittest_scriptlike_d)
334 unittest
335 {
336 	string file;
337 	void checkPre()
338 	{
339 		assert(std.file.exists(file));
340 		assert(std.file.isFile(file));
341 		assert(cast(string) std.file.read(file) == "abc");
342 	}
343 
344 	void checkPost()
345 	{
346 		assert(!std.file.exists(file));
347 	}
348 
349 	testFileOperation!("remove", "string")(() {
350 		mixin(useTmpName!"file");
351 		std.file.write(file, "abc");
352 
353 		checkPre();
354 		remove(file);
355 		mixin(checkResult);
356 	});
357 
358 	testFileOperation!("remove", "Path")(() {
359 		mixin(useTmpName!"file");
360 		std.file.write(file, "abc");
361 
362 		checkPre();
363 		remove(Path(file));
364 		mixin(checkResult);
365 	});
366 }
367 
368 /// Like $(FULL_STD_FILE getSize), but supports Path and command echoing.
369 ulong getSize(in Path name)
370 {
371 	return getSize(name.raw);
372 }
373 
374 ///ditto
375 ulong getSize(in string name)
376 {
377 	yapFunc(name);
378 	return std.file.getSize(name);
379 }
380 
381 version(unittest_scriptlike_d)
382 unittest
383 {
384 	string file;
385 
386 	testFileOperation!("getSize", "string")(() {
387 		mixin(useTmpName!"file");
388 		std.file.write(file, "abc123");
389 		
390 		assert(getSize(file) == 6);
391 	});
392 
393 	testFileOperation!("getSize", "Path")(() {
394 		mixin(useTmpName!"file");
395 		std.file.write(file, "abc123");
396 
397 		assert(getSize(Path(file)) == 6);
398 	});
399 }
400 
401 /// Like $(FULL_STD_FILE getTimes), but supports Path and command echoing.
402 void getTimes(in Path name,
403 	out SysTime accessTime,
404 	out SysTime modificationTime)
405 {
406 	getTimes(name.raw, accessTime, modificationTime);
407 }
408 
409 ///ditto
410 void getTimes(in string name,
411 	out SysTime accessTime,
412 	out SysTime modificationTime)
413 {
414 	yapFunc(name);
415 	std.file.getTimes(name, accessTime, modificationTime);
416 }
417 
418 version(unittest_scriptlike_d)
419 unittest
420 {
421 	string file;
422 
423 	testFileOperation!("getTimes", "string")(() {
424 		mixin(useTmpName!"file");
425 		std.file.write(file, "abc123");
426 
427 		SysTime a, b;
428 		getTimes(file, a, b);
429 	});
430 
431 	testFileOperation!("getTimes", "Path")(() {
432 		mixin(useTmpName!"file");
433 		std.file.write(file, "abc123");
434 
435 		SysTime a, b;
436 		getTimes(Path(file), a, b);
437 	});
438 }
439 
440 version(docs_scriptlike_d)
441 {
442 	/// Windows-only. Like $(FULL_STD_FILE getTimesWin), but supports Path and command echoing.
443 	void getTimesWin(in Path name,
444 		out SysTime fileCreationTime,
445 		out SysTime fileAccessTime,
446 		out SysTime fileModificationTime);
447 
448 	///ditto
449 	void getTimesWin(in string name,
450 		out SysTime fileCreationTime,
451 		out SysTime fileAccessTime,
452 		out SysTime fileModificationTime);
453 }
454 else version(Windows)
455 {
456 	void getTimesWin(in Path name,
457 		out SysTime fileCreationTime,
458 		out SysTime fileAccessTime,
459 		out SysTime fileModificationTime)
460 	{
461 		getTimesWin(name.raw, fileCreationTime, fileAccessTime, fileModificationTime);
462 	}
463 
464 	void getTimesWin(in string name,
465 		out SysTime fileCreationTime,
466 		out SysTime fileAccessTime,
467 		out SysTime fileModificationTime)
468 	{
469 		yapFunc(name);
470 		std.file.getTimesWin(name, fileCreationTime, fileAccessTime, fileModificationTime);
471 	}
472 
473 	version(unittest_scriptlike_d)
474 	unittest
475 	{
476 		string file;
477 
478 		testFileOperation!("getTimesWin", "string")(() {
479 			mixin(useTmpName!"file");
480 			std.file.write(file, "abc123");
481 
482 			SysTime a, b, c;
483 			getTimesWin(file, a, b, c);
484 		});
485 
486 		testFileOperation!("getTimesWin", "Path")(() {
487 			mixin(useTmpName!"file");
488 			std.file.write(file, "abc123");
489 
490 			SysTime a, b, c;
491 			getTimesWin(Path(file), a, b, c);
492 		});
493 	}
494 }
495 
496 /// Like $(FULL_STD_FILE setTimes), but supports Path, command echoing and dryrun.
497 void setTimes(in Path name,
498 	SysTime accessTime,
499 	SysTime modificationTime)
500 {
501 	setTimes(name.raw, accessTime, modificationTime);
502 }
503 
504 ///ditto
505 void setTimes(in string name,
506 	SysTime accessTime,
507 	SysTime modificationTime)
508 {
509 	yapFunc(name.escapeShellArg(),
510 		"Accessed ", accessTime, "; Modified ", modificationTime);
511 
512 	if(!scriptlikeDryRun)
513 		std.file.setTimes(name, accessTime, modificationTime);
514 }
515 
516 version(unittest_scriptlike_d)
517 unittest
518 {
519 	string file;
520 	SysTime actualAccessTime, actualModTime;
521 	SysTime expectedAccessTime = SysTime(234567890);
522 	SysTime expectedModTime    = SysTime(123456789);
523 	
524 	void checkPre()
525 	{
526 		std.file.getTimes(file, actualAccessTime, actualModTime);
527 		assert(actualAccessTime != expectedAccessTime);
528 		assert(actualModTime != expectedModTime);
529 	}
530 
531 	void checkPost()
532 	{
533 		std.file.getTimes(file, actualAccessTime, actualModTime);
534 		assert(actualAccessTime == expectedAccessTime);
535 		assert(actualModTime == expectedModTime);
536 	}
537 
538 	/+
539 	testFileOperation!("setTimes", "string")(() {
540 		mixin(useTmpName!"file");
541 		std.file.write(file, "abc");
542 
543 		checkPre();
544 		setTimes(file, expectedAccessTime, expectedModTime);
545 		mixin(checkResult);
546 	});
547 
548 	testFileOperation!("setTimes", "Path")(() {
549 		mixin(useTmpName!"file");
550 		std.file.write(file, "abc");
551 
552 		checkPre();
553 		setTimes(Path(file), expectedAccessTime, expectedModTime);
554 		mixin(checkResult);
555 	});
556 	+/
557 }
558 
559 /// Like $(FULL_STD_FILE timeLastModified), but supports Path and command echoing.
560 SysTime timeLastModified(in Path name)
561 {
562 	return timeLastModified(name.raw);
563 }
564 
565 ///ditto
566 SysTime timeLastModified(in string name)
567 {
568 	yapFunc(name);
569 	return std.file.timeLastModified(name);
570 }
571 
572 ///ditto
573 SysTime timeLastModified(in Path name, SysTime returnIfMissing)
574 {
575 	return timeLastModified(name.raw, returnIfMissing);
576 }
577 
578 ///ditto
579 SysTime timeLastModified(in string name, SysTime returnIfMissing)
580 {
581 	yapFunc(name);
582 	return std.file.timeLastModified(name, returnIfMissing);
583 }
584 
585 version(unittest_scriptlike_d)
586 unittest
587 {
588 	string file;
589 
590 	testFileOperation!("timeLastModified", "string")(() {
591 		mixin(useTmpName!"file");
592 		std.file.write(file, "abc123");
593 
594 		timeLastModified(file);
595 	});
596 
597 	testFileOperation!("timeLastModified", "Path")(() {
598 		mixin(useTmpName!"file");
599 		std.file.write(file, "abc123");
600 
601 		timeLastModified(Path(file));
602 	});
603 
604 	testFileOperation!("timeLastModified", "string,SysTime - exists")(() {
605 		mixin(useTmpName!"file");
606 		std.file.write(file, "abc123");
607 
608 		auto ifMissing = SysTime(123);
609 		timeLastModified(file, ifMissing);
610 	});
611 
612 	testFileOperation!("timeLastModified", "Path,SysTime - exists")(() {
613 		mixin(useTmpName!"file");
614 		std.file.write(file, "abc123");
615 
616 		auto ifMissing = SysTime(123);
617 		timeLastModified(Path(file), ifMissing);
618 	});
619 
620 	testFileOperation!("timeLastModified", "string,SysTime - missing")(() {
621 		mixin(useTmpName!"file");
622 
623 		auto ifMissing = SysTime(123);
624 		assert(timeLastModified(file, ifMissing) == SysTime(123));
625 	});
626 
627 	testFileOperation!("timeLastModified", "Path,SysTime - missing")(() {
628 		mixin(useTmpName!"file");
629 
630 		auto ifMissing = SysTime(123);
631 		assert(timeLastModified(Path(file), ifMissing) == SysTime(123));
632 	});
633 }
634 
635 /// Like $(FULL_STD_FILE exists), but supports Path and command echoing.
636 bool exists(in Path name) @trusted
637 {
638 	return exists(name.raw);
639 }
640 
641 ///ditto
642 bool exists(in string name) @trusted
643 {
644 	yapFunc(name);
645 	return std.file.exists(name);
646 }
647 
648 version(unittest_scriptlike_d)
649 unittest
650 {
651 	string file;
652 
653 	testFileOperation!("exists", "string")(() {
654 		mixin(useTmpName!"file");
655 
656 		assert(!exists(file));
657 		std.file.write(file, "abc");
658 		assert(exists(file));
659 	});
660 
661 	testFileOperation!("exists", "Path")(() {
662 		mixin(useTmpName!"file");
663 
664 		assert(!exists(Path(file)));
665 		std.file.write(file, "abc");
666 		assert(exists(Path(file)));
667 	});
668 }
669 
670 /// Like $(FULL_STD_FILE getAttributes), but supports Path and command echoing.
671 uint getAttributes(in Path name)
672 {
673 	return getAttributes(name.raw);
674 }
675 
676 ///ditto
677 uint getAttributes(in string name)
678 {
679 	yapFunc(name);
680 	return std.file.getAttributes(name);
681 }
682 
683 version(unittest_scriptlike_d)
684 unittest
685 {
686 	string file;
687 
688 	testFileOperation!("getAttributes", "string")(() {
689 		mixin(useTmpName!"file");
690 		std.file.write(file, "abc123");
691 
692 		getAttributes(file);
693 	});
694 
695 	testFileOperation!("getAttributes", "Path")(() {
696 		mixin(useTmpName!"file");
697 		std.file.write(file, "abc123");
698 
699 		getAttributes(Path(file));
700 	});
701 }
702 
703 /// Like $(FULL_STD_FILE getLinkAttributes), but supports Path and command echoing.
704 uint getLinkAttributes(in Path name)
705 {
706 	return getLinkAttributes(name.raw);
707 }
708 
709 ///ditto
710 uint getLinkAttributes(in string name)
711 {
712 	yapFunc(name);
713 	return std.file.getLinkAttributes(name);
714 }
715 
716 version(unittest_scriptlike_d)
717 unittest
718 {
719 	string file;
720 
721 	testFileOperation!("getLinkAttributes", "string")(() {
722 		mixin(useTmpName!"file");
723 		std.file.write(file, "abc123");
724 
725 		getLinkAttributes(file);
726 	});
727 
728 	testFileOperation!("getLinkAttributes", "Path")(() {
729 		mixin(useTmpName!"file");
730 		std.file.write(file, "abc123");
731 
732 		getLinkAttributes(Path(file));
733 	});
734 }
735 
736 /// Like $(FULL_STD_FILE isDir), but supports Path and command echoing.
737 @property bool isDir(in Path name)
738 {
739 	return isDir(name.raw);
740 }
741 
742 ///ditto
743 @property bool isDir(in string name)
744 {
745 	yapFunc(name);
746 	return std.file.isDir(name);
747 }
748 
749 version(unittest_scriptlike_d)
750 unittest
751 {
752 	string file, dir;
753 
754 	testFileOperation!("isDir", "string")(() {
755 		mixin(useTmpName!"file");
756 		mixin(useTmpName!"dir");
757 		std.file.write(file, "abc123");
758 		std.file.mkdir(dir);
759 
760 		assert( !isDir(file) );
761 		assert( isDir(dir) );
762 	});
763 
764 	testFileOperation!("isDir", "Path")(() {
765 		mixin(useTmpName!"file");
766 		mixin(useTmpName!"dir");
767 		std.file.write(file, "abc123");
768 		std.file.mkdir(dir);
769 
770 		assert( !isDir(Path(file)) );
771 		assert( isDir(Path(dir)) );
772 	});
773 }
774 
775 /// Like $(FULL_STD_FILE isFile), but supports Path and command echoing.
776 @property bool isFile(in Path name)
777 {
778 	return isFile(name.raw);
779 }
780 
781 ///ditto
782 @property bool isFile(in string name)
783 {
784 	yapFunc(name);
785 	return std.file.isFile(name);
786 }
787 
788 version(unittest_scriptlike_d)
789 unittest
790 {
791 	string file, dir;
792 
793 	testFileOperation!("isFile", "string")(() {
794 		mixin(useTmpName!"file");
795 		mixin(useTmpName!"dir");
796 		std.file.write(file, "abc123");
797 		std.file.mkdir(dir);
798 
799 		assert( isFile(file) );
800 		assert( !isFile(dir) );
801 	});
802 
803 	testFileOperation!("isFile", "Path")(() {
804 		mixin(useTmpName!"file");
805 		mixin(useTmpName!"dir");
806 		std.file.write(file, "abc123");
807 		std.file.mkdir(dir);
808 
809 		assert( isFile(Path(file)) );
810 		assert( !isFile(Path(dir)) );
811 	});
812 }
813 
814 /// Like $(FULL_STD_FILE isSymlink), but supports Path and command echoing.
815 @property bool isSymlink(in Path name)
816 {
817 	return isSymlink(name.raw);
818 }
819 
820 ///ditto
821 @property bool isSymlink(in string name)
822 {
823 	yapFunc(name);
824 	return std.file.isSymlink(name);
825 }
826 
827 version(unittest_scriptlike_d)
828 unittest
829 {
830 	string file, dir, fileLink, dirLink;
831 
832 	testFileOperation!("isSymlink", "string")(() {
833 		mixin(useTmpName!"file");
834 		mixin(useTmpName!"dir");
835 		mixin(useTmpName!"fileLink");
836 		mixin(useTmpName!"dirLink");
837 		std.file.write(file, "abc123");
838 		std.file.mkdir(dir);
839 		version(Posix)
840 		{
841 			std.file.symlink(file, fileLink);
842 			std.file.symlink(dir, dirLink);
843 		}
844 
845 		assert( !isSymlink(file) );
846 		assert( !isSymlink(dir) );
847 		version(Posix)
848 		{
849 			assert( isSymlink(fileLink) );
850 			assert( isSymlink(dirLink) );
851 		}
852 	});
853 
854 	testFileOperation!("isSymlink", "Path")(() {
855 		mixin(useTmpName!"file");
856 		mixin(useTmpName!"dir");
857 		mixin(useTmpName!"fileLink");
858 		mixin(useTmpName!"dirLink");
859 		std.file.write(file, "abc123");
860 		std.file.mkdir(dir);
861 		version(Posix)
862 		{
863 			std.file.symlink(file, fileLink);
864 			std.file.symlink(dir, dirLink);
865 		}
866 
867 		assert( !isSymlink(Path(file)) );
868 		assert( !isSymlink(Path(dir)) );
869 		version(Posix)
870 		{
871 			assert( isSymlink(Path(fileLink)) );
872 			assert( isSymlink(Path(dirLink)) );
873 		}
874 	});
875 }
876 
877 /// Like $(FULL_STD_FILE getcwd), but returns a Path.
878 Path getcwd()
879 {
880 	return Path( std.file.getcwd() );
881 }
882 
883 /// Like $(FULL_STD_FILE chdir), but supports Path and command echoing.
884 void chdir(in Path pathname)
885 {
886 	chdir(pathname.raw);
887 }
888 
889 /// Like $(FULL_STD_FILE chdir), but supports Path and command echoing.
890 void chdir(in string pathname)
891 {
892 	yapFunc(pathname.escapeShellArg());
893 	std.file.chdir(pathname);
894 }
895 
896 version(unittest_scriptlike_d)
897 unittest
898 {
899 	string dir;
900 
901 	testFileOperation!("chdir", "string")(() {
902 		mixin(useTmpName!"dir");
903 		std.file.mkdir(dir);
904 		auto origDir = std.file.getcwd();
905 		scope(exit) std.file.chdir(origDir);
906 
907 		chdir(dir);
908 		assert(std.file.getcwd() == dir);
909 	});
910 
911 	testFileOperation!("chdir", "Path")(() {
912 		mixin(useTmpName!"dir");
913 		std.file.mkdir(dir);
914 		auto origDir = std.file.getcwd();
915 		scope(exit) std.file.chdir(origDir);
916 
917 		chdir(Path(dir));
918 		assert(std.file.getcwd() == dir);
919 	});
920 }
921 
922 /// Like $(FULL_STD_FILE mkdir), but supports Path, command echoing and dryrun.
923 void mkdir(in Path pathname)
924 {
925 	mkdir(pathname.raw);
926 }
927 
928 ///ditto
929 void mkdir(in string pathname)
930 {
931 	yapFunc(pathname.escapeShellArg());
932 
933 	if(!scriptlikeDryRun)
934 		std.file.mkdir(pathname);
935 }
936 
937 version(unittest_scriptlike_d)
938 unittest
939 {
940 	string dir;
941 	void checkPre()
942 	{
943 		assert(!std.file.exists(dir));
944 	}
945 
946 	void checkPost()
947 	{
948 		assert(std.file.exists(dir));
949 		assert(std.file.isDir(dir));
950 	}
951 
952 	testFileOperation!("mkdir", "string")(() {
953 		mixin(useTmpName!"dir");
954 
955 		checkPre();
956 		mkdir(dir);
957 		mixin(checkResult);
958 	});
959 
960 	testFileOperation!("mkdir", "Path")(() {
961 		mixin(useTmpName!"dir");
962 
963 		checkPre();
964 		mkdir(Path(dir));
965 		mixin(checkResult);
966 	});
967 }
968 
969 /// Like $(FULL_STD_FILE mkdirRecurse), but supports Path, command echoing and dryrun.
970 void mkdirRecurse(in Path pathname)
971 {
972 	mkdirRecurse(pathname.raw);
973 }
974 
975 ///ditto
976 void mkdirRecurse(in string pathname)
977 {
978 	yapFunc(pathname.escapeShellArg());
979 
980 	if(!scriptlikeDryRun)
981 		std.file.mkdirRecurse(pathname);
982 }
983 
984 version(unittest_scriptlike_d)
985 unittest
986 {
987 	string dir;
988 	void checkPre()
989 	{
990 		assert(!std.file.exists(dir));
991 	}
992 
993 	void checkPost()
994 	{
995 		assert(std.file.exists(dir));
996 		assert(std.file.isDir(dir));
997 	}
998 
999 	testFileOperation!("mkdirRecurse", "string")(() {
1000 		mixin(useTmpName!("dir", "subdir"));
1001 
1002 		checkPre();
1003 		mkdirRecurse(dir);
1004 		mixin(checkResult);
1005 	});
1006 
1007 	testFileOperation!("mkdirRecurse", "Path")(() {
1008 		mixin(useTmpName!("dir", "subdir"));
1009 
1010 		checkPre();
1011 		mkdirRecurse(Path(dir));
1012 		mixin(checkResult);
1013 	});
1014 }
1015 
1016 /// Like $(FULL_STD_FILE rmdir), but supports Path, command echoing and dryrun.
1017 void rmdir(in Path pathname)
1018 {
1019 	rmdir(pathname.raw);
1020 }
1021 
1022 ///ditto
1023 void rmdir(in string pathname)
1024 {
1025 	yapFunc(pathname.escapeShellArg());
1026 
1027 	if(!scriptlikeDryRun)
1028 		std.file.rmdir(pathname);
1029 }
1030 
1031 version(unittest_scriptlike_d)
1032 unittest
1033 {
1034 	string dir;
1035 	void checkPre()
1036 	{
1037 		assert(std.file.exists(dir));
1038 		assert(std.file.isDir(dir));
1039 	}
1040 
1041 	void checkPost()
1042 	{
1043 		assert(!std.file.exists(dir));
1044 	}
1045 
1046 	testFileOperation!("rmdir", "string")(() {
1047 		mixin(useTmpName!"dir");
1048 		std.file.mkdir(dir);
1049 
1050 		checkPre();
1051 		rmdir(dir);
1052 		mixin(checkResult);
1053 	});
1054 
1055 	testFileOperation!("rmdir", "Path")(() {
1056 		mixin(useTmpName!"dir");
1057 		std.file.mkdir(dir);
1058 
1059 		checkPre();
1060 		rmdir(Path(dir));
1061 		mixin(checkResult);
1062 	});
1063 }
1064 
1065 version(docs_scriptlike_d)
1066 {
1067 	/// Posix-only. Like $(FULL_STD_FILE symlink), but supports Path and command echoing.
1068 	void symlink(Path original, Path link);
1069 
1070 	///ditto
1071 	void symlink(string original, Path link);
1072 
1073 	///ditto
1074 	void symlink(Path original, string link);
1075 
1076 	///ditto
1077 	void symlink(string original, string link);
1078 
1079 	/// Posix-only. Like $(FULL_STD_FILE readLink), but supports Path and command echoing.
1080 	Path readLink(Path link);
1081 
1082 	///ditto
1083 	string readLink(string link);
1084 }
1085 else version(Posix)
1086 {
1087 	void symlink(Path original, Path link)
1088 	{
1089 		symlink(original.raw, link.raw);
1090 	}
1091 
1092 	void symlink(string original, Path link)
1093 	{
1094 		symlink(original, link.raw);
1095 	}
1096 
1097 	void symlink(Path original, string link)
1098 	{
1099 		symlink(original.raw, link);
1100 	}
1101 
1102 	void symlink(string original, string link)
1103 	{
1104 		yapFunc("[original] ", original.escapeShellArg(), " : [symlink] ", link.escapeShellArg());
1105 
1106 		if(!scriptlikeDryRun)
1107 			std.file.symlink(original, link);
1108 	}
1109 
1110 	version(unittest_scriptlike_d)
1111 	unittest
1112 	{
1113 		string file, link;
1114 		void checkPre()
1115 		{
1116 			assert(std.file.exists(file));
1117 			assert(std.file.isFile(file));
1118 			assert(cast(string) std.file.read(file) == "abc123");
1119 			
1120 			assert(!std.file.exists(link));
1121 		}
1122 
1123 		void checkPost()
1124 		{
1125 			assert(std.file.exists(file));
1126 			assert(std.file.isFile(file));
1127 			assert(cast(string) std.file.read(file) == "abc123");
1128 			
1129 			assert(std.file.exists(link));
1130 			assert(std.file.isSymlink(link));
1131 			assert(std.file.readLink(link) == file);
1132 			assert(cast(string) std.file.read(link) == "abc123");
1133 		}
1134 
1135 		testFileOperation!("symlink", "string,string")(() {
1136 			mixin(useTmpName!"file");
1137 			mixin(useTmpName!"link");
1138 			std.file.write(file, "abc123");
1139 
1140 			checkPre();
1141 			symlink(file, link);
1142 			mixin(checkResult);
1143 		});
1144 
1145 		testFileOperation!("symlink", "string,Path")(() {
1146 			mixin(useTmpName!"file");
1147 			mixin(useTmpName!"link");
1148 			std.file.write(file, "abc123");
1149 
1150 			checkPre();
1151 			symlink(file, Path(link));
1152 			mixin(checkResult);
1153 		});
1154 
1155 		testFileOperation!("symlink", "Path,string")(() {
1156 			mixin(useTmpName!"file");
1157 			mixin(useTmpName!"link");
1158 			std.file.write(file, "abc123");
1159 
1160 			checkPre();
1161 			symlink(Path(file), link);
1162 			mixin(checkResult);
1163 		});
1164 
1165 		testFileOperation!("symlink", "Path,Path")(() {
1166 			mixin(useTmpName!"file");
1167 			mixin(useTmpName!"link");
1168 			std.file.write(file, "abc123");
1169 
1170 			checkPre();
1171 			symlink(Path(file), Path(link));
1172 			mixin(checkResult);
1173 		});
1174 	}
1175 
1176 	Path readLink(Path link)
1177 	{
1178 		return Path( readLink(link.raw) );
1179 	}
1180 
1181 	string readLink(string link)
1182 	{
1183 		yapFunc(link);
1184 		return std.file.readLink(link);
1185 	}
1186 
1187 	version(unittest_scriptlike_d)
1188 	unittest
1189 	{
1190 		string file, link;
1191 
1192 		testFileOperation!("readLink", "string")(() {
1193 			mixin(useTmpName!"file");
1194 			mixin(useTmpName!"link");
1195 			std.file.write(file, "abc123");
1196 			std.file.symlink(file, link);
1197 
1198 			assert(readLink(link) == file);
1199 		});
1200 
1201 		testFileOperation!("readLink", "Path")(() {
1202 			mixin(useTmpName!"file");
1203 			mixin(useTmpName!"link");
1204 			std.file.write(file, "abc123");
1205 			std.file.symlink(file, link);
1206 
1207 			assert(readLink(Path(link)) == Path(file));
1208 		});
1209 	}
1210 }
1211 
1212 /// Like $(FULL_STD_FILE copy), but supports Path, command echoing and dryrun.
1213 void copy(in Path from, in Path to)
1214 {
1215 	copy(from.raw, to.raw);
1216 }
1217 
1218 ///ditto
1219 void copy(in string from, in Path to)
1220 {
1221 	copy(from, to.raw);
1222 }
1223 
1224 ///ditto
1225 void copy(in Path from, in string to)
1226 {
1227 	copy(from.raw, to);
1228 }
1229 
1230 ///ditto
1231 void copy(in string from, in string to)
1232 {
1233 	yapFunc(from.escapeShellArg(), " -> ", to.escapeShellArg());
1234 
1235 	if(!scriptlikeDryRun)
1236 		std.file.copy(from, to);
1237 }
1238 
1239 version(unittest_scriptlike_d)
1240 unittest
1241 {
1242 	string file1;
1243 	string file2;
1244 	void checkPre()
1245 	{
1246 		assert(std.file.exists(file1));
1247 		assert(std.file.isFile(file1));
1248 		assert(cast(string) std.file.read(file1) == "abc");
1249 
1250 		assert(!std.file.exists(file2));
1251 	}
1252 
1253 	void checkPost()
1254 	{
1255 		assert(std.file.exists(file1));
1256 		assert(std.file.isFile(file1));
1257 		assert(cast(string) std.file.read(file1) == "abc");
1258 
1259 		assert(std.file.exists(file2));
1260 		assert(std.file.isFile(file2));
1261 		assert(cast(string) std.file.read(file2) == "abc");
1262 	}
1263 
1264 	testFileOperation!("copy", "string,string")(() {
1265 		mixin(useTmpName!"file1");
1266 		mixin(useTmpName!"file2");
1267 		std.file.write(file1, "abc");
1268 
1269 		checkPre();
1270 		copy(file1, file2);
1271 		mixin(checkResult);
1272 	});
1273 
1274 	testFileOperation!("copy", "string,Path")(() {
1275 		mixin(useTmpName!"file1");
1276 		mixin(useTmpName!"file2");
1277 		std.file.write(file1, "abc");
1278 
1279 		checkPre();
1280 		copy(file1, Path(file2));
1281 		mixin(checkResult);
1282 	});
1283 
1284 	testFileOperation!("copy", "Path,string")(() {
1285 		mixin(useTmpName!"file1");
1286 		mixin(useTmpName!"file2");
1287 		std.file.write(file1, "abc");
1288 
1289 		checkPre();
1290 		copy(Path(file1), file2);
1291 		mixin(checkResult);
1292 	});
1293 
1294 	testFileOperation!("copy", "Path,Path")(() {
1295 		mixin(useTmpName!"file1");
1296 		mixin(useTmpName!"file2");
1297 		std.file.write(file1, "abc");
1298 
1299 		checkPre();
1300 		copy(Path(file1), Path(file2));
1301 		mixin(checkResult);
1302 	});
1303 }
1304 
1305 /// Like $(FULL_STD_FILE rmdirRecurse), but supports Path, command echoing and dryrun.
1306 void rmdirRecurse(in Path pathname)
1307 {
1308 	rmdirRecurse(pathname.raw);
1309 }
1310 
1311 ///ditto
1312 void rmdirRecurse(in string pathname)
1313 {
1314 	yapFunc(pathname.escapeShellArg());
1315 
1316 	if(!scriptlikeDryRun)
1317 		std.file.rmdirRecurse(pathname);
1318 }
1319 
1320 version(unittest_scriptlike_d)
1321 unittest
1322 {
1323 	string dir;
1324 	void checkPre()
1325 	{
1326 		assert(std.file.exists(dir));
1327 		assert(std.file.isDir(dir));
1328 	}
1329 
1330 	void checkPost()
1331 	{
1332 		assert(!std.file.exists( std.path.dirName(dir) ));
1333 	}
1334 
1335 	testFileOperation!("rmdirRecurse", "string")(() {
1336 		mixin(useTmpName!("dir", "subdir"));
1337 		std.file.mkdirRecurse(dir);
1338 
1339 		checkPre();
1340 		rmdirRecurse( std.path.dirName(dir) );
1341 		mixin(checkResult);
1342 	});
1343 
1344 	testFileOperation!("rmdirRecurse", "Path")(() {
1345 		mixin(useTmpName!("dir", "subdir"));
1346 		std.file.mkdirRecurse(dir);
1347 
1348 		checkPre();
1349 		rmdirRecurse(Path( std.path.dirName(dir) ));
1350 		mixin(checkResult);
1351 	});
1352 }
1353 
1354 /// Like $(FULL_STD_FILE dirEntries), but supports Path and command echoing.
1355 auto dirEntries(string path, SpanMode mode, bool followSymlink = true)
1356 {
1357 	yapFunc(path);
1358 	return std.file.dirEntries(path, mode, followSymlink);
1359 }
1360 
1361 ///ditto
1362 auto dirEntries(Path path, SpanMode mode, bool followSymlink = true)
1363 {
1364 	return dirEntries(path.raw, mode, followSymlink);
1365 }
1366 
1367 ///ditto
1368 auto dirEntries(string path, string pattern, SpanMode mode,
1369 	bool followSymlink = true)
1370 {
1371 	yapFunc(path);
1372 	return std.file.dirEntries(path, pattern, mode, followSymlink);
1373 }
1374 
1375 ///ditto
1376 auto dirEntries(Path path, string pattern, SpanMode mode,
1377 	bool followSymlink = true)
1378 {
1379 	return dirEntries(path.raw, pattern, mode, followSymlink);
1380 }
1381 
1382 version(unittest_scriptlike_d)
1383 unittest
1384 {
1385 	string dir;
1386 
1387 	testFileOperation!("dirEntries", "string")(() {
1388 		mixin(useTmpName!("dir", "subdir"));
1389 		std.file.mkdirRecurse(dir);
1390 
1391 		auto range = dirEntries(std.path.dirName(dir), SpanMode.shallow);
1392 		assert(range.front.name == dir);
1393 		range.popFront();
1394 		assert(range.empty);
1395 	});
1396 
1397 	testFileOperation!("dirEntries", "Path")(() {
1398 		mixin(useTmpName!("dir", "subdir"));
1399 		std.file.mkdirRecurse(dir);
1400 
1401 		auto range = dirEntries(Path(std.path.dirName(dir)), SpanMode.shallow);
1402 		assert(range.front.name == dir);
1403 		range.popFront();
1404 		assert(range.empty);
1405 	});
1406 
1407 	testFileOperation!("dirEntries", "string,pattern")(() {
1408 		mixin(useTmpName!("dir", "subdir"));
1409 		std.file.mkdirRecurse(dir);
1410 
1411 		auto range = dirEntries(std.path.dirName(dir), "*", SpanMode.shallow);
1412 		assert(range.front.name == dir);
1413 		range.popFront();
1414 		assert(range.empty);
1415 	});
1416 
1417 	testFileOperation!("dirEntries", "Path,pattern")(() {
1418 		mixin(useTmpName!("dir", "subdir"));
1419 		std.file.mkdirRecurse(dir);
1420 
1421 		auto range = dirEntries(Path(std.path.dirName(dir)), "*", SpanMode.shallow);
1422 		assert(range.front.name == dir);
1423 		range.popFront();
1424 		assert(range.empty);
1425 	});
1426 }
1427 
1428 /// Like $(FULL_STD_FILE slurp), but supports Path and command echoing.
1429 auto slurp(Types...)(Path filename, in string format)
1430 {
1431 	return slurp!Types(filename.raw, format);
1432 }
1433 
1434 ///ditto
1435 auto slurp(Types...)(string filename, in string format)
1436 {
1437 	yapFunc(filename);
1438 	return std.file.slurp!Types(filename, format);
1439 }
1440 
1441 version(unittest_scriptlike_d)
1442 unittest
1443 {
1444 	string file;
1445 
1446 	testFileOperation!("slurp", "string")(() {
1447 		mixin(useTmpName!"file");
1448 		std.file.write(file, "abc, 123");
1449 
1450 		auto result = slurp!(string, int)(file, "%s, %s");
1451 		auto expected = [Tuple!(string, int)("abc", 123)];
1452 		assert(result == expected);
1453 	});
1454 
1455 	testFileOperation!("slurp", "Path")(() {
1456 		mixin(useTmpName!"file");
1457 		std.file.write(file, "abc, 123");
1458 
1459 		auto result = slurp!(string, int)(Path(file), "%s, %s");
1460 		auto expected = [Tuple!(string, int)("abc", 123)];
1461 		assert(result == expected);
1462 	});
1463 }
1464 
1465 /// Like $(FULL_STD_FILE thisExePath), but supports Path and command echoing.
1466 @trusted Path thisExePath()
1467 {
1468 	auto path = Path( std.file.thisExePath() );
1469 	yapFunc(path);
1470 	return path;
1471 }
1472 
1473 version(unittest_scriptlike_d)
1474 unittest
1475 {
1476 	testFileOperation!("thisExePath", "Path")(() {
1477 		thisExePath();
1478 	});
1479 }
1480 
1481 /// Like $(FULL_STD_FILE tempDir), but supports Path and command echoing.
1482 @trusted Path tempDir()
1483 {
1484 	auto path = Path( std.file.tempDir() );
1485 	yapFunc(path);
1486 	return path;
1487 }
1488 
1489 version(unittest_scriptlike_d)
1490 unittest
1491 {
1492 	testFileOperation!("tempDir", "Path")(() {
1493 		assert( tempDir() == Path(std.file.tempDir()) );
1494 	});
1495 }