From 0e232822faa2028eef3f10809afd6069e4607fe4 Mon Sep 17 00:00:00 2001 From: "Tristan B. Velloza Kildaire" Date: Mon, 15 Apr 2024 10:45:16 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Feature:=20Prompting=20framework=20?= =?UTF-8?q?(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prompter - Added Prompt - Added * Prompter - Fixed source `File` check Prompter (unittests) - Added a unittest * Prompter - Fixed `prompt()` Prompter (unittests) - `flush()` pipe write end * Prompter (unittests) - Cleaned up * Prompter (unittests) - Cleaned up * Prompter (unittests) - Fixed missing import * Prompter - Added more docs * Prompter - Added more docs * Prompt - Documented * Prompter - Cleand up * Prompter (unittests) - Removed unittest * README - Updated --- README.md | 2 + source/niknaks/mechanisms.d | 201 ++++++++++++++++++++++++++++++++++++ 2 files changed, 203 insertions(+) diff --git a/README.md b/README.md index 3214db2..0ce10e2 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ is expected to grow over time. * `niknaks.containers` * Some useful container types * Things such as `CacheMap` +* `niknaks.mechanisms` + * User-defined input prompter, retry mechanisms ## License diff --git a/source/niknaks/mechanisms.d b/source/niknaks/mechanisms.d index 46abc33..69a3f51 100644 --- a/source/niknaks/mechanisms.d +++ b/source/niknaks/mechanisms.d @@ -9,6 +9,8 @@ import std.functional : toDelegate; import std.datetime : Duration; import std.datetime.stopwatch : StopWatch, AutoStart; import core.thread : Thread; +import std.stdio : File, write; +import std.string : strip; /** * A verdict-providing function @@ -291,4 +293,203 @@ unittest } } +/** + * A user-defined prompt + */ +public struct Prompt +{ + private string query; + private string value; + /** + * Constructs a new prompt + * with the given query + * + * Params: + * query = the prompt + * query itself + */ + this(string query) + { + this.query = query; + } + + /** + * Gets the prompt query + * + * Returns: the query + */ + public string getQuery() + { + return this.query; + } + + /** + * Retrieves this prompt's + * answer + * + * Returns: the answer + */ + public string getValue() + { + return this.value; + } + + /** + * Fill this prompt's + * query with a corresponding + * answer + * + * Params: + * value = the answer + */ + public void fill(string value) + { + this.value = value; + } +} + +/** + * A prompting mechanism + * which can be filled up + * with questions and a + * file-based source to + * read answers in from + * and associate with + * their original respective + * questions + */ +public class Prompter +{ + /** + * Source file + */ + private File source; + + /** + * Whether or not to close + * the source file on destruction + */ + private bool closeOnDestruct; + + /** + * Prompts to query by + */ + private Prompt[] prompts; + + /** + * Constructs a new prompter + * with the given file source + * from where the input is to + * be read from. + * + * Params: + * source = the `File` to + * read from + * closeOnDestruct = if + * set to `true` then on + * destruction we will close + * the source, if `false` it + * is left untouched + * + * Throws: + * Exception if the provided + * `File` is not open + */ + this(File source, bool closeOnDestruct = false) + { + if(!source.isOpen()) + { + throw new Exception("Source not open"); + } + + this.closeOnDestruct = closeOnDestruct; + this.source = source; + } + + /** + * Appends the given prompt + * + * Params: + * prompt = the prompt + */ + public void addPrompt(Prompt prompt) + { + this.prompts ~= prompt; + } + + /** + * Performs the prompting + * by querying each attached + * prompt for an answer + * which is then associated + * with the given prompt + * + * Returns: the answered + * prompts + */ + public Prompt[] prompt() + { + char[] buff; + + foreach(ref Prompt prompt; this.prompts) + { + scope(exit) + { + buff.length = 0; + } + + // Perform the query + write(prompt.getQuery()); + this.source.readln(buff); + + // Fill answer into prompt + prompt.fill(strip(cast(string)buff)); + } + + return this.prompts; + } + + /** + * Destructor + */ + ~this() + { + if(this.closeOnDestruct) + { + this.source.close(); + } + } +} + +version(unittest) +{ + import std.process : pipe, Pipe; + import std.conv : to; + import std.stdio : writeln; +} + +unittest +{ + Pipe pipe = pipe(); + + // Create a prompter with some prompts + Prompter p = new Prompter(pipe.readEnd()); + p.addPrompt(Prompt("What is your name?")); + p.addPrompt(Prompt("How old are you")); + + // Fill up pipe with data for read end + File writeEnd = pipe.writeEnd(); + writeEnd.writeln("Tristan Brice Velloza Kildaire"); + writeEnd.writeln(1); + writeEnd.flush(); + + // Perform the prompt and get the + // answers back out + Prompt[] ans = p.prompt(); + + writeln(ans); + + assert(ans[0].getValue() == "Tristan Brice Velloza Kildaire"); + assert(to!(int)(ans[1].getValue()) == 1); // TODO: Allow union conversion later +} \ No newline at end of file