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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! The probe contract between shep and a dog: the flag names, the
//! `shep-protocol:` line's grammar, and the schema's secret marker key.
//!
//! # Why this lives here and not in the crate that asks or the crate that answers
//!
//! `shep-cli`'s `adopt` spawns a candidate binary with [`VERSION_FLAG`] and
//! [`SCHEMA_FLAG`] and parses what it prints; `shep-client` (the dog side)
//! answers both from `shep_client::dogs::probe`. Before that call existed,
//! both sides read a string a dog author hand-typed from a snippet in
//! `docs/dogs.md`, so a typo read as "protocol unknown" and nothing said so.
//! One definition, owned by the crate both already depend on, is what lets
//! the asker and the answerer agree by construction instead of by copying a
//! doc snippet correctly.
//!
//! The asker itself (spawning the binary, applying the timeout, deciding
//! whether an unknown protocol refuses an adopt) stays in `shep-cli`,
//! beside the rest of the vetting `adopt` already does. Only the shape of
//! the question and the answer moves here.
//!
//! [`DogVersion`] moved with the parser rather than staying behind: its two
//! fields (`version`, `protocol`) are plain data with no CLI-specific type
//! in them, and it IS the grammar `parse_version_answer` returns, so
//! splitting the struct from the function that builds it would put one
//! definition of the answer's shape in one crate and the reader of that
//! shape in another.
/// The flag a candidate is spawned with when shep asks for its version, and
/// the one `docs/dogs.md` publishes as the contract. Read by
/// `shep-cli`'s `adopt`; answered, from release 2, by
/// `shep_client::dogs::probe`.
pub const VERSION_FLAG: &str = "--version";
/// The flag a candidate is spawned with when shep asks for its config
/// schema. Asked by `shep-cli`'s `adopt`, beside the version and on the
/// same terms: a dog that answers nothing is refused nothing. Answered by
/// `shep_client::dogs::probe`.
pub const SCHEMA_FLAG: &str = "--schema";
/// The one key [`parse_version_answer`] reads in a `--version` answer.
/// Every other `shep-` key is reserved for a number this shep has not
/// heard of, and is ignored rather than refused, so a dog written against a
/// later contract stays adoptable by this one.
pub const SHEP_PROTOCOL_KEY: &str = "shep-protocol";
/// The schemars extension key that marks a config field as a credential.
/// Written by the `DogConfig` derive, which exists so that no dog author
/// ever types it; the reader in `shep lookout` that redacts a field
/// carrying it arrives in a later task. Getting this string right matters
/// more than the other three here, because a typo in it does not fail
/// loudly: the schema still validates, the field is simply not marked, and
/// a credential can end up rendered on screen.
pub const SECRET_KEY: &str = "x-shep-secret";
/// What a dog answered [`VERSION_FLAG`] with, parsed by
/// [`parse_version_answer`] from the format `docs/dogs.md` publishes.
///
/// Two fields rather than one, because they answer different questions:
/// `protocol` decides whether the dog can handshake at all, and `version`
/// only says which build it is. A dog may give the second and not the
/// first, which is why `protocol` is optional and an absent one reads as
/// unknown rather than as a fault.
/// Parses the format `docs/dogs.md` publishes: `<name> <version>` on line
/// 1, then `<key>: <value>` lines.
///
/// `None` when there is no line 1 to read a version from. Everything past
/// that is tolerated rather than refused: unknown keys, blank lines, key
/// order, and a `shep-protocol` that is not a number, because a shep that
/// refuses a dog over the shape of text the dog never promised to print is
/// refusing on its own guess. The strictness is all in the other direction:
/// only an exact [`SHEP_PROTOCOL_KEY`] carrying a decimal is believed, and
/// only a believed protocol can refuse.