Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce FSEntry.removeFile, FSEntry.removeDir, refactor FSEntry.writeFile #2879

Merged
merged 1 commit into from
Feb 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
103 changes: 91 additions & 12 deletions source/dub/test/base.d
Original file line number Diff line number Diff line change
Expand Up @@ -598,6 +598,34 @@ public class FSEntry
return null;
}

/** Get the parent `FSEntry` of a `NativePath`
*
* If the parent doesn't exist, an `Exception` will be thrown
* unless `silent` is provided. If the parent path is a file,
* an `Exception` will be thrown regardless of `silent`.
*
* Params:
* path = The path to look up the parent for
* silent = Whether to error on non-existing parent,
* default to `false`.
*/
protected inout(FSEntry) getParent(NativePath path, bool silent = false)
inout return scope
{
// Relative path in the current directory
if (!path.hasParentPath())
return this;

// If we're not in the right `FSEntry`, recurse
const parentPath = path.parentPath();
auto p = this.lookup(parentPath);
enforce(silent || p !is null,
"No such directory: " ~ parentPath.toNativeString());
enforce(p is null || p.type == Type.Directory,
"Parent path is not a directory: " ~ parentPath.toNativeString());
return p;
}

/// Returns: A path relative to `this.path`
protected NativePath relativePath(NativePath path) const scope
{
Expand Down Expand Up @@ -759,20 +787,71 @@ public class FSEntry
enforce(file.type == Type.File,
"Trying to write to directory: " ~ path.toNativeString());
file.content = data.dup;
} else if (path.hasParentPath()) {
// If we're not in the right `FSEntry`, recurse
auto parentPath = path.parentPath();
auto parent = this.lookup(parentPath);
enforce(parent !is null,
"No such directory: " ~ parentPath.toNativeString());
enforce(parent.type == Type.Directory,
"Parent path is not a directory: " ~ parentPath.toNativeString());
return parent.writeFile(NativePath(path.head), data);
} else {
// We're in the right `FSEntry`, create the file
auto file = new FSEntry(this, Type.File, path.head.name());
auto p = this.getParent(path);
auto file = new FSEntry(p, Type.File, path.head.name());
file.content = data.dup;
this.children ~= file;
p.children ~= file;
}
}

/** Remove a file
*
* Always error if the target is a directory.
* Does not error if the target does not exists
* and `force` is set to `true`.
*
* Params:
* path = Path to the file to remove
* force = Whether to ignore non-existing file,
* default to `false`.
*/
public void removeFile (NativePath path, bool force = false)
{
import std.algorithm.searching : countUntil;

assert(!path.empty, "Empty path provided to `removeFile`");
enforce(!path.endsWithSlash(),
"Cannot remove file with directory path: " ~ path.toNativeString());
auto p = this.getParent(path, force);
const idx = p.children.countUntil!(e => e.name == path.head.name());
if (idx < 0) {
enforce(force,
"removeFile: No such file: " ~ path.toNativeString());
} else {
enforce(p.children[idx].type == Type.File,
"removeFile called on a directory: " ~ path.toNativeString());
p.children = p.children[0 .. idx] ~ p.children[idx + 1 .. $];
}
}

/** Remove a directory
*
* Remove an existing empty directory.
* If `force` is set to `true`, no error will be thrown
* if the directory is empty or non-existing.
*
* Params:
* path = Path to the directory to remove
* force = Whether to ignore non-existing / non-empty directories,
* default to `false`.
*/
public void removeDir (NativePath path, bool force = false)
{
import std.algorithm.searching : countUntil;

assert(!path.empty, "Empty path provided to `removeFile`");
auto p = this.getParent(path, force);
const idx = p.children.countUntil!(e => e.name == path.head.name());
if (idx < 0) {
enforce(force,
"removeDir: No such directory: " ~ path.toNativeString());
} else {
enforce(p.children[idx].type == Type.Directory,
"removeDir called on a file: " ~ path.toNativeString());
enforce(force || p.children[idx].children.length == 0,
"removeDir called on non-empty directory: " ~ path.toNativeString());
p.children = p.children[0 .. idx] ~ p.children[idx + 1 .. $];
}
}
}
Expand Down