1 /**
2  * Defines the FilePath class, which stores default resource paths, and handles path manipulation.
3  */
4 module utility.filepath;
5 import utility.output;
6 
7 static import std.file, std.path;
8 import std.stdio;
9 
10 /**
11  * A class which stores default resource paths, and handles path manipulation.
12  */
13 final class FilePath
14 {
15 public:
16 	/**
17 	 * The path to the resources home folder.
18 	 */
19 	enum ResourceHome = "..";
20 
21 	/**
22 	 * Paths to the different resource files.
23 	 */
24 	enum Resources : string
25 	{
26 		Materials = ResourceHome ~ "/Materials",
27 		Meshes = ResourceHome ~ "/Meshes",
28 		Textures = ResourceHome ~ "/Textures",
29 		Scripts = ResourceHome ~ "/Scripts",
30 		Prefabs = ResourceHome ~ "/Prefabs",
31 		Objects = ResourceHome ~ "/Objects",
32 		Shaders = ResourceHome ~ "/Shaders",
33 		UI = ResourceHome ~ "/UI",
34 		ConfigDir = ResourceHome ~ "/Config",
35 		ConfigFile = ConfigDir ~ "/Config.yml",
36 		InputBindings = ConfigDir ~ "/Input.yml", 
37 	}
38 
39 	/**
40 	 * Get all files in a given directory.
41 	 */
42 	final static FilePath[] scanDirectory( string path, string pattern = "" )
43 	{
44 		// Get absolute path to folder
45 		string safePath = std.path.buildNormalizedPath( std.path.absolutePath( path ) );
46 
47 		if( !std.file.exists( safePath ) )
48 		{
49 			log( OutputType.Info, path, " does not exist." );
50 			return [];
51 		}
52 
53 		// Start array
54 		auto files = new FilePath[ 1 ];
55 		uint filesFound = 0;
56 
57 		// Add file to array
58 		void handleFile( string name )
59 		{
60 			if( filesFound == files.length )
61 				files.length *= 2;
62 
63 			files[ filesFound++ ] = new FilePath( name );
64 		}
65 
66 		// Find files
67 		if( pattern.length )
68 			foreach( name; std.file.dirEntries( safePath, pattern, std.file.SpanMode.breadth ) )
69 				handleFile( name );
70 		else
71 			foreach( name; std.file.dirEntries( safePath, std.file.SpanMode.breadth ) )
72 				handleFile( name );
73 
74 		files.length = filesFound;
75 
76 		return files;
77 	}
78 
79 	/// The full path to the file.
80 	final @property string fullPath()		{ return _fullPath; }
81 	/// The relative path from the executable to the file.
82 	final @property string relativePath()
83 	{
84 		if( !_relativePath )
85 			_relativePath = std.path.relativePath( _fullPath );
86 
87 		return _relativePath;
88 	}
89 	/// The name of the file with its extension.
90 	final @property string fileName()
91 	{
92 		if( !_fileName )
93 			_fileName = std.path.baseName( _fullPath );
94 
95 		return _fileName;
96 	}
97 	/// The name of the file without its extension.
98 	final @property string baseFileName()
99 	{
100 		if( !_baseFileName )
101 			_baseFileName = std.path.stripExtension( fileName );
102 
103 		return _baseFileName;
104 	}
105 	/// The path to the directory containing the file.
106 	final @property string directory()
107 	{
108 		if( !_directory )
109 			_directory = std.path.dirName( _fullPath );
110 
111 		return _directory;
112 	}
113 	/// The extensino of the file.
114 	final @property ref string extension()
115 	{
116 		if( !_extension )
117 			_extension = std.path.extension( _fullPath );
118 
119 		return _extension;
120 	}
121 	/// Converts to a std.stdio.File
122 	final File* toFile( string mode = "r" )
123 	{
124 		if( !file )
125 			file = new File( _fullPath, mode );
126 
127 		return file;
128 	}
129 
130 	final string getContents()
131 	{
132 		return cast(string)std.file.read(_fullPath);
133 	}
134 
135 	/**
136 	 * Create an instance based on a given file path.
137 	 */
138 	this( string path )
139 	{
140 		if( std.file.isFile( path ) )
141 			_fullPath = std.path.buildNormalizedPath( std.path.absolutePath( path ) );
142 		else
143 			throw new Exception( "Invalid file name." );
144 	}
145 
146 	/**
147 	 * Shuts down the File if it was instantiated.
148 	 */
149 	~this()
150 	{
151 		if( file && file.isOpen )
152 			file.close();
153 	}
154 
155 private:
156 	string _fullPath;
157 	string _relativePath;
158 	string _fileName;
159 	string _baseFileName;
160 	string _directory;
161 	string _extension;
162 	File* file;
163 }
164 
165 unittest
166 {
167 	import std.stdio;
168 	writeln( "Dash FilePath properties unittest" );
169 
170 	auto fp = new FilePath( FilePath.Resources.ConfigFile );
171 
172 	assert( fp.fileName == "Config.yml", "FilePath.fileName error." );
173 	assert( fp.baseFileName == "Config", "FilePath.baseFileName error." );
174 	assert( fp.extension == ".yml", "FilePath.extension error." );
175 }