replay-journal.dhover×265all
#!/usr/bin/env dub
/+ dub.sdl:
    name "replay_journal"
    targetPath "build"
    buildType "checked" {
        buildOptions "optimize" "inline" "debugInfo"
    }
+/
/**
 * Durable execution by replay — the mechanism in ~200 lines.
 *
 * A workflow is an ordinary function that performs its effects through one
 * handler, `Durable.step`. Each step has a stable key and an argument
 * fingerprint. On a live run the handler executes the step and appends the
 * result to an append-only journal. On a resumed run the same function runs
 * again from the top; while the journal still has entries the handler answers
 * each step from the journal instead of executing it, checking that the key
 * and fingerprint match what was recorded, and only once the journal is
 * exhausted does it go live again.
 *
 * The program demonstrates the two properties every system in the catalog
 * relies on (see docs/research/algebraic-effects/durable-execution/concepts.md
 * § "Replay" and § "Determinism"):
 *
 *   1. Crash-at-every-index: cutting the journal after any entry and resuming
 *      re-executes exactly the steps after the cut, and the final journal is
 *      identical to the uninterrupted run.
 *   2. Divergence detection: a step whose key or arguments differ from the
 *      recorded entry is refused, loudly, instead of silently re-run.
 *
 * It is an illustration of the replay contract, not a prototype of a library.
 *
 * Run with: `dub run --single replay-journal.d`
 */
module 
(module) replay_journal

Durable execution by replay — the mechanism in ~200 lines.

A workflow is an ordinary function that performs its effects through one handler, Durable.step. Each step has a stable key and an argument fingerprint. On a live run the handler executes the step and appends the result to an append-only journal. On a resumed run the same function runs again from the top; while the journal still has entries the handler answers each step from the journal instead of executing it, checking that the key and fingerprint match what was recorded, and only once the journal is exhausted does it go live again.

The program demonstrates the two properties every system in the catalog relies on (see docs/research/algebraic-effects/durable-execution/concepts.md § "Replay" and § "Determinism"):

  1. Crash-at-every-index: cutting the journal after any entry and resuming re-executes exactly the steps after the cut, and the final journal is identical to the uninterrupted run.

  2. Divergence detection: a step whose key or arguments differ from the recorded entry is refused, loudly, instead of silently re-run.

It is an illustration of the replay contract, not a prototype of a library.

Run with: dub run --single replay-journal.d

replay_journal
;
import
(package) std
std
.
(module) std.algorithm

This package implements generic algorithms oriented towards the processing of sequences. Sequences processed by these functions define range-based interfaces. See also Reference on ranges and tutorial on ranges.

Algorithms are categorized into the following submodules:

Submodule Functions

| Searching | all any balancedParens boyerMooreFinder canFind commonPrefix count countUntil endsWith find findAdjacent findAmong findSkip findSplit findSplitAfter findSplitBefore minCount maxCount minElement maxElement minIndex maxIndex minPos maxPos skipOver startsWith until |

| Comparison | among castSwitch clamp cmp either equal isPermutation isSameLength levenshteinDistance levenshteinDistanceAndPath max min mismatch predSwitch |

| Iteration | cache cacheBidirectional chunkBy cumulativeFold each filter filterBidirectional fold group joiner map mean permutations reduce splitWhen splitter substitute sum uniq |

| Sorting | completeSort isPartitioned isSorted isStrictlyMonotonic ordered strictlyOrdered makeIndex merge multiSort nextEvenPermutation nextPermutation nthPermutation partialSort partition partition3 schwartzSort sort topN topNCopy topNIndex |

| Set operations (setops) | cartesianProduct largestPartialIntersection largestPartialIntersectionWeighted multiwayMerge multiwayUnion setDifference setIntersection setSymmetricDifference |

| Mutation | bringToFront copy fill initializeAll move moveAll moveSome moveEmplace moveEmplaceAll moveEmplaceSome remove reverse strip stripLeft stripRight swap swapRanges uninitializedFill |

Many functions in this package are parameterized with a predicate. The predicate may be any suitable callable type (a function, a delegate, a functor, or a lambda), or a compile-time string. The string may consist of any legal D expression that uses the symbol a (for unary functions) or the symbols a and b (for binary functions). These names will NOT interfere with other homonym symbols in user code because they are evaluated in a different context. The default for all binary comparison predicates is "a == b" for unordered operations and "a < b" for ordered operations.

Example

int[] a = ...;
static bool greater(int a, int b)
{
    return a > b;
}
sort!greater(a);           // predicate as alias
sort!((a, b) => a > b)(a); // predicate as a lambda.
sort!"a > b"(a);           // predicate as string
                           // (no ambiguity with array name)
sort(a);                   // no predicate, "a < b" is implicit

Source

std/algorithm/package.d

@copyrightAndrei Alexandrescu 2008-.@licenseBoost License 1.0.@authorsAndrei Alexandrescu
algorithm
:
(alias template) replay_journal.all = std.algorithm.searching.all(alias pred = "a")

Checks if all of the elements satisfy pred.

all
,
(alias template) replay_journal.equal = std.algorithm.comparison.equal(alias pred = "a == b")

Compares two or more ranges for equality, as defined by predicate pred (which is == by default).

equal
,
(alias template) replay_journal.map = std.algorithm.iteration.map(fun...) if (fun.length >= 1)

Implements the homonym function (also known as transform) present in many languages of functional flavor. The call ``map!(fun)(range) returns a range of which elements are obtained by applying fun(a) left to right for all elements a in range. The original ranges are not changed. Evaluation is done lazily.

@paramfun one or more transformation functions@seeMap (higher-order function)
map
;
import
(package) std
std
.
(module) std.array

Functions and types that manipulate built-in arrays and associative arrays.

This module provides all kinds of functions to create, manipulate or convert arrays:

Function Name Description

