D言語でARGF

RubyのARGFが羨ましかったので,D言語で簡単に書いてみた.といってもLLみたいに特殊変数とかないので,ファイル名の配列渡せばそれっぽく動くというだけ.もっと凝ってもいいのだけど,単に今書いてるので欲しいってだけなので,それほど厳密ではなかったり^^;

/**
 * D like ARGF
 */
class ARGF
{
  private:
    string[] argv;
    size_t   argc;
    Stream   stream;  // current file
    bool     dined;   // true if opened stream is din


  public:
    /**
     * Shortcut for new.
     */
    static ARGF opCall(string[] args)
    {
        return new ARGF(args);
    }

    this(string[] args)
    {
        if (args.length) {
            foreach_reverse (filename; args)
                argv ~= filename;
            argc = argv.length;
            setStream;
        } else {
            stream = din;
            dined = true;
        }
    }

    /**
     * Returns the current file path.
     */
    pure string path()
    {
        if (dined)
            return null;

        return argv[argc];
    }

    /**
     * Returns the current file.
     */
    nothrow Stream file()
    {
        return stream;
    }

    /**
     * Skips current file and sets next file.
     */
    ARGF skip()
    {
        if (!dined) {
            stream.close;
            setStream;
        }

        return this;
    }

    /**
     * Supports for foreach(iterate through the stream line-by-line).
     */
    int opApply(int delegate(ref char[]) dg)
    {
        int result;

        while (true) {
            foreach (char[] line; stream) {
                result = dg(line);
                if (result)
                    break;
            }
            if (dined || !argc)
                break;

            skip;
        }

        return result;
    }

    /**
     * Supports for foreach with line count(iterate through the stream line-by-line).
     */
    int opApply(int delegate(ref size_t, ref char[]) dg)
    {
        int    result;
        size_t lineno;

        while (true) {
            foreach (char[] line; stream) {
                lineno++;
                result = dg(lineno, line);
                if (result)
                    break;
            }
            if (dined || !argc)
                break;

            skip;
        }

        return result;
    }

    /**
     * Reads the entire streams and returns joined it as a string
     */
    string toString()
    {
        string result;

        while (true) {
            result ~= stream.toString;
            if (dined || !argc)
                break;

            skip;
        }

        return result;
    }


  private:
    void setStream()
    {
        if (argc)
            stream = new BufferedFile(argv[--argc], FileMode.In);
    }
}