I've managed to make a cut-down version that's < 170 LOC.
It needs to be run on Debian or a Debian-based Linux (e.g., Ubuntu).

Below is the full source followed by the error output.

// app.d
import std.typecons: Tuple;

void main() {
    import std.stdio: writeln, writefln;
    auto model = Model();
    model.readPackages();
    writefln("read %,d packages", model.length);
}

enum PACKAGE_DIR = "/var/lib/apt/lists";
enum PACKAGE_PATTERN = "*Packages";
alias Unit = void[0];
enum unit = Unit.init;
alias MaybeKeyValue = Tuple!(string, "key", string, "value", bool, "ok");
struct DoneMessage {}

struct Deb {
    string name;
    string description;
    Unit[string] tags; // set of tags

    Deb dup() const {
        Deb deb;
        deb.name = name;
        deb.description = description;
        foreach (key; tags.byKey)
            deb.tags[key] = unit;
        return deb;
    }

    bool valid() {
        import std.string: empty;
        return !name.empty && !description.empty;
    }

    void clear() {
        name = "";
        description = "";
        tags.clear;
    }
}

struct Model {
    import std.concurrency: Tid;

    private Deb[string] debForName; // Only read once populated

    size_t length() const { return debForName.length; }

    void readPackages() {
        import std.concurrency: receive, thisTid, spawn;
        import std.file: dirEntries, FileException, SpanMode;

        Tid[] tids;
        try {
            foreach (string filename; dirEntries(PACKAGE_DIR,
                                                 PACKAGE_PATTERN,
SpanMode.shallow)) tids ~= spawn(&readPackageFile, thisTid, filename);
            auto jobs = tids.length;
            while (jobs) {
                receive(
                    (Deb deb) { debForName[deb.name] = deb; },
                    (DoneMessage) { jobs--; }
                );
            }
        } catch (FileException err) {
            import std.stdio: stderr;
            stderr.writeln("failed to read packages: ", err);
        }
    }

    private void readPackageFile(Tid parentTid, string filename) {
        import std.concurrency: send;
        import std.file: FileException;
        import std.range: enumerate;
        import std.stdio: File, stderr;

        try {
bool inDescription = false; // Descriptions can by multi-line bool inContinuation = false; // Other things can be multi-line
            Deb deb;
            auto file = File(filename);
            foreach(lino, line; file.byLine.enumerate(1))
readPackageLine(parentTid, filename, lino, line, deb,
                                inDescription, inContinuation);
            if (deb.valid)
                send(parentTid, cast(immutable)deb.dup);
        } catch (FileException err) {
stderr.writefln("error: %s: failed to read packages: %s",
                            filename, err);
        }
        send(parentTid, DoneMessage());
    }

    private void readPackageLine(
            Tid parentTid, const string filename, const int lino,
const(char[]) line, ref Deb deb, ref bool inDescription,
            ref bool inContinuation) {
        import std.concurrency: send;
        import std.path: baseName;
        import std.stdio: stderr;
        import std.string: empty, startsWith, strip;

        if (strip(line).empty) {
            if (deb.valid)
                send(parentTid, cast(immutable)deb.dup);
            else if (!deb.name.empty || !deb.description.empty ||
                     !deb.tags.empty)
stderr.writefln("error: %s:%,d: incomplete package: %s",
                                baseName(filename), lino, deb);
            deb.clear;
            return;
        }
        if (inDescription || inContinuation) {
            if (line.startsWith(' ') || line.startsWith('\t')) {
                if (inDescription)
                    deb.description ~= line;
                return;
            }
            inDescription = inContinuation = false;
        }
        immutable keyValue = maybeKeyValue(line);
        if (!keyValue.ok)
            inContinuation = true;
        else
            inDescription = populateDeb(deb, keyValue.key,
                                        keyValue.value);
    }
}

MaybeKeyValue maybeKeyValue(const(char[]) line) {
    import std.string: indexOf, strip;

    immutable i = line.indexOf(':');
    if (i == -1)
        return MaybeKeyValue("", "", false);
    immutable key = strip(line[0..i]).idup;
    immutable value = strip(line[i + 1..$]).idup;
    return MaybeKeyValue(key, value, true);
}

bool populateDeb(ref Deb deb, const string key, const string value) {
    import std.conv: to;

    switch (key) {
        case "Package":
            deb.name = value;
            return false;
        case "Description", "Npp-Description": // XXX ignore Npp-?
            deb.description ~= value;
            return true; // We are now in a description
        case "Tag":
            maybePopulateTags(deb, value);
            return false;
        default: return false; // Ignore "uninteresting" fields
    }
}

void maybePopulateTags(ref Deb deb, const string tags) {
    import std.regex: ctRegex, split;

    auto rx = ctRegex!(`\s*,\s*`);
    foreach (tag; tags.split(rx))
        deb.tags[tag] = unit;
}
// end of app.d

Error output:
src/app.d(59,30): Error: template std.concurrency.spawn cannot deduce function from argument types !()(void delegate(Tid parentTid, string filename), Tid, string), candidates are:
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/concurrency.d(460,5): 
       spawn(F, T...)(F fn, T args)
  with F = void delegate(Tid, string),
       T = (Tid, string)
  must satisfy the following constraint:
       isSpawnable!(F, T)
src/app.d(62,24): Error: template std.concurrency.receive cannot deduce function from argument types !()(void delegate(Deb deb) pure nothrow @safe, void), candidates are:
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/concurrency.d(673,6): 
       receive(T...)(T ops)
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/variant.d(701,26): 
Error: cannot implicitly convert expression rhs of type immutable(Deb) to Deb
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/variant.d(603,17): 
Error: template instance 
std.variant.VariantN!32LU.VariantN.opAssign!(immutable(Deb)) error instantiating
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/concurrency.d(126,22):
        instantiated from here: __ctor!(immutable(Deb))
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/concurrency.d(656,23):
        instantiated from here: __ctor!(immutable(Deb))
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/concurrency.d(647,10):
        instantiated from here: _send!(immutable(Deb))
/home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/../import/std/concurrency.d(626,10):
        instantiated from here: _send!(immutable(Deb))
src/app.d(88,21): instantiated from here: send!(immutable(Deb)) /home/mark/opt/ldc2-1.20.0-linux-x86_64/bin/ldc2 failed with exit code 1.


Hopefully this will help someone understand and be able to help!

Reply via email to