| array | Returns a copy of the input in a newly allocated dynamic array. | | appender | Returns a new Appender or RefAppender initialized with a given array. | | assocArray | Returns a newly allocated associative array from a range/ranges of keys and values. | | byPair | Construct a range iterating over an associative array by key/value tuples. | | insertInPlace | Inserts into an existing array at a given position. | | join | Concatenates a range of ranges into one array. | | minimallyInitializedArray | Returns a new array of type T. | | replace | Returns a new array with all occurrences of a certain subrange replaced. | | replaceFirst | Returns a new array with the first occurrence of a certain subrange replaced. | | replaceInPlace | Replaces all occurrences of a certain subrange and puts the result into a given array. | | replaceInto | Replaces all occurrences of a certain subrange and puts the result into an output range. | | replaceLast | Returns a new array with the last occurrence of a certain subrange replaced. | | replaceSlice | Returns a new array with a given slice replaced. | | replicate | Creates a new array out of several copies of an input array or range. | | sameHead | Checks if the initial segments of two arrays refer to the same place in memory. | | sameTail | Checks if the final segments of two arrays refer to the same place in memory. | | split | Eagerly split a range or string into an array. | | staticArray | Creates a new static array from given data. | | uninitializedArray | Returns a new array of type T without initializing its elements. |

Source

std/array.d

@copyrightCopyright Andrei Alexandrescu 2008- and Jonathan M Davis 2011-.@licenseBoost License 1.0.@authorsAndrei Alexandrescu and Jonathan M Davis
array
:
(alias template) replay_journal.array = std.array.array(Range)(Range r) if (isIterable!Range && !isAutodecodableString!Range && !isInfinite!Range)

Allocates an array and initializes it with copies of the elements of range r.

Narrow strings are handled as follows:

  • If autodecoding is turned on (default), then they are handled as a separate overload.

  • If autodecoding is turned off, then this is equivalent to duplicating the array.

@paramr range (or aggregate with opApply function) whose elements are copied into the allocated array@returnsallocated and initialized array
array
;
import
(package) std
std
.
(module) std.conv

A one-stop shop for converting values from one type to another.

Category Functions
Generic asOriginalType castFrom parse to toChars bitCast
Strings text wtext dtext writeText writeWText writeDText hexString
Numeric octal roundTo signed unsigned
Exceptions ConvException ConvOverflowException

Source

std/conv.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Shin Fujishiro, Adam D. Ruppe, Kenji Hara
conv
:
(alias template) replay_journal.text = std.conv.text(T...)(T args) if (T.length > 0)

Convenience functions for converting one or more arguments of any type into text (the three character widths).

text
,
(alias template) replay_journal.to = std.conv.to(T)

The to template converts a value from one type to another. The source type is deduced and the target type must be specified, for example the expression to`!int(42.0)` converts the number 42 from `double` to `int`. The conversion is "safe", i.e., it checks for overflow; to!int(4.2e10) would throw the ConvOverflowException exception. Overflow checks are only inserted when necessary, e.g., ``to!double(42) does not do any checking because any int fits in a double.

Conversions from string to numeric types differ from the C equivalents atoi() and atol() by checking for overflow and not allowing whitespace.

For conversion of strings to signed types, the grammar recognized is: Integer: Sign UnsignedInteger UnsignedInteger Sign: + -

For conversion to unsigned types, the grammar recognized is: UnsignedInteger: DecimalDigit DecimalDigit UnsignedInteger

to
;
import
(package) std
std
.
(package) std.digest
digest
.
(module) std.digest.sha

Computes SHA1 and SHA2 hashes of arbitrary data. SHA hashes are 20 to 64 byte quantities (depending on the SHA algorithm) that are like a checksum or CRC, but are more robust.

Category Functions

| Template API | SHA1 |

| OOP API | SHA1Digest |

| Helpers | sha1Of |

SHA2 comes in several different versions, all supported by this module: SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/224 and SHA-512/256.

This module conforms to the APIs defined in std.digest. To understand the differences between the template and the OOP API, see std.digest.

This module publicly imports std.digest and can be used as a stand-alone module.

CTFE

Digests do not work in CTFE

References

Source

std/digest/sha.d

Examples

//Template API
import std.digest.sha;

ubyte[20] hash1 = sha1Of("abc");
assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D");

ubyte[28] hash224 = sha224Of("abc");
assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");

//Feeding data
ubyte[1024] data;
SHA1 sha1;
sha1.start();
sha1.put(data[]);
sha1.start(); //Start again
sha1.put(data[]);
hash1 = sha1.finish();
//OOP API
import std.digest.sha;

auto sha1 = new SHA1Digest();
ubyte[] hash1 = sha1.digest("abc");
assert(toHexString(hash1) == "A9993E364706816ABA3E25717850C26C9CD0D89D");

auto sha224 = new SHA224Digest();
ubyte[] hash224 = sha224.digest("abc");
assert(toHexString(hash224) == "23097D223405D8228642A477BDA255B32AADBCE4BDA0B3F7E36C9DA7");

//Feeding data
ubyte[1024] data;
sha1.put(data[]);
sha1.reset(); //Start again
sha1.put(data[]);
hash1 = sha1.finish();
@licenseBoost License 1.0.@authors

The routines and algorithms are derived from the Secure Hash Signature Standard (SHS) (FIPS PUB 180-2).

Kai Nacke, Johannes Pfau, Nick Sabalausky

sha
:
(alias template) replay_journal.sha256Of = std.digest.sha.sha256Of(T...)(T data)

These are convenience aliases for digest using the SHA implementation.

sha256Of
;
import
(package) std
std
.
(module) std.digest

This module describes the digest APIs used in Phobos. All digests follow these APIs. Additionally, this module contains useful helper methods which can be used with every digest type.

Category Functions

| Template API | isDigest DigestType hasPeek hasBlockSize ExampleDigest digest hexDigest makeDigest |

| OOP API | Digest |

| Helper functions | toHexString secureEqual |

