dtagfs/source/filesystem.d

205 lines
4.2 KiB
D
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module dtagfs.filesystem;
import std.algorithm;
import std.range;
import std.file;
import std.conv;
import std.path;
import std.array;
import std.string;
import std.stdio;
import std.typecons;
import core.sys.posix.unistd;
import core.stdc.string;
import core.stdc.errno;
import dfuse.fuse;
import dtagfs.tagprovider;
class FileSystem : Operations
{
private string _source;
private TagProvider[] _tagProviders;
private stat_t _sourceStat;
private string[][string] _tagCache;
private string[][string] _dirCache;
private string[] _tagList;
private string[string] _fileLinkCache;
private bool _noCommon;
enum exclusionChar = '!';
this(string source, TagProvider[] tagProviders, bool noCommon)
{
_source = source;
_tagProviders = tagProviders;
_noCommon = noCommon;
lstat(toStringz(source), &_sourceStat);
cacheTags();
}
@property
TagProvider primaryTagProvider()
{
return _tagProviders[0];
}
void cacheTags()
{
foreach(tagProvider; _tagProviders.filter!(a => a.cacheReads))
{
foreach(file; dirEntries(_source, SpanMode.breadth).filter!(a => a.isFile))
{
auto tags = tagProvider.getTags(file);
_tagCache[file] ~= tags.map!(tag => tag.replace("/", "")).array; //replace '/' (directory separator) with full-width solidus
_fileLinkCache[file.baseName] = file;
}
}
_tagList = _tagCache.byValue()
.joiner
.array
.sort()
.uniq
.array;
}
override void getattr(const(char)[] path, ref stat_t stat)
{
stat.st_uid = _sourceStat.st_uid;
stat.st_gid = _sourceStat.st_gid;
//excluded tags are also valid directories
if(path == "/" || isTag(path.baseName.stripLeft(exclusionChar)))
{
stat.st_mode = _sourceStat.st_mode;
}
else if(isFile(path.baseName))
{
stat.st_mode = S_IFLNK | octal!777;
stat.st_nlink = 1;
}
else
{
throw new FuseException(ENOENT);
}
}
bool isTag(const(char)[] name)
{
return _tagList.canFind(name);
}
bool isFile(const(char)[] name)
{
return (name in _fileLinkCache) !is null;
}
string findFile(const(char)[] name)
{
if(!isFile(name))
{
return null;
}
return _fileLinkCache[name];
}
Tuple!(bool, "exclude", string, "name")[] pathToTagTuples(const(char)[] path)
{
//map the path to a range of tuples with an exclude (bool) and a name (string) field for each tag in the path
return pathSplitter(path)
.array[1..$]
.map!(tag => tuple!("exclude", "name")(tag[0] == exclusionChar, cast(immutable)tag.stripLeft(exclusionChar)))
.array;
}
string[] getTags(const(char)[] path)
{
if(path == "/")
{
return _tagList;
}
else
{
auto tags = pathToTagTuples(path);
//filter pairs with tags that (should not be excluded && can be found) || (should be excluded && can't be found)
auto filePairs = _tagCache.byKeyValue().filter!(a => tags.all!(b => !b.exclude == a.value.canFind(b.name)));
return filePairs.map!(a => a.value)
.joiner
.filter!(a => !tags.map!(tag => tag.name).canFind(a)) //don't match tags that are already in the path
.filter!(a => !_noCommon || !filePairs.all!(f => f.value.canFind(a)))
.array
.sort()
.uniq
.array;
}
}
string[] getFiles(const(char)[] path)
{
if(path == "/")
{
return _tagCache.keys.map!(a => a.baseName).array;
}
else
{
auto tags = pathToTagTuples(path);
//filter files with tags that (should not be excluded && can be found) || (should be excluded && can't be found)
return _tagCache.byKeyValue()
.filter!(a => tags.all!(b => !b.exclude == a.value.canFind(b.name)))
.map!(a => a.key.baseName)
.array;
}
}
override ulong readlink(const(char)[] path, ubyte[] buf)
{
auto realPath = findFile(path.baseName);
if(realPath is null)
{
throw new FuseException(ENOENT);
}
strncpy(cast(char*)buf, cast(char*)realPath.toStringz, realPath.length);
return realPath.length;
}
override bool access(const(char)[] path, int mode)
{
//TODO: Check if this should always be true
return true;
}
override string[] readdir(const(char)[] path)
{
if(path in _dirCache)
{
return _dirCache[path];
}
if (path == "/")
{
return _dirCache[path] = getTags(path) ~ getFiles(path);
}
else
{
return _dirCache[path] = getTags(path) ~ getFiles(path) ~ [".", ".."];
}
}
}