use serde::Deserialize;
use std::io::{self, Read};
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
pub struct Binding {
#[serde(default)]
pub remote: Option<String>,
#[serde(default)]
pub stealth: bool,
pub tasks_branch: String,
pub store: String,
#[serde(default)]
pub landing: String,
pub invocation_path: String,
}
#[derive(Deserialize)]
struct Envelope {
binding: Binding,
}
pub fn read_binding(input: &mut impl Read) -> io::Result<Binding> {
let mut buf = String::new();
input.read_to_string(&mut buf)?;
let envelope: Envelope = serde_json::from_str(&buf).map_err(io::Error::other)?;
Ok(envelope.binding)
}
#[cfg(test)]
mod tests {
use super::*;
fn read(json: &str) -> io::Result<Binding> {
read_binding(&mut json.as_bytes())
}
#[test]
fn reads_a_tracked_binding_and_ignores_extra_wire_fields() {
let b = read(
r#"{"op":"sync","phase":"pre","actor":"x","command":{"op":"sync"},
"binding":{"remote":"git@h:r","tasks_branch":"balls/tasks",
"store":"/store","landing":"/landing","invocation_path":"/proj"}}"#,
)
.unwrap();
assert_eq!(b.remote.as_deref(), Some("git@h:r"));
assert_eq!(b.tasks_branch, "balls/tasks");
assert_eq!(b.store, "/store");
assert_eq!(b.landing, "/landing");
assert_eq!(b.invocation_path, "/proj");
assert!(!b.stealth); }
#[test]
fn an_absent_remote_or_landing_is_the_stealth_binding() {
let b = read(
r#"{"binding":{"tasks_branch":"balls/tasks","store":"/store","invocation_path":"/p"}}"#,
)
.unwrap();
assert_eq!(b.remote, None);
assert_eq!(b.landing, "");
}
#[test]
fn an_explicit_stealth_flag_rides_the_binding() {
let b = read(
r#"{"binding":{"stealth":true,"tasks_branch":"balls/tasks","store":"/store","invocation_path":"/p"}}"#,
)
.unwrap();
assert!(b.stealth);
assert_eq!(b.remote, None);
}
#[test]
fn malformed_json_is_an_error() {
assert!(read("not json").is_err());
}
}