| Implementation helpers | digestLength WrapperDigest |

APIs

There are two APIs for digests: The template API and the OOP API. The template API uses structs and template helpers like isDigest. The OOP API implements digests as classes inheriting the Digest interface. All digests are named so that the template API struct is called "x" and the OOP API class is called "xDigest". For example we have MD5 <--> MD5Digest, CRC32 <--> CRC32Digest, etc.

The template API is slightly more efficient. It does not have to allocate memory dynamically, all memory is allocated on the stack. The OOP API has to allocate in the finish method if no buffer was provided. If you provide a buffer to the OOP APIs finish function, it doesn't allocate, but the Digest classes still have to be created using new which allocates them using the GC.

The OOP API is useful to change the digest function and/or digest backend at 'runtime'. The benefit here is that switching e.g. Phobos MD5Digest and an OpenSSLMD5Digest implementation is ABI compatible.

If just one specific digest type and backend is needed, the template API is usually a good fit. In this simplest case, the template API can even be used without templates: Just use the "x" structs directly.

Source

std/digest/package.d

CTFE

Digests do not work in CTFE

TODO

Digesting single bits (as opposed to bytes) is not implemented. This will be done as another template constraint helper (hasBitDigesting!T) and an additional interface (BitDigest)

Examples

import std.digest.crc;

//Simple example
char[8] hexHash = hexDigest!CRC32("The quick brown fox jumps over the lazy dog");
assert(hexHash == "39A34F41");

//Simple example, using the API manually
CRC32 context = makeDigest!CRC32();
context.put(cast(ubyte[])"The quick brown fox jumps over the lazy dog");
ubyte[4] hash = context.finish();
assert(toHexString(hash) == "39A34F41");
//Generating the hashes of a file, idiomatic D way
import std.digest.crc, std.digest.md, std.digest.sha;
import std.stdio;

// Digests a file and prints the result.
void digestFile(Hash)(string filename)
if (isDigest!Hash)
{
    auto file = File(filename);
    auto result = digest!Hash(file.byChunk(4096 * 1024));
    writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
}

void main(string[] args)
{
    foreach (name; args[1 .. $])
    {
        digestFile!MD5(name);
        digestFile!SHA1(name);
        digestFile!CRC32(name);
    }
}
//Generating the hashes of a file using the template API
import std.digest.crc, std.digest.md, std.digest.sha;
import std.stdio;
// Digests a file and prints the result.
void digestFile(Hash)(ref Hash hash, string filename)
if (isDigest!Hash)
{
    File file = File(filename);

    //As digests imlement OutputRange, we could use std.algorithm.copy
    //Let's do it manually for now
    foreach (buffer; file.byChunk(4096 * 1024))
        hash.put(buffer);

    auto result = hash.finish();
    writefln("%s (%s) = %s", Hash.stringof, filename, toHexString(result));
}

void uMain(string[] args)
{
    MD5 md5;
    SHA1 sha1;
    CRC32 crc32;

    md5.start();
    sha1.start();
    crc32.start();

    foreach (arg; args[1 .. $])
    {
        digestFile(md5, arg);
        digestFile(sha1, arg);
        digestFile(crc32, arg);
    }
}
import std.digest.crc, std.digest.md, std.digest.sha;
import std.stdio;

// Digests a file and prints the result.
void digestFile(Digest hash, string filename)
{
    File file = File(filename);

    //As digests implement OutputRange, we could use std.algorithm.copy
    //Let's do it manually for now
    foreach (buffer; file.byChunk(4096 * 1024))
      hash.put(buffer);

    ubyte[] result = hash.finish();
    writefln("%s (%s) = %s", typeid(hash).toString(), filename, toHexString(result));
}

void umain(string[] args)
{
    auto md5 = new MD5Digest();
    auto sha1 = new SHA1Digest();
    auto crc32 = new CRC32Digest();

    foreach (arg; args[1 .. $])
    {
      digestFile(md5, arg);
      digestFile(sha1, arg);
      digestFile(crc32, arg);
    }
}
@licenseBoost License 1.0.@authorsJohannes Pfau
digest
:
(alias template) replay_journal.toHexString = std.digest.toHexString(Order order = Order.increasing, ulong num, LetterCase letterCase = LetterCase.upper)(const ubyte[num] digest)

Used to convert a hash value (a static or dynamic array of ubytes) to a string. Can be used with the OOP and with the template API.

The additional order parameter can be used to specify the order of the input data. By default the data is processed in increasing order, starting at index 0. To process it in the opposite order, pass Order.decreasing as a parameter.

The additional letterCase parameter can be used to specify the case of the output data. By default the output is in upper case. To change it to the lower case pass LetterCase.lower as a parameter.

Note

The function overloads returning a string allocate their return values using the GC. The versions returning static arrays use pass-by-value for the return value, effectively avoiding dynamic allocation.

toHexString
;
import
(package) std
std
.
(module) std.exception

This module defines functions related to exceptions and general error handling. It also defines functions intended to aid in unit testing.

Category Functions
Assumptions assertNotThrown assertThrown assumeUnique assumeWontThrow mayPointTo
Enforce doesPointTo enforce errnoEnforce
Handlers collectException collectExceptionMsg ifThrown handle
Other basicExceptionCtors emptyExceptionMsg ErrnoException RangePrimitive

Source

std/exception.d

Examples

Synopis

import core.stdc.stdlib : malloc, free;
import std.algorithm.comparison : equal;
import std.algorithm.iteration : map, splitter;
import std.algorithm.searching : endsWith;
import std.conv : ConvException, to;
import std.range : front, retro;

// use enforce like assert
int a = 3;
enforce(a > 2, "a needs to be higher than 2.");

// enforce can throw a custom exception
enforce!ConvException(a > 2, "a needs to be higher than 2.");

// enforce will return it's input
enum size = 42;
auto memory = enforce(malloc(size), "malloc failed")[0 .. size];
scope(exit) free(memory.ptr);

