Last active
December 26, 2017 02:37
-
-
Save Biotronic/0bc6048b880d67bfdca970453cc47cf9 to your computer and use it in GitHub Desktop.
Expressive C++ challenge in D
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Expressive C++17 challenge, in D | |
http://www.bfilipek.com/2017/09/the-expressive-cpp17-challenge.html | |
D features used: | |
Ranges (MmText, zip, map) | |
UFCS (all over) | |
String mixins (in find) | |
Wysiwyg strings | |
Scoped imports | |
Selective imports | |
Built-in unit tests | |
@safe and @trusted | |
Import expressions | |
*/ | |
import std.range : zip, array, front; | |
import std.algorithm : map, splitter, canFind, find, min; | |
import std.format : format; | |
@safe: | |
struct MmText { | |
import std.mmfile : MmFile; | |
import std.utf : decodeFront; | |
ulong _fileOffset; | |
MmFile _file; | |
@trusted | |
this(string filename) { | |
_file = new MmFile(filename); | |
} | |
dchar front() { | |
auto data = nextSlice; | |
return data.decodeFront; | |
} | |
void popFront() { | |
auto data = nextSlice; | |
size_t bytes; | |
data.decodeFront(bytes); | |
_fileOffset += bytes; | |
} | |
bool empty() { | |
return ulLength <= 0; | |
} | |
@trusted | |
ulong ulLength() const { | |
return _file.length - _fileOffset; | |
} | |
@property @trusted | |
private string nextSlice() { | |
auto offset = min(4, ulLength); | |
return cast(string)_file[_fileOffset.._fileOffset+offset]; | |
} | |
} unittest { | |
import std.algorithm.comparison : equal; | |
auto expected = import(__FILE__); | |
auto result = MmText(__FILE__); | |
assert(equal(result, expected)); | |
} | |
struct Record { | |
import std.typecons : Tuple; | |
Tuple!(string, `name`, string, `value`)[] payload; | |
this(string[] headers, string[] values) { | |
assert(headers.length == values.length); | |
payload = headers.zip(values).map!(typeof(payload[0])).array; | |
} | |
ref string opIndex(string column) { | |
return payload.find!`a.name == b`(column).front.value; | |
} unittest { | |
auto r = Record([`a`],[`b`]); | |
assert(r[`a`] == `b`); | |
r[`a`] = `c`; | |
assert(r[`a`] == `c`); | |
} | |
string toString() const { | |
return format(`%-(%s,%)`, payload.map!(a => escape(a.value))); | |
} unittest { | |
auto r = Record([`a`],[`b`]); | |
assert(r.toString == `b`); | |
r = Record([`a`,`b`],[`c`,`d`]); | |
assert(r.toString == `c,d`); | |
r = Record([`a`,`b`],[`c`,`d,e`]); | |
assert(r.toString == `c,"d,e"`); | |
} | |
} | |
string escape(string s) { | |
import std.string : replace; | |
if (s.canFind(`"`, `,`)) | |
return `"%s"`.format(s.replace(`"`, `""`)); | |
return s; | |
} unittest { | |
assert(``.escape == ``); | |
assert(`a`.escape == `a`); | |
assert(`,`.escape == `","`); | |
assert(`"`.escape == `""""`); | |
assert(`",`.escape == `""","`); | |
} | |
void main(string[] args) { | |
import std.stdio : writefln, writeln; | |
import std.file : readText; | |
import std.csv : csvReader; | |
if (args.length < 4) { | |
writeln(`Please provide input file, column, and replacement.`); | |
return; | |
} | |
const filename = args[1]; | |
const column = args[2]; | |
const replacement = args[3]; | |
auto data = MmText(filename).csvReader!string; | |
auto headers = data.front.array; | |
data.popFront(); | |
writefln(`%-(%s,%)`, headers); | |
writefln("%-(%s\n%)", data | |
.map!(a => Record(headers, a.array)) | |
.map!(a => (a[column] = replacement, a))); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment