module pind.samples.ja.fibers.fibers_6; import std.stdio; import std.string; import std.format; import std.exception; import std.conv; import std.array; import core.thread; struct User { string name; string email; uint age; } /* このFiberサブクラスは、ユーザーの * サインオンフローを表している。 */ class SignOnFlow : Fiber { /* このフローで最後に読み込まれたデータ。 */ string inputData_; /* ユーザーオブジェクトを構築するための情報。 */ string name; string email; uint age; this() { /* 'run'メンバー関数を * ファイバーの開始点として設定する。 */ super(&run); } void run() { /* 最初の入力は名前。 */ name = inputData_; Fiber.yield(); /* 2番目の入力はメールアドレス。 */ email = inputData_; Fiber.yield(); /* 最後の入力は年齢。 */ age = inputData_.to!uint; /* この時点で、ユーザーを構築するためのすべての情報を * 収集した。ここで'Fiber.yield()'ではなく * 'return'を実行する。その結果、このファイバーの状態は * Fiber.State.TERMになる。 */ } /* このプロパティ関数は、呼び出し元からデータを受け取る * ためのもの。 */ void inputData(string data) { inputData_ = data; } /* このプロパティ関数は、ユーザーを構築し、 * それを呼び出し元に返すためのもの。 */ User user() const { return User(name, email, age); } } /* 特定のフローの入力から読み込まれたデータを表す。 */ struct FlowData { size_t id; string data; } /* フローに関連するデータを解析する。 */ FlowData parseFlowData(string line) { size_t id; string data; const items = line.formattedRead!" %s %s"(id, data); enforce(items == 2, format("Bad input '%s'.", line)); return FlowData(id, data); } void main() { User[] users; SignOnFlow[] flows; bool done = false; while (!done) { write("> "); string line = readln.strip; switch (line) { case "hi": /* 新しい接続のフローを開始する。 */ flows ~= new SignOnFlow(); writefln("Flow %s started.", flows.length - 1); break; case "bye": /* プログラムを終了する。 */ done = true; break; default: /* 入力データをフローデータとして使用しようとする。 */ try { auto user = handleFlowData(line, flows); if (!user.name.empty) { users ~= user; writefln("Added user '%s'.", user.name); } } catch (Exception exc) { writefln("Error: %s", exc.msg); } break; } } writeln("Goodbye."); writefln("Users:\n%( %s\n%)", users); } /* 入力の所有ファイバーを識別し、その入力データを設定し、 * そのファイバーを再開する。フローが完了している場合は、 * 有効なフィールドを持つユーザーを返す。 */ User handleFlowData(string line, SignOnFlow[] flows) { const input = parseFlowData(line); const id = input.id; enforce(id < flows.length, format("Invalid id: %s.", id)); auto flow = flows[id]; enforce(flow.state == Fiber.State.HOLD, format("Flow %s is not runnable.", id)); /* フローデータを設定する。 */ flow.inputData = input.data; /* フローを再開する。 */ flow.call(); User user; if (flow.state == Fiber.State.TERM) { writefln("Flow %s has completed.", id); /* 戻り値を新しく作成されたユーザーに設定する。 */ user = flow.user; /* TODO: このファイバーの'flows'配列内のエントリは、 * 将来新しいフローで再利用可能。ただし、 * まず'flow.reset()'でリセットする必要がある。 */ } return user; }