1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
// Provenance: own design — shared run scripts (CLI)
using System.Globalization;
namespace Godosa.Core.Runs.Scripts;
/// <summary>Run-script CLI options (<c>--script</c>, <c>--var k=v</c>, <c>--output</c>, <c>--no-cache</c>, <c>--no-snapshots</c>, <c>--from</c>,
/// <c>--until</c>, <c>--nondeterministic</c>, <c>--step-cap</c>, <c>--report-dir</c>); determinism and seed come from
/// <see cref="RunFlags"/>.</summary>
public sealed class ScriptRunFlags
{
public string? Script { get; set; }
public Dictionary<string, string> Vars { get; } = new(StringComparer.Ordinal);
public OutputLevel? Output { get; set; }
public bool NoCache { get; set; }
public bool NoSnapshots { get; set; }
public string? From { get; set; }
public string? Until { get; set; }
public bool NonDeterministic { get; set; }
public int? StepCap { get; set; }
public string? ReportDir { get; set; }
public static bool TryTake(string[] args, ref int i, ScriptRunFlags flags)
{
string Value(ref int j)
{
if (j + 1 >= args.Length)
throw new ArgumentException($"{args[j]} needs a value");
return args[++j];
}
switch (args[i])
{
case "--script":
flags.Script = Value(ref i);
return true;
case "--var":
{
var v = Value(ref i);
var eq = v.IndexOf('=');
if (eq <= 0)
throw new ArgumentException($"--var needs name=value, got '{v}'");
flags.Vars[v[..eq]] = v[(eq + 1)..];
return true;
}
case "--output":
{
var v = Value(ref i);
try
{
flags.Output = RunOutput.Parse(v);
}
catch (ScriptFault e)
{
throw new ArgumentException(e.Message);
}
return true;
}
case "--no-cache":
flags.NoCache = true;
return true;
case "--no-snapshots":
flags.NoSnapshots = true;
return true;
case "--from":
flags.From = Value(ref i);
return true;
case "--until":
flags.Until = Value(ref i);
return true;
case "--nondeterministic":
flags.NonDeterministic = true;
return true;
case "--step-cap":
{
var v = Value(ref i);
flags.StepCap = int.TryParse(v, CultureInfo.InvariantCulture, out var n) && n > 0 ? n : throw new ArgumentException($"bad --step-cap '{v}'");
return true;
}
case "--report-dir":
flags.ReportDir = Value(ref i);
return true;
default:
return false;
}
}
public ScriptRunOptions ToOptions(RunFlags run, string? cacheDir) => new()
{
Deterministic = !NonDeterministic,
Seed = run.Seed,
Output = Output ?? OutputLevel.Labels,
Floor = Output ?? OutputLevel.Quiet,
StepCap = StepCap ?? 36_000,
CacheDir = cacheDir,
UseCache = !NoCache,
Snapshots = !NoSnapshots,
From = From,
Until = Until,
ReportDir = ReportDir,
};
}
|