// collectException can be used to test for exceptions
Exception e = collectException("abc".to!int);
assert(e.file.endsWith("conv.d"));

// and just for the exception message
string msg = collectExceptionMsg("abc".to!int);
assert(msg == "Unexpected 'a' when converting from type string to type int");

// assertThrown can be used to assert that an exception is thrown
assertThrown!ConvException("abc".to!int);

// ifThrown can be used to provide a default value if an exception is thrown
assert("x".to!int().ifThrown(0) == 0);

// handle is a more advanced version of ifThrown for ranges
auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a));
auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);
assert(h.equal([12, 0, 54]));
assertThrown!ConvException(h.retro.equal([54, 0, 12]));

// basicExceptionCtors avoids the boilerplate when creating custom exceptions
static class MeaCulpa : Exception
{
    mixin basicExceptionCtors;
}
e = collectException((){throw new MeaCulpa("diagnostic message");}());
assert(e.msg == "diagnostic message");
assert(e.file == __FILE__);
assert(e.line == __LINE__ - 3);

// assumeWontThrow can be used to cast throwing code into `nothrow`
void exceptionFreeCode() nothrow
{
    // auto-decoding only throws if an invalid UTF char is given
    assumeWontThrow("abc".front);
}

// assumeUnique can be used to cast mutable instance to an `immutable` one
// use with care
char[] str = "  mutable".dup;
str[0 .. 2] = "im";
immutable res = assumeUnique(str);
assert(res == "immutable");
@copyrightCopyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-.@licenseBoost License 1.0@authorsAndrei Alexandrescu and Jonathan M Davis
exception
:
(alias template) replay_journal.enforce = std.exception.enforce(E : Throwable = Exception) if (is(typeof(new E("", string.init, size_t.init)) : Throwable) || is(typeof(new E(string.init, size_t.init)) : Throwable))

Enforces that the given value is true. If the given value is false, an exception is thrown. The

  • msg - error message as a string

  • dg - custom delegate that return a string and is only called if an exception occurred

  • ex - custom exception to be thrown. It is lazy and is only created if an exception occurred

@paramvalue The value to test.@paramE Exception type to throw if the value evaluates to false.@parammsg The error message to put in the exception if it is thrown.@paramdg The delegate to be called if the value evaluates to false.@paramex The exception to throw if the value evaluates to false.@paramfile The source file of the caller.@paramline The line number of the caller.@returns

value, if cast(bool) value is true. Otherwise, depending on the chosen overload, new Exception(msg), dg() or ex is thrown.

enforce is used to throw exceptions and is therefore intended to aid in error handling. It is not intended for verifying the logic of your program - that is what assert is for.

Do not use enforce inside of contracts (i.e. inside of in and out blocks and invariants), because contracts are compiled out when compiling with -release.

If a delegate is passed, the safety and purity of this function are inferred from Dg's safety and purity.

enforce
;
import
(package) std
std
.
(module) std.stdio
Category Symbols
File handles _popen File isFileHandle openNetwork stderr stdin stdout
Reading chunks lines readf readfln readln
Writing toFile write writef writefln writeln
Misc KeepTerminator LockType StdioException

Standard I/O functions that extend core.stdc.stdio. core.stdc.stdio is publically imported when importing std.stdio.

There are three layers of I/O:

  1. The lowest layer is the operating system layer. The two main schemes are Windows and Posix.

  2. C's stdio.h which unifies the two operating system schemes.

  3. std.stdio, this module, unifies the various stdio.h implementations into a high level package for D programs.

Source

std/stdio.d

@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Alex Rønne Petersen
stdio
:
(alias template) replay_journal.writefln = std.stdio.writefln(alias fmt, A...)(A args) if (isSomeString!(typeof(fmt)))

Equivalent to writef(fmt, args, '\n').

writefln
,
(alias template) replay_journal.writeln = std.stdio.writeln(T...)(T args)

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
;
import
(package) std
std
.
(module) std.string

String handling functions.

Category Functions
Searching
column
indexOf
indexOfAny
indexOfNeither
lastIndexOf
lastIndexOfAny
lastIndexOfNeither
Comparison
isNumeric
Mutation
capitalize
Pruning and Filling
center
chomp
chompPrefix
chop
detabber
detab
entab
entabber
leftJustify
outdent
rightJustify
strip
stripLeft
stripRight
wrap
Substitution
abbrev
soundex
soundexer
succ
tr
translate
Miscellaneous
assumeUTF
fromStringz
lineSplitter
representation
splitLines
toStringz
Objects of types string, wstring, and dstring are value types
and cannot be mutated element-by-element. For using mutation during building
strings, use char[], wchar[], or dchar[]. The xxxstring
types are preferable because they don't exhibit undesired aliasing, thus
making code more robust.

The following functions are publicly imported:

Module Functions
Publicly imported functions
std.algorithm
cmp, std,algorithm,comparison
count, std,algorithm,searching
endsWith, std,algorithm,searching
startsWith, std,algorithm,searching
std.array
join, std,array
replace, std,array
replaceInPlace, std,array
split, std,array
empty, std,array
std.format
format, std,format
sformat, std,format
std.uni
icmp, std,uni
toLower, std,uni
toLowerInPlace, std,uni
toUpper, std,uni
toUpperInPlace, std,uni
There is a rich set of functions for string handling defined in other modules.
Functions related to Unicode and ASCII are found in std.uni
and std.ascii, respectively. Other functions that have a
wider generality than just strings can be found in std.algorithm
and std.range.

Source

std/string.d

@seestd.algorithm and std.range for generic range algorithms , std.ascii for functions that work with ASCII strings , std.uni for functions that work with unicode strings@copyrightCopyright The D Language Foundation 2007-.@licenseBoost License 1.0.@authorsWalter Bright, Andrei Alexandrescu, Jonathan M Davis, and David L. 'SpottedTiger' Davis
string
:
(alias template) replay_journal.format = std.format.format(Char, Args...)(in Char[] fmt, Args args) if (isSomeChar!Char)

