module stri;

import std.format : format;
import std.algorithm : map;

auto parse(string s) {
    import std.string;
    import std.algorithm : find;
    import std.typecons;

    string fmt = s;
    string[] ids;
    auto subs = s.find("${"); // "${def}gh${i}..."
    while (!subs.empty) {
        auto ends = subs.find("}"); // "}gh${i}..."
        if (ends.empty) {
            // TODO assert here
            break;
        }
        auto _id = subs[2..$-ends.length]; // "def"
        auto idFmt = _id.find("%");
        auto id = _id[0 .. $-idFmt.length];
        ids ~= id; // [..., "def"]
        auto quote = subs[0..$+1-ends.length]; // "${def}"
        fmt = fmt.replace(quote, idFmt.empty ? "%s" : idFmt);
        subs = ends.find("${"); // "${i}..."
    }
    return tuple!("ids", "fmt")(ids, fmt);
}

mixin template s(string sfmt) {
    enum _ret = parse(sfmt);
    mixin(format!`immutable str = format!"%s"(%-(%s, %));`(_ret.fmt, _ret.ids));
}

unittest
{
    import std.stdio;
    auto a = 1;
    struct A {
        static a = 0.123;
    }

    enum _a0 = "D-lang";
    mixin s!"${a} is one. ${_a0} is nice. ${a%03d}, ${A.a%.3f}" i;
    writeln(i.str);
    assert(i.str == "1 is one. D-lang is nice. 001, 0.123");
}