pub struct Environment {
pub variables: BTreeMap<String, String>,
pub auth: Option<Auth>,
pub source: Option<PathBuf>,
/* private fields */
}Expand description
A set of variables a request can be sent against.
Environment::default is the empty environment: no variables, and no file
behind it. That is the state of a project with no .sendra/environments/ at
all, and it is not an error — a request with no {{...}} in it is untouched
by substitution, so Sendra behaves exactly as it did before this existed.
Fields§
§variables: BTreeMap<String, String>The file’s contents, verbatim. Values still hold their ${VAR}
references: those are resolved when a variable is used, not when the
file is read. See Environment::lookup.
auth: Option<Auth>A default auth: block, reusing Request::auth’s
exact shape, applied by Environment::apply to every request run
against this environment that sets no auth: of its own.
Fully replaced, never merged, by a request’s own auth: — the
same “two things claiming ownership of one setting” stance
Request::auth already takes against an explicit Authorization
header, applied one layer up: a request that wants different
credentials than its environment’s default writes its own auth:
block, in full, rather than overriding one field of this one.
bearer/basic/api_key still hold unsubstituted {{var}}/${VAR}
text here, exactly like variables — resolved,
against this same environment, only when apply
actually uses it.
source: Option<PathBuf>The file this came from, or None for an environment that was not read
from disk (the empty default, or one built in a test). Carried so a
missing-variable error can name the file to go and fix.
Implementations§
Source§impl Environment
impl Environment
Sourcepub fn apply(&self, request: &Request) -> Result<Request, SendraError>
pub fn apply(&self, request: &Request) -> Result<Request, SendraError>
Substitute this environment into request, returning the request as it
will be sent.
Every {{name}} in url, in each header name, in each header value, in
body and in the values of the assertions block is replaced. An
unknown name is VariableNotFound, and
a value whose ${VAR} is not in the OS environment is
EnvVarNotSet — never an empty string, and
never a half-substituted request. The whole request is built before it is
sent, so both failures land before any of its bytes go out.
Headers are substituted in place, entry by entry, so order is preserved
exactly as written in the file. Two header names that were distinct in
the file can collide once substituted ({{prefix}}-Key and X-Key,
say) — that used to be an error back when headers were a map and a
silent collision would have dropped a value, but Request.headers is a
Vec that allows a name to repeat, so a post-substitution collision is
now exactly that: two headers of the same name, sent as written.
Assertions are substituted for the same reason the rest of the file is:
what a staging response should say is exactly as environment-dependent
as what the request asks for, and body_contains: '{{tenant}}' would
otherwise compare against the literal braces. See
apply_assertions for the one line it draws.
Sourcepub fn apply_collection(
&self,
collection: &Collection,
) -> Result<Collection, SendraError>
pub fn apply_collection( &self, collection: &Collection, ) -> Result<Collection, SendraError>
Environment::apply for every request in a collection, in file order.
All or nothing: one request that cannot be substituted fails the whole
call. That suits a caller that wants a fully-resolved collection in hand,
which is what this returns. It is not what sendra run does with a
collection — there each request is substituted as it is reached, so a
broken one fails on its own and the requests around it are still sent, in
the same way a refused connection does not cancel its siblings. A caller
wanting those per-request outcomes calls Environment::apply in a loop
and keeps each Result.
The collection’s own name is left alone for the same reason a
request’s is.
Sourcepub fn apply_document(
&self,
document: &Document,
) -> Result<Document, SendraError>
pub fn apply_document( &self, document: &Document, ) -> Result<Document, SendraError>
Environment::apply over whichever shape a file turned out to hold.
Source§impl Environment
impl Environment
Sourcepub fn from_yaml_str(yaml: &str) -> Result<Self, SendraError>
pub fn from_yaml_str(yaml: &str) -> Result<Self, SendraError>
Parse an environment from a YAML string.
Sourcepub fn from_path(path: impl AsRef<Path>) -> Result<Self, SendraError>
pub fn from_path(path: impl AsRef<Path>) -> Result<Self, SendraError>
Read and parse an environment file from disk.
Sourcepub fn resolve(name: &str) -> Result<Self, SendraError>
pub fn resolve(name: &str) -> Result<Self, SendraError>
Find and load the environment called name, starting from the current
directory.
A missing environment file is not an error, it is the empty
environment — the same call the config module makes for a missing config
file. What is an error is a request asking for a variable the
environment does not have, empty or not; that surfaces in
Environment::apply, where the message can name the variable.
Sourcepub fn resolve_from(start_dir: &Path, name: &str) -> Result<Self, SendraError>
pub fn resolve_from(start_dir: &Path, name: &str) -> Result<Self, SendraError>
Environment::resolve with the starting directory passed in, so the
search is testable against a temporary tree without changing the
process’s working directory.
Sourcepub fn with_captured(&self, captured: &BTreeMap<String, String>) -> Self
pub fn with_captured(&self, captured: &BTreeMap<String, String>) -> Self
This environment as it stands at one point in a run: the file’s own variables, plus everything captured by the requests that have already finished.
This is the whole of the accumulating store. A run holds one growing
map and calls this once per request, so the environment a request is
substituted against is a view built from the captures that existed
when that request was reached — request 3 sees what 1 and 2 captured,
request 1 sees nothing, and no request can see forwards. Threading the
growth through a rebuilt value rather than through a mutable
Environment is what makes that structural instead of a rule the loop
has to remember: there is no &mut Environment anywhere for a later
capture to reach an earlier request through.
The copy is a BTreeMap clone per request, which is nothing at the
sizes a hand-written collection reaches, and it buys the property that
the value handed to apply cannot change underneath it.
Nothing else changes: source, auth, and the OS-environment
override tests use, are carried through untouched.
Sourcepub fn names(&self) -> Vec<String>
pub fn names(&self) -> Vec<String>
The variable names this environment’s file defines, sorted — the
list a “no variable named X” error offers, the way
RequestNotFound offers request names.
Captured names are deliberately not in here: this list is offered under
the name of the file it came from, and a capture did not come from that
file. They are reported beside it — see
captured_names.
Sourcepub fn captured_names(&self) -> Vec<String>
pub fn captured_names(&self) -> Vec<String>
The names captured by earlier requests in this run, sorted. Empty
unless with_captured put something there.
Sourcepub fn is_empty(&self) -> bool
pub fn is_empty(&self) -> bool
Whether this environment defines no variables at all — captures
included, since a {{name}} can resolve against either.
Sourcepub fn validate(&self) -> Result<(), SendraError>
pub fn validate(&self) -> Result<(), SendraError>
Every rule Deserialize cannot express, checked directly rather than
only ever at parse time: auth’s own mutual-exclusivity and (for
oauth) grant-field rules — the exact same checks
from_file already runs when reading a file from
disk, exposed here so a caller building or mutating an Environment
in memory (a front-end applying an edit, say) can ask the question
before save_to_path ever writes it, the same
“check before writing rather than only discover it broken on the next
load” reasoning Document::validate
documents for its own callers. variables has no rule of its own to
check: every BTreeMap<String, String>, empty included, is already a
valid environment — see this module’s own doc comment on why an empty
file is not an error.
Sourcepub fn to_yaml_string(&self) -> Result<String, SendraError>
pub fn to_yaml_string(&self) -> Result<String, SendraError>
Serializes this environment back to YAML, exactly the flat shape
from_yaml_str/from_path
parse: every variable as a top-level key, plus auth: when set — see
EnvironmentFile’s own Serialize impl for why this goes through
that type’s hand-written map serialization rather than a derived one
on Environment itself. captured/os_env_override are runtime-only
(never part of a file — see their own doc comments) and so play no
part here; only variables and auth round-trip.
Sourcepub fn save_to_path(&self, path: impl AsRef<Path>) -> Result<(), SendraError>
pub fn save_to_path(&self, path: impl AsRef<Path>) -> Result<(), SendraError>
Writes this environment back to path, atomically — the exact same
write-to-a-sibling-temp-file-then-rename guarantee
Document::save_to_path documents
for a collection file, reusing its own [unique_temp_path] helper
rather than a second implementation of the same atomicity argument.
Refuses to write an invalid environment (see
validate) before the temp file is even created,
for the same reason Document::save_to_path checks first: an
in-memory edit that left auth invalid would otherwise still produce
a file that parses back as YAML but fails validation the next time
anything loads it.
Trait Implementations§
Source§impl Clone for Environment
impl Clone for Environment
Source§fn clone(&self) -> Environment
fn clone(&self) -> Environment
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for Environment
impl Debug for Environment
Source§impl Default for Environment
impl Default for Environment
Source§fn default() -> Environment
fn default() -> Environment
impl Eq for Environment
Source§impl PartialEq for Environment
impl PartialEq for Environment
impl StructuralPartialEq for Environment
Auto Trait Implementations§
impl Freeze for Environment
impl RefUnwindSafe for Environment
impl Send for Environment
impl Sync for Environment
impl Unpin for Environment
impl UnsafeUnpin for Environment
impl UnwindSafe for Environment
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.