Converts its arguments according to a format string into a string.

The second version of format takes the format string as template argument. In this case, it is checked for consistency at compile-time and produces slightly faster code, because the length of the output buffer can be estimated in advance.

@paramfmt a format string@paramargs a variadic list of arguments to be formatted@paramChar character type of fmt@paramArgs a variadic list of types of the arguments@returnsThe formatted string.@throwsA FormatException if formatting did not succeed.@seesformat for a variant, that tries to avoid garbage collection.
format
;
// ── the journal ───────────────────────────────────────────────────────────── /// One completed step. `fingerprint` is what makes replay detect a changed /// program: the same key with different arguments is a different step. struct
(struct) replay_journal.Entry

One completed step. fingerprint is what makes replay detect a changed program: the same key with different arguments is a different step.

Entry
{
(alias) object.string = string
string
(field) string replay_journal.Entry.key
key
;
(alias) object.string = string
string
(field) string replay_journal.Entry.fingerprint
fingerprint
;
(alias) object.string = string
string
(field) string replay_journal.Entry.result
result
;
} /// A `key` plus `args` fingerprint — the identity a step is matched on.
(alias) object.string = string
string
string replay_journal.fingerprintOf(string key, string args) @safe

A key plus args fingerprint — the identity a step is matched on.

fingerprintOf
(
(alias) object.string = string
string
(parameter) string key
key
,
(alias) object.string = string
string
(parameter) string args
args
) @safe
=>
ubyte[32] std.digest.sha.sha256Of!string(string __param_0) pure nothrow @nogc @safe

These are convenience aliases for digest using the SHA implementation.

sha256Of
(
(parameter) string key
key
~ "\0" ~
(parameter) string args
args
).
char[64] std.digest.toHexString!(Order.increasing, 32LU, LetterCase.upper)(const(ubyte[32]) digest) pure nothrow @nogc @safe

Used to convert a hash value (a static or dynamic array of ubytes) to a string. Can be used with the OOP and with the template API.

The additional order parameter can be used to specify the order of the input data. By default the data is processed in increasing order, starting at index 0. To process it in the opposite order, pass Order.decreasing as a parameter.

The additional letterCase parameter can be used to specify the case of the output data. By default the output is in upper case. To change it to the lower case pass LetterCase.lower as a parameter.

Note

The function overloads returning a string allocate their return values using the GC. The versions returning static arrays use pass-by-value for the return value, effectively avoiding dynamic allocation.

toHexString
[0 .. 16].
string object.idup!char(char[] a) pure nothrow @property @safe

Provide the .idup array property, which creates an immutable duplicate.

idup
;
/// Raised when the program diverges from its journal. class
(class) replay_journal.Divergence

Raised when the program diverges from its journal.

Divergence
:
(class) object.Exception

The base class of all errors that are safe to catch and handle.

In principle, only thrown objects derived from this class are safe to catch inside a catch block. Thrown objects not derived from Exception represent runtime errors that should not be caught, as certain runtime guarantees may not hold, making it unsafe to continue program execution.

Examples

bool gotCaught;
try
{
    throw new Exception("msg");
}
catch (Exception e)
{
    gotCaught = true;
    assert(e.msg == "msg");
}
assert(gotCaught);
Exception
{ this(
replay_journal.Divergence replay_journal.Divergence.this(string msg) @safe
string
(parameter) string msg
msg
) @safe { super(
(parameter) string msg
msg
); }
} // ── the handler ───────────────────────────────────────────────────────────── /// Answers steps from the journal while it lasts, then executes them live. struct
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
{
(struct) replay_journal.Entry

One completed step. fingerprint is what makes replay detect a changed program: the same key with different arguments is a different step.

Entry
[]
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
; /// the entries recorded so far (input: the prior run's)
(alias) object.size_t = ulong
size_t
(field) ulong replay_journal.Durable.cursor

how many of them replay has consumed

cursor
; /// how many of them replay has consumed
(alias) object.size_t = ulong
size_t
(field) ulong replay_journal.Durable.executed

steps that actually ran in this invocation

executed
; /// steps that actually ran in this invocation
/// Runs (or replays) one step. `live` is only invoked when the journal has /// no entry for this position.
(alias) object.string = string
string
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
(
(alias) object.string = string
string
(parameter) string key
key
,
(alias) object.string = string
string
(parameter) string args
args
, scope
(alias) object.string = string
string
delegate()
(parameter) string delegate() live
live
)
{ const
(local variable) const(string) fp
fp
=
string replay_journal.fingerprintOf(string key, string args) @safe

A key plus args fingerprint — the identity a step is matched on.

fingerprintOf
(
(parameter) string key
key
,
(parameter) string args
args
);
if (
(field) ulong replay_journal.Durable.cursor

how many of them replay has consumed

cursor
<
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
.
(field) ulong replay_journal.Entry[].length
length
)
{ const
(local variable) const(replay_journal.Entry) recorded
recorded
=
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
[
(field) ulong replay_journal.Durable.cursor

how many of them replay has consumed

cursor
];
if (
(local variable) const(replay_journal.Entry) recorded
recorded
.
(field) string replay_journal.Entry.key
key
!=
(parameter) string key
key
||
(local variable) const(replay_journal.Entry) recorded
recorded
.
(field) string replay_journal.Entry.fingerprint
fingerprint
!=
(local variable) const(string) fp
fp
)
throw new
(class) replay_journal.Divergence

Raised when the program diverges from its journal.

Divergence
(
string std.format.format!("journal entry %d is `%s` (%s) but the program asked for `%s` (%s)", ulong, string, string, string, string)(ulong __param_0, string __param_1, string __param_2, string __param_3, string __param_4) pure @safe

Examples

The format string can be checked at compile-time:

auto s = format!"%s is %s"("Pi", 3.14);
assert(s == "Pi is 3.14");

// This line doesn't compile, because 3.14 cannot be formatted with %d:
// s = format!"%s is %d"("Pi", 3.14);
format
!"journal entry %d is `%s` (%s) but the program asked for `%s` (%s)"(
(field) ulong replay_journal.Durable.cursor

how many of them replay has consumed

cursor
,
(local variable) const(replay_journal.Entry) recorded
recorded
.
(field) string replay_journal.Entry.key
key
,
(local variable) const(replay_journal.Entry) recorded
recorded
.
(field) string replay_journal.Entry.fingerprint
fingerprint
,
(parameter) string key
key
,
(local variable) const(string) fp
fp
));
(field) ulong replay_journal.Durable.cursor

how many of them replay has consumed

cursor
++;
return
(local variable) const(replay_journal.Entry) recorded
recorded
.
(field) string replay_journal.Entry.result
result
;
} const
(local variable) const(string) result
result
=
(parameter) string delegate() live
live
();
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
~=
(struct) replay_journal.Entry

One completed step. fingerprint is what makes replay detect a changed program: the same key with different arguments is a different step.

Entry
(
(parameter) string key
key
,
(local variable) const(string) fp
fp
,
(local variable) const(string) result
result
);
(field) ulong replay_journal.Durable.cursor

how many of them replay has consumed

cursor
++;
(field) ulong replay_journal.Durable.executed

steps that actually ran in this invocation

executed
++;
return
(local variable) const(string) result
result
;
} } // ── the world ─────────────────────────────────────────────────────────────── /// The external state the workflow acts on. Each effect leaves a trace, so a /// re-executed step is visible. struct
(struct) replay_journal.World

