fix: rust 1.95.0 clippy lints and matrix-sdk 0.17 API changes

Toolchain bump surfaced new lints (derivable_impls,
unnecessary_unwrap, unnecessary_sort_by, while_let_loop,
collapsible_match, unnecessary_option_map_or_else, cmp_owned)
across bft-json-crdt and huskies-server. All fixed mechanically.

Cargo.toml: dropped the no-longer-existing `rustls-tls` matrix-sdk
feature, then chased through the 0.17 API breakage:
- Relation::Reply is now a tuple variant wrapping Reply, not a
  struct variant with `in_reply_to`
- UserIdentifier::UserIdOrLocalpart removed — use
  UserIdentifier::Matrix(MatrixUserIdentifier::new(..))
- SendMessageLikeEventResult no longer exposes event_id directly;
  it's now on the inner `response` field

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Timmy
2026-05-14 14:48:49 +01:00
parent 995c878961
commit 8625b9a7fc
13 changed files with 275 additions and 317 deletions
+1 -1
View File
@@ -135,7 +135,7 @@ fn loc_top_n(project_root: &std::path::Path, top_n: usize) -> String {
})
.collect();
files.sort_by(|a, b| b.0.cmp(&a.0));
files.sort_by_key(|b| std::cmp::Reverse(b.0));
files.truncate(top_n);
if files.is_empty() {
@@ -61,7 +61,7 @@ pub(super) async fn is_reply_to_bot(
bot_sent_event_ids: &TokioMutex<HashSet<OwnedEventId>>,
) -> bool {
let candidate_ids: Vec<&OwnedEventId> = match relates_to {
Some(Relation::Reply { in_reply_to }) => vec![&in_reply_to.event_id],
Some(Relation::Reply(reply)) => vec![&reply.in_reply_to.event_id],
Some(Relation::Thread(thread)) => {
let mut ids = vec![&thread.event_id];
if let Some(irti) = &thread.in_reply_to {
@@ -216,8 +216,9 @@ mod tests {
sent.lock().await.insert(event_id.clone());
let in_reply_to = matrix_sdk::ruma::events::relation::InReplyTo::new(event_id);
let relates_to: Option<Relation<RoomMessageEventContentWithoutRelation>> =
Some(Relation::Reply { in_reply_to });
let relates_to: Option<Relation<RoomMessageEventContentWithoutRelation>> = Some(
Relation::Reply(matrix_sdk::ruma::events::relation::Reply::new(in_reply_to)),
);
assert!(is_reply_to_bot(relates_to.as_ref(), &sent).await);
}
@@ -231,8 +232,9 @@ mod tests {
let in_reply_to = matrix_sdk::ruma::events::relation::InReplyTo::new(
"$other:example.com".parse::<OwnedEventId>().unwrap(),
);
let relates_to: Option<Relation<RoomMessageEventContentWithoutRelation>> =
Some(Relation::Reply { in_reply_to });
let relates_to: Option<Relation<RoomMessageEventContentWithoutRelation>> = Some(
Relation::Reply(matrix_sdk::ruma::events::relation::Reply::new(in_reply_to)),
);
assert!(!is_reply_to_bot(relates_to.as_ref(), &sent).await);
}
+3 -1
View File
@@ -92,7 +92,9 @@ pub async fn run_bot(
{
use matrix_sdk::ruma::api::client::uiaa;
let password_auth = uiaa::AuthData::Password(uiaa::Password::new(
uiaa::UserIdentifier::UserIdOrLocalpart(config.username.clone().unwrap_or_default()),
uiaa::UserIdentifier::Matrix(uiaa::MatrixUserIdentifier::new(
config.username.clone().unwrap_or_default(),
)),
config.password.clone().unwrap_or_default(),
));
if let Err(e) = client
@@ -48,7 +48,7 @@ impl ChatTransport for MatrixTransport {
.await
.map_err(|e| format!("Matrix send error: {e}"))?;
Ok(resp.event_id.to_string())
Ok(resp.response.event_id.to_string())
}
async fn edit_message(
+1 -4
View File
@@ -156,10 +156,7 @@ fn is_inside_code_fence(text: &str) -> bool {
pub fn drain_complete_paragraphs(buffer: &mut String) -> Vec<String> {
let mut paragraphs = Vec::new();
let mut search_from = 0;
loop {
let Some(pos) = buffer[search_from..].find("\n\n") else {
break;
};
while let Some(pos) = buffer[search_from..].find("\n\n") {
let abs_pos = search_from + pos;
// Only split at this boundary when we are NOT inside a code fence.
if is_inside_code_fence(&buffer[..abs_pos]) {
+2 -3
View File
@@ -200,6 +200,7 @@ pub(crate) fn resolve_path_arg(path_str: Option<&str>, cwd: &std::path::Path) ->
#[cfg(test)]
mod tests {
use super::*;
use std::path::Path;
#[test]
fn parse_no_args() {
@@ -411,9 +412,7 @@ mod tests {
fn resolve_path_arg_returns_path_for_absolute_arg() {
let cwd = PathBuf::from("/home/user/project");
let result = resolve_path_arg(Some("/some/absolute/path"), &cwd).unwrap();
assert!(
result.ends_with("absolute/path") || result == PathBuf::from("/some/absolute/path")
);
assert!(result.ends_with("absolute/path") || result == Path::new("/some/absolute/path"));
}
#[test]
+6 -8
View File
@@ -44,13 +44,11 @@ pub async fn crdt_sync_handler(
// ── Bearer-token check (pre-upgrade) ────────────────────────────
let require_token = REQUIRE_TOKEN.get().copied().unwrap_or(false);
match &params.token {
Some(t) => {
if !validate_join_token(t) {
slog!("[crdt-sync] Rejected connection: invalid or expired token");
return poem::Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body("invalid or expired token");
}
Some(t) if !validate_join_token(t) => {
slog!("[crdt-sync] Rejected connection: invalid or expired token");
return poem::Response::builder()
.status(StatusCode::UNAUTHORIZED)
.body("invalid or expired token");
}
None if require_token => {
slog!("[crdt-sync] Rejected connection: token required but not provided");
@@ -58,7 +56,7 @@ pub async fn crdt_sync_handler(
.status(StatusCode::UNAUTHORIZED)
.body("token required");
}
None => {}
_ => {}
}
// ── WebSocket upgrade ────────────────────────────────────────────
+7 -10
View File
@@ -101,16 +101,13 @@ pub(crate) fn tool_ensure_acceptance(args: &Value, ctx: &AppContext) -> Result<S
file_results = project_root.as_deref().and_then(|root| {
crate::http::workflow::read_test_results_from_story_file(root, story_id)
});
file_results.as_ref().map_or_else(
|| {
// No results anywhere — use empty default for the acceptance check
// (it will fail with "No test results recorded")
static EMPTY: std::sync::OnceLock<crate::workflow::StoryTestResults> =
std::sync::OnceLock::new();
EMPTY.get_or_init(Default::default)
},
|r| r,
)
file_results.as_ref().unwrap_or_else(|| {
// No results anywhere — use empty default for the acceptance check
// (it will fail with "No test results recorded")
static EMPTY: std::sync::OnceLock<crate::workflow::StoryTestResults> =
std::sync::OnceLock::new();
EMPTY.get_or_init(Default::default)
})
};
let coverage = workflow.coverage.get(story_id);
+2 -5
View File
@@ -90,11 +90,8 @@ pub async fn ws_handler(ws: WebSocket, ctx: Data<&Arc<AppContext>>) -> impl poem
let mut pending_perms: HashMap<String, oneshot::Sender<PermissionDecision>> =
HashMap::new();
loop {
// Outer loop: wait for the next WebSocket message.
let Some(Ok(WsMessage::Text(text))) = stream.next().await else {
break;
};
// Outer loop: wait for the next WebSocket message.
while let Some(Ok(WsMessage::Text(text))) = stream.next().await {
// Handle read-RPC frames (discriminated by "kind", not "type").
if let Some(rpc_resp) = crate::crdt_sync::try_handle_rpc_text(&text).await {