A directory seamlessly working on a stack of multiple directories.

Directories can be mounted using the mount() method.

When looking for a file or directory in a StackDir, the last directory is searched first, then the second last, and so on. This means that directories mounted later override those mounted before.


this(string name)

Construct a StackDir.



void mount(VFSDir dir)

Mount a directory.

Inherited Members

From VFSDir

string name [@property getter]

Get the name of this directory.

string path [@property getter]

Get full path of this directory in the VFS.

bool writable [@property getter]

Is it possible to write to the directory?

bool exists [@property getter]

Does the directory exist?

VFSFile file(string path)

Get file with specified _path in the directory.

VFSDir dir(string path)

Get a subdirectory with specified _path in the directory.

VFSFiles files(Flag!"deep" deep = No.deep, string glob = null)

Get a range of files in the directory.

VFSDirs dirs(Flag!"deep" deep = No.deep, string glob = null)

Get a range of subdirectories.

void create()

Create the directory if it does not exist (otherwise do nothing).

void remove()

Remove the directory if it exists (otherwise do nothing).

VFSDirs dirsRange(VFSDirs.Items dirs)

Construct a range from a set of directories.

VFSFiles filesRange(VFSFiles.Items files)

Construct a range from a set of _files.

string composePath(const VFSDir child)

Compose path for a _child directory. Used e.g. to allow StackDir to set children's paths.

void create_()

Implementation of create(). Caller contract guarantees that the directory is writable.

VFSDir copyWithoutParent()

Return a copy of this VFSDir without a parent. Used for mounting.

VFSDir getCopyWithoutParent(VFSDir dir)

Access for derived classes to call copyWithoutParent() of other instances.


We have a directory called data with the following contents:

1 shaders:
2    font.frag 
3    font.vert 
4 logs:
5    (empty)
6 main.cfg

and a directory called user_data with the following contents:

1 shaders: 
2     font.frag
3 logs:
4     (empty)
5 custom.cfg

the following code will work as specified in the comments:

1 VFSDir data, user_data; //initialized somewhere before
3 auto stack = new StackDir("stack");
4 stack.mount(data);
5 stack.mount(user_data);
7 //This will access user_data/shaders/font.frag
8 auto frag = stack.file("shaders/font.frag");
9 //This will access data/shaders/font.vert
10 auto vert = stack.file("shaders/font.vert");
11 //This will return a StackDir (as VFSDir) with "data/logs" and "user_data/logs"
12 //mounted, in that order:
13 auto logs = stack.dir("logs");

Accessing a file in a StackDir will actually return a StackFile, which decides which file to access on read, write and other operations. The StackFile is a stack of all files that map to the same path in the StackDir in the same order as StackDir's mounted directories.

For example, when reading or determining file size, the directories in the stack will be searched from newest to oldest and the first file found will be used.

When writing, the file in the newest writable directory will be written to.

In some cases, it might be required to access a particular directory in the stack. E.g. a game might have multiple packages stacked on top of each other, but sometimes default, non-overridden version of a file could be needed. This can be done using the :: separator.

In the context of the previous example:

//This will access data/shaders/font.frag even though user_data/shaders/font.frag exists
auto default_frag = stack.file("data::shaders/font.frag");

StackDir is considered writable when any directory in the stack is writable. Similarly, it exists when any directory in the stack exists.

When we have a StackDir that does not exist and we create() it, the newest directory that is writable will be created. (This can happen when getting a nonexistent subdirectory of a StackDir.)