The external state the workflow acts on. Each effect leaves a trace, so a re-executed step is visible.

World
{
(alias) object.string = string
string
[]
(field) string[] replay_journal.World.tags
tags
;
(alias) object.string = string
string
[]
(field) string[] replay_journal.World.pushed
pushed
;
(alias) object.string = string
string
[]
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
; /// every effect that actually happened, in order
} // ── the workflow ──────────────────────────────────────────────────────────── /// A miniature release: read the range, decide a version, write notes, tag, /// push. Written once; the same function serves the first run and every /// resume. void
void replay_journal.releaseWorkflow(ref replay_journal.Durable d, ref replay_journal.World w, string head)

A miniature release: read the range, decide a version, write notes, tag, push. Written once; the same function serves the first run and every resume.

releaseWorkflow
(ref
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(parameter) replay_journal.Durable d
d
, ref
(struct) replay_journal.World

The external state the workflow acts on. Each effect leaves a trace, so a re-executed step is visible.

World
(parameter) replay_journal.World w
w
,
(alias) object.string = string
string
(parameter) string head
head
)
{ const
(local variable) const(string) range
range
=
(parameter) replay_journal.Durable d
d
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("range",
(parameter) string head
head
, {
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
~= "git log " ~
(parameter) string head
head
;
return "3 commits"; }); const
(local variable) const(string) version_
version_
=
(parameter) replay_journal.Durable d
d
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("decide.version",
(local variable) const(string) range
range
, {
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
~= "bump";
return "v0.2.0"; }); const
(local variable) const(string) notes
notes
=
(parameter) replay_journal.Durable d
d
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("notes",
(local variable) const(string) version_
version_
, {
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
~= "agent notes for " ~
(local variable) const(string) version_
version_
;
return "notes for " ~
(local variable) const(string) version_
version_
;
});
(parameter) replay_journal.Durable d
d
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("tag." ~
(local variable) const(string) version_
version_
,
(local variable) const(string) notes
notes
, {
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
~= "git tag " ~
(local variable) const(string) version_
version_
;
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.tags
tags
~=
(local variable) const(string) version_
version_
;
return "tagged"; });
(parameter) replay_journal.Durable d
d
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("push." ~
(local variable) const(string) version_
version_
, "origin", {
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
~= "git push " ~
(local variable) const(string) version_
version_
;
(parameter) replay_journal.World w
w
.
(field) string[] replay_journal.World.pushed
pushed
~=
(local variable) const(string) version_
version_
;
return "pushed"; }); } // ── the demonstration ─────────────────────────────────────────────────────── int
int D main()
main
()
{ // 1. An uninterrupted run: every step executes, the journal fills.
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(local variable) replay_journal.Durable full
full
;
(struct) replay_journal.World

The external state the workflow acts on. Each effect leaves a trace, so a re-executed step is visible.

World
(local variable) replay_journal.World w0
w0
;
void replay_journal.releaseWorkflow(ref replay_journal.Durable d, ref replay_journal.World w, string head)

A miniature release: read the range, decide a version, write notes, tag, push. Written once; the same function serves the first run and every resume.

releaseWorkflow
(
(local variable) replay_journal.Durable full
full
,
(local variable) replay_journal.World w0
w0
, "abc123");
void std.stdio.writefln!(char, ulong, ulong)(in char[] fmt, ulong __param_1, ulong __param_2) @safe

Equivalent to writef(fmt, args, '\n').

writefln
("uninterrupted: %d steps executed, journal has %d entries",
(local variable) replay_journal.Durable full
full
.
(field) ulong replay_journal.Durable.executed

steps that actually ran in this invocation

executed
,
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
.
(field) ulong replay_journal.Entry[].length
length
);
foreach (
(parameter) ulong i
i
,
(parameter) replay_journal.Entry e
e
;
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
)
void std.stdio.writefln!(char, ulong, string, string, string)(in char[] fmt, ulong __param_1, string __param_2, string __param_3, string __param_4) @safe

Equivalent to writef(fmt, args, '\n').

writefln
(" %d %-16s %s %s",
(local variable) ulong i
i
,
(local variable) replay_journal.Entry e
e
.
(field) string replay_journal.Entry.key
key
,
(local variable) replay_journal.Entry e
e
.
(field) string replay_journal.Entry.fingerprint
fingerprint
,
(local variable) replay_journal.Entry e
e
.
(field) string replay_journal.Entry.result
result
);
// 2. Crash-at-every-index: cut after k entries, resume, compare.
void std.stdio.writeln!()() @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
void std.stdio.writeln!string(string __param_0) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("resume after a crash at each index:");
void std.stdio.writeln!string(string __param_0) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
(" cut replayed executed journal identical");
foreach (
(local variable) ulong k
k
; 0 ..
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
.
(field) ulong replay_journal.Entry[].length
length
+ 1)
{
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(local variable) replay_journal.Durable resumed
resumed
=
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
[0 ..
(local variable) ulong k
k
].
replay_journal.Entry[] object.dup!(replay_journal.Entry)(const(replay_journal.Entry)[] a) pure nothrow @property @safe
dup
);
(struct) replay_journal.World

The external state the workflow acts on. Each effect leaves a trace, so a re-executed step is visible.

World
(local variable) replay_journal.World w
w
;
void replay_journal.releaseWorkflow(ref replay_journal.Durable d, ref replay_journal.World w, string head)

A miniature release: read the range, decide a version, write notes, tag, push. Written once; the same function serves the first run and every resume.

releaseWorkflow
(
(local variable) replay_journal.Durable resumed
resumed
,
(local variable) replay_journal.World w
w
, "abc123");
const
(local variable) const(bool) identical
identical
=
(local variable) replay_journal.Durable resumed
resumed
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
.
bool std.algorithm.comparison.equal!().equal!(replay_journal.Entry[], replay_journal.Entry[])(replay_journal.Entry[] __param_0, replay_journal.Entry[] __param_1) pure nothrow @nogc @safe

Compares two or more ranges for equality, as defined by predicate pred (which is == by default).

Compares two or more ranges for equality. The ranges may have different element types, as long as all are comparable by means of the pred. Performs O(min(rs[0].length, rs[1].length, ...)) evaluations of pred. However, if equal is invoked with the default predicate, the implementation may take the liberty to use faster implementations that have the theoretical worst-case O(max(rs[0].length, rs[1].length, ...)).

At least one of the ranges must be finite. If one range involved is infinite, the result is (statically known to be) false.

If the ranges have different kinds of UTF code unit (char, wchar, or dchar), then they are compared using UTF decoding to avoid accidentally integer-promoting units.

Examples

import std.algorithm.comparison : equal;
import std.math.operations : isClose;

int[4] a = [ 1, 2, 4, 3 ];
assert(!equal(a[], a[1..$]));
assert(equal(a[], a[]));
assert(equal!((a, b) => a == b)(a[], a[]));

// different types
double[4] b = [ 1.0, 2, 4, 3];
assert(!equal(a[], b[1..$]));
assert(equal(a[], b[]));

// predicated: ensure that two vectors are approximately equal
double[4] c = [ 1.0000000005, 2, 4, 3];
assert(equal!isClose(b[], c[]));

Tip

equal can itself be used as a predicate to other functions. This can be very useful when the element type of a range is itself a range. In particular, equal can be its own predicate, allowing range of range (of range...) comparisons.

import std.algorithm.comparison : equal;
import std.range : iota, chunks;
assert(equal!(equal!equal)(
    [[[0, 1], [2, 3]], [[4, 5], [6, 7]]],
    iota(0, 8).chunks(2).chunks(2)
));
@paramrs The ranges to be compared.@returnstrue if and only if all ranges compare equal element for element, according to binary predicate pred.
equal
(
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
);
const(bool) std.exception.enforce!().enforce!(const(bool))(const(bool) value, lazy const(char)[] msg = null, string file = __FILE__, ulong line = cast(ulong)__LINE__) pure @safe

Enforces that the given value is true. If the given value is false, an exception is thrown. The

  • msg - error message as a string

  • dg - custom delegate that return a string and is only called if an exception occurred

  • ex - custom exception to be thrown. It is lazy and is only created if an exception occurred

@paramvalue The value to test.@paramE Exception type to throw if the value evaluates to false.@parammsg The error message to put in the exception if it is thrown.@paramdg The delegate to be called if the value evaluates to false.@paramex The exception to throw if the value evaluates to false.@paramfile The source file of the caller.@paramline The line number of the caller.@returns

value, if cast(bool) value`` is true. Otherwise, depending on the chosen overload, new Exception(msg), dg() or ex is thrown.

enforce is used to throw exceptions and is therefore intended to aid in error handling. It is not intended for verifying the logic of your program - that is what assert is for.

Do not use enforce inside of contracts (i.e. inside of in and out blocks and invariants), because contracts are compiled out when compiling with -release.

If a delegate is passed, the safety and purity of this function are inferred from Dg's safety and purity.

enforce
(
(local variable) const(bool) identical
identical
, "resume diverged from the uninterrupted run");
bool std.exception.enforce!().enforce!bool(bool value, lazy const(char)[] msg = null, string file = __FILE__, ulong line = cast(ulong)__LINE__) pure @safe

Enforces that the given value is true. If the given value is false, an exception is thrown. The

  • msg - error message as a string

  • dg - custom delegate that return a string and is only called if an exception occurred

  • ex - custom exception to be thrown. It is lazy and is only created if an exception occurred

@paramvalue The value to test.@paramE Exception type to throw if the value evaluates to false.@parammsg The error message to put in the exception if it is thrown.@paramdg The delegate to be called if the value evaluates to false.@paramex The exception to throw if the value evaluates to false.@paramfile The source file of the caller.@paramline The line number of the caller.@returns

value, if cast(bool) value`` is true. Otherwise, depending on the chosen overload, new Exception(msg), dg() or ex is thrown.

enforce is used to throw exceptions and is therefore intended to aid in error handling. It is not intended for verifying the logic of your program - that is what assert is for.

Do not use enforce inside of contracts (i.e. inside of in and out blocks and invariants), because contracts are compiled out when compiling with -release.

If a delegate is passed, the safety and purity of this function are inferred from Dg's safety and purity.

enforce
(
(local variable) replay_journal.Durable resumed
resumed
.
(field) ulong replay_journal.Durable.executed

steps that actually ran in this invocation

executed
==
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
.
(field) ulong replay_journal.Entry[].length
length
-
(local variable) ulong k
k
,
"resume re-executed a journaled step");
void std.stdio.writefln!(char, ulong, ulong, ulong, string)(in char[] fmt, ulong __param_1, ulong __param_2, ulong __param_3, string __param_4) @safe

Equivalent to writef(fmt, args, '\n').

writefln
(" %3d %8d %8d %s",
(local variable) ulong k
k
,
(local variable) ulong k
k
,
(local variable) replay_journal.Durable resumed
resumed
.
(field) ulong replay_journal.Durable.executed

steps that actually ran in this invocation

executed
,
(local variable) const(bool) identical
identical
? "yes" : "NO");
} // 3. Divergence: the same journal, but the program now asks for a different // step at position 1 (a changed bump policy). Replay refuses.
void std.stdio.writeln!()() @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(local variable) replay_journal.Durable drifted
drifted
=
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
.
replay_journal.Entry[] object.dup!(replay_journal.Entry)(const(replay_journal.Entry)[] a) pure nothrow @property @safe
dup
);
(alias) object.string = string
string
string replay_journal.main.mustNotRun() pure nothrow @nogc @safe
mustNotRun
() { assert(false, "a journaled step must not execute"); }
try {
(local variable) replay_journal.Durable drifted
drifted
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("range", "abc123", &
string replay_journal.main.mustNotRun() pure nothrow @nogc @safe
mustNotRun
);
(local variable) replay_journal.Durable drifted
drifted
.
string replay_journal.Durable.step(string key, string args, scope string delegate() live)

Runs (or replays) one step. live is only invoked when the journal has no entry for this position.

step
("decide.version", "4 commits", &
string replay_journal.main.mustNotRun() pure nothrow @nogc @safe
mustNotRun
);
void std.stdio.writeln!string(string __param_0) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("divergence NOT detected");
return 1; } catch (
(class) replay_journal.Divergence

Raised when the program diverges from its journal.

Divergence
(local variable) replay_journal.Divergence e
e
)
void std.stdio.writeln!(string, string)(string __param_0, string __param_1) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("divergence detected: ",
(local variable) replay_journal.Divergence e
e
.
(field) string object.Throwable.msg

A message describing the error.

msg
);
// 4. The world after a resume shows only the steps that were re-executed: // a crash after the tag re-runs only the push.
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(local variable) replay_journal.Durable afterTag
afterTag
=
(struct) replay_journal.Durable

