Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions cmux-tui/crates/acpmux/src/cli/host_peering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,16 +202,17 @@ pub(crate) async fn peer_step(
let settle = |reply: Result<Value>| match reply {
Err(e) => fail(format!("the local daemon refused {name} at {url}: {e}")),
// The peer stopped (it was removed, or never started) before its attempt settled.
Ok(reply) if reply["wait"]["stopped"] == true => fail(format!(
Ok(reply) if reply.pointer("/wait/stopped") == Some(&Value::Bool(true)) => fail(format!(
"{name} at {url} stopped before it connected{}",
last_error(&reply["wait"]["peer"])
last_error(reply.pointer("/wait/peer").unwrap_or(&Value::Null))
))
.with_fix("`acpmux host ls` shows whether it is still configured; add it again if not."),
// The daemon's wait ran out before the attempt settled (`Hub::wait_for_peer`): say how
// far it got, not "does not connect" with no reason.
Ok(reply) if reply["wait"]["settled"] == false => {
let p = &reply["wait"]["peer"];
let step = if p["handshaking"] == true { "in its handshake" } else { "connecting" };
Ok(reply) if reply.pointer("/wait/settled") == Some(&Value::Bool(false)) => {
let p = reply.pointer("/wait/peer").unwrap_or(&Value::Null);
let handshaking = p.get("handshaking") == Some(&Value::Bool(true));
let step = if handshaking { "in its handshake" } else { "connecting" };
let secs = reply["wait"]["waitedMs"].as_u64().unwrap_or(0) / 1000;
fail(format!("{name} at {url} is still {step} after {secs}s{}", last_error(p)))
.with_fix("Check again in a minute; `acpmux host ls` shows when it connects.")
Expand Down
4 changes: 2 additions & 2 deletions cmux-tui/crates/acpmux/src/hub/peers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,12 +235,12 @@ impl Hub {
if stopped {
tracing::warn!(peer = %name, "it stopped before its connect attempt settled");
} else if !done {
tracing::warn!(peer = %name, state = %peer["state"], "its connect attempt had not settled after {}s; answering with how it is now", budget.as_secs());
tracing::warn!(peer = %name, state = %peer.get("state").unwrap_or(&serde_json::Value::Null), "its connect attempt had not settled after {}s; answering with how it is now", budget.as_secs());
}
let waited = u64::try_from(started.elapsed().as_millis()).unwrap_or(u64::MAX);
let mut wait = json!({"settled": done, "waitedMs": waited, "peer": peer});
if stopped {
wait["stopped"] = Value::Bool(true);
wait.set("stopped", Value::Bool(true));
}
wait
}
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/crates/acpmux/src/server/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub(super) fn ask<'a>(conn: &Conn, params: &Value, who: &'a str) -> crate::hub::
/// without one.
fn with_wait(mut reply: Value, waited: Value) -> Value {
if !waited.is_null() {
reply["wait"] = waited;
reply.set("wait", waited);
}
reply
}
Expand Down
14 changes: 8 additions & 6 deletions cmux-tui/crates/cmux-rd-link/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,8 @@ impl Link {
let n = handshake.write_message(&[], &mut buf).map_err(LinkError::Noise)?;
let mut packet = Vec::with_capacity(1 + n);
packet.push(tag);
packet.extend_from_slice(&buf[..n]);
buf.truncate(n);
packet.extend_from_slice(&buf);
self.out.push_back(packet.clone());
self.last_handshake = Some((packet, now_us));
Ok(())
Expand Down Expand Up @@ -355,11 +356,12 @@ impl Link {
let mut plain = Vec::with_capacity(1 + payload.len());
plain.push(kind);
plain.extend_from_slice(payload);
let mut packet = vec![0u8; 1 + 8 + plain.len() + 16];
packet[0] = TAG_SEALED;
packet[1..9].copy_from_slice(&nonce.to_le_bytes());
let n =
transport.write_message(nonce, &plain, &mut packet[9..]).map_err(LinkError::Noise)?;
let mut packet = Vec::with_capacity(1 + 8 + plain.len() + 16);
packet.push(TAG_SEALED);
packet.extend_from_slice(&nonce.to_le_bytes());
packet.resize(1 + 8 + plain.len() + 16, 0);
let body = packet.get_mut(9..).unwrap_or_default();
let n = transport.write_message(nonce, &plain, body).map_err(LinkError::Noise)?;
packet.truncate(9 + n);
Ok(packet)
}
Expand Down
10 changes: 7 additions & 3 deletions cmux-tui/crates/cmux-rd-link/src/replay.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl ReplayWindow {
return false;
}
let (word, bit) = Self::slot(nonce);
self.bits[word] & bit == 0
self.bits.get(word).is_some_and(|w| w & bit == 0)
}

pub fn accept(&mut self, nonce: u64) {
Expand All @@ -43,11 +43,15 @@ impl ReplayWindow {
let advance = (nonce + 1 - self.top).min(WINDOW);
for n in 0..advance {
let (word, bit) = Self::slot(self.top + n);
self.bits[word] &= !bit;
if let Some(w) = self.bits.get_mut(word) {
*w &= !bit;
}
}
self.top = nonce + 1;
}
let (word, bit) = Self::slot(nonce);
self.bits[word] |= bit;
if let Some(w) = self.bits.get_mut(word) {
*w |= bit;
}
}
}
6 changes: 3 additions & 3 deletions cmux-tui/crates/cmux-tui/src/cli/app/group_create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ pub(super) fn parse(args: &[String]) -> Result<Option<AppCommand>, UsageError> {
if words != ["workspace", "group", "create"] {
return Ok(None);
}
let rest = &args[3..];
let rest = args.get(3..).unwrap_or_default();
let mut remote = None;
let mut parent = None;
let mut name = None;
let mut other = None;
let mut index = 0;
while index < rest.len() {
let arg = rest[index].as_str();
while let Some(arg) = rest.get(index) {
let arg = arg.as_str();
let (flag, inline) = match arg.split_once('=') {
Some((flag, value)) if flag.starts_with("--") => (flag, Some(value.to_owned())),
_ => (arg, None),
Expand Down
6 changes: 4 additions & 2 deletions cmux-tui/crates/cmux-tui/src/cli/apps_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,10 @@ pub(super) fn info_text(detail: &Value) -> String {
detail["summary"].as_str().unwrap_or(""),
detail["tier"].as_str().unwrap_or(""),
);
let targets: Vec<&str> = detail["deploy"]["targets"]
.as_array()
let targets: Vec<&str> = detail
.get("deploy")
.and_then(|deploy| deploy.get("targets"))
.and_then(Value::as_array)
.into_iter()
.flatten()
.filter_map(Value::as_str)
Expand Down
24 changes: 18 additions & 6 deletions cmux-tui/crates/cmux-tui/src/cli/automation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,20 +151,17 @@ const SPEC_FLAGS: &[&str] = &["hourly", "manual", "paused"];
fn take_target(args: &[String]) -> Result<(bool, Vec<String>), Failure> {
let mut out = Vec::with_capacity(args.len());
let mut target = None;
let mut i = 0;
while i < args.len() {
let arg = &args[i];
let mut words = args.iter();
while let Some(arg) = words.next() {
if let Some(v) = arg.strip_prefix("--target=") {
target = Some(v.to_string());
} else if arg == "--target" {
i += 1;
target = Some(args.get(i).cloned().ok_or_else(|| {
target = Some(words.next().cloned().ok_or_else(|| {
Failure::Usage(messages().missing_value.replace("{flag}", "target"))
})?);
} else {
out.push(arg.clone());
}
i += 1;
}
match target.as_deref() {
None | Some("local" | "this-mac") => Ok((false, out)),
Expand Down Expand Up @@ -1274,4 +1271,19 @@ mod tests {
assert_eq!(parse_duration("1d").ok(), Some(86_400));
assert!(parse_duration("soon").is_err());
}

#[test]
fn target_is_taken_out_of_the_words_in_both_forms() {
let words = |w: &[&str]| w.iter().map(|s| s.to_string()).collect::<Vec<_>>();
let taken = |w: &[&str]| take_target(&words(w)).ok();
assert_eq!(taken(&["list"]), Some((false, words(&["list"]))));
assert_eq!(
taken(&["list", "--target", "cloud", "--json"]),
Some((true, words(&["list", "--json"])))
);
assert_eq!(taken(&["--target=cloud", "list"]), Some((true, words(&["list"]))));
assert_eq!(taken(&["list", "--target", "this-mac"]), Some((false, words(&["list"]))));
assert!(matches!(take_target(&words(&["list", "--target"])), Err(Failure::Usage(_))));
assert!(matches!(take_target(&words(&["--target", "mars"])), Err(Failure::Usage(_))));
}
}
2 changes: 1 addition & 1 deletion cmux-tui/crates/cmux-tui/src/cli/chief/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ fn run(global: GlobalArgs, args: &[String]) -> i32 {
return relocate::run(&global, &args[1..]);
}
if args.first().is_some_and(|word| word == "acp") {
return acp::run(&global, &args[1..]);
return acp::run(&global, args.get(1..).unwrap_or_default());
}
if args.first().is_some_and(|word| word == "usage") {
return usage::run(&global, &args[1..]);
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/crates/cmux-tui/src/cli/chief/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ pub(super) fn follow(
let Some(event) = adapt(&line, &session.conversation) else { continue };
turn.on(&event);
// A snapshot after a gap can add several replies at once.
for reply in &turn.replies[printed..] {
for reply in turn.replies.get(printed..).unwrap_or_default() {
out.message(reply);
drafts.on_message(reply);
}
Expand Down
Loading