Answers steps from the journal while it lasts, then executes them live.

Durable
(
(local variable) replay_journal.Durable full
full
.
(field) replay_journal.Entry[] replay_journal.Durable.journal

the entries recorded so far (input: the prior run's)

journal
[0 .. 4].
replay_journal.Entry[] object.dup!(replay_journal.Entry)(const(replay_journal.Entry)[] a) pure nothrow @property @safe
dup
);
(struct) replay_journal.World

The external state the workflow acts on. Each effect leaves a trace, so a re-executed step is visible.

World
(local variable) replay_journal.World w4
w4
;
void replay_journal.releaseWorkflow(ref replay_journal.Durable d, ref replay_journal.World w, string head)

A miniature release: read the range, decide a version, write notes, tag, push. Written once; the same function serves the first run and every resume.

releaseWorkflow
(
(local variable) replay_journal.Durable afterTag
afterTag
,
(local variable) replay_journal.World w4
w4
, "abc123");
void std.stdio.writeln!()() @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
();
void std.stdio.writeln!(string, string[])(string __param_0, string[] __param_1) @safe

Equivalent to write(args, '\n'). Calling writeln without arguments is valid and just prints a newline to the standard output.

Example

Reads stdin and writes it to stdout with an argument counter.

import std.stdio;

void main()
{
    string line;

    for (size_t count = 0; (line = readln) !is null; count++)
    {
         writeln("Input ", count, ": ", line);
    }
}
@paramargs the items to write to stdout@throwsIn case of an I/O error, throws an StdioException.
writeln
("effects performed when resuming after the tag: ",
(local variable) replay_journal.World w4
w4
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
);
bool std.exception.enforce!().enforce!bool(bool value, lazy const(char)[] msg = null, string file = __FILE__, ulong line = cast(ulong)__LINE__) pure @safe

Enforces that the given value is true. If the given value is false, an exception is thrown. The

  • msg - error message as a string

  • dg - custom delegate that return a string and is only called if an exception occurred

  • ex - custom exception to be thrown. It is lazy and is only created if an exception occurred

@paramvalue The value to test.@paramE Exception type to throw if the value evaluates to false.@parammsg The error message to put in the exception if it is thrown.@paramdg The delegate to be called if the value evaluates to false.@paramex The exception to throw if the value evaluates to false.@paramfile The source file of the caller.@paramline The line number of the caller.@returns

value, if cast(bool) value`` is true. Otherwise, depending on the chosen overload, new Exception(msg), dg() or ex is thrown.

enforce is used to throw exceptions and is therefore intended to aid in error handling. It is not intended for verifying the logic of your program - that is what assert is for.

Do not use enforce inside of contracts (i.e. inside of in and out blocks and invariants), because contracts are compiled out when compiling with -release.

If a delegate is passed, the safety and purity of this function are inferred from Dg's safety and purity.

enforce
(
(local variable) replay_journal.World w4
w4
.
(field) string[] replay_journal.World.log

every effect that actually happened, in order

log
== ["git push v0.2.0"]);
return 0; }