mirror of
https://github.com/ratspeak/lrgp-rs
synced 2026-08-12 18:07:21 -04:00
ci: enforce strict Clippy across game targets
This commit is contained in:
parent
60d570dd22
commit
a3da010b5c
9 changed files with 136 additions and 112 deletions
20
.github/workflows/ci.yml
vendored
20
.github/workflows/ci.yml
vendored
|
|
@ -31,6 +31,22 @@ jobs:
|
|||
- name: Check formatting
|
||||
run: cargo fmt --all -- --check
|
||||
- name: Lint all targets
|
||||
run: cargo clippy --all-targets --all-features -- -D warnings
|
||||
run: cargo clippy --all-targets --all-features --locked -- -D warnings
|
||||
- name: Run tests
|
||||
run: cargo test --all-features
|
||||
run: cargo test --all-features --locked
|
||||
|
||||
msrv:
|
||||
name: Rust 1.85 MSRV
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@1.85.0
|
||||
with:
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-bin: false
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
- name: Clippy (all targets and features)
|
||||
run: cargo clippy --all-targets --all-features --locked -- -D warnings
|
||||
|
|
|
|||
2
clippy.toml
Normal file
2
clippy.toml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Keep Clippy suggestions aligned with the public MSRV in Cargo.toml.
|
||||
msrv = "1.85"
|
||||
|
|
@ -102,11 +102,11 @@ fn main() {
|
|||
let result = router_b
|
||||
.dispatch_incoming(&move_env, player_a, player_b)
|
||||
.unwrap();
|
||||
if let IncomingDispatch::Applied(result) = result
|
||||
&& let Some(emit) = &result.emit
|
||||
{
|
||||
if let Some(ev_type) = emit.get("type") {
|
||||
println!("B inbound event: {ev_type:?}");
|
||||
if let IncomingDispatch::Applied(result) = result {
|
||||
if let Some(emit) = &result.emit {
|
||||
if let Some(ev_type) = emit.get("type") {
|
||||
println!("B inbound event: {ev_type:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ fn main() {
|
|||
// Player B receives the challenge
|
||||
println!("\n=== Player B receives challenge ===");
|
||||
let result = router.dispatch_incoming(&env, player_a, player_b).unwrap();
|
||||
if let IncomingDispatch::Applied(result) = result
|
||||
&& let Some(emit) = &result.emit
|
||||
{
|
||||
println!("Event: {:?}", emit.get("type"));
|
||||
if let IncomingDispatch::Applied(result) = result {
|
||||
if let Some(emit) = &result.emit {
|
||||
println!("Event: {:?}", emit.get("type"));
|
||||
}
|
||||
}
|
||||
|
||||
// Player B accepts
|
||||
|
|
@ -63,10 +63,10 @@ fn main() {
|
|||
let result = router
|
||||
.dispatch_incoming(&accept_env, player_b, player_a)
|
||||
.unwrap();
|
||||
if let IncomingDispatch::Applied(result) = result
|
||||
&& let Some(emit) = &result.emit
|
||||
{
|
||||
println!("Event: {:?}", emit.get("type"));
|
||||
if let IncomingDispatch::Applied(result) = result {
|
||||
if let Some(emit) = &result.emit {
|
||||
println!("Event: {:?}", emit.get("type"));
|
||||
}
|
||||
}
|
||||
|
||||
// Play some moves
|
||||
|
|
@ -103,12 +103,12 @@ fn main() {
|
|||
// Other player receives
|
||||
let result = router.dispatch_incoming(&move_env, player, other).unwrap();
|
||||
|
||||
if let IncomingDispatch::Applied(result) = result
|
||||
&& let Some(emit) = &result.emit
|
||||
{
|
||||
if let Some(payload_val) = emit.get("payload") {
|
||||
if let Some(board_val) = payload_val.get("b") {
|
||||
println!("Board: {}", board_val.as_str().unwrap_or("?"));
|
||||
if let IncomingDispatch::Applied(result) = result {
|
||||
if let Some(emit) = &result.emit {
|
||||
if let Some(payload_val) = emit.get("payload") {
|
||||
if let Some(board_val) = payload_val.get("b") {
|
||||
println!("Board: {}", board_val.as_str().unwrap_or("?"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,10 +43,10 @@ static FORCED_COIN: Mutex<Option<bool>> = Mutex::new(None);
|
|||
fn flip_responder_coin() -> bool {
|
||||
#[cfg(any(test, feature = "test-helpers"))]
|
||||
{
|
||||
if let Ok(guard) = FORCED_COIN.lock()
|
||||
&& let Some(v) = *guard
|
||||
{
|
||||
return v;
|
||||
if let Ok(guard) = FORCED_COIN.lock() {
|
||||
if let Some(v) = *guard {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
use rand::RngCore;
|
||||
|
|
@ -179,10 +179,10 @@ fn insufficient_material(board: &Board) -> bool {
|
|||
for sq in board.pieces(Piece::Bishop) & black {
|
||||
b_sq = Some(sq);
|
||||
}
|
||||
if let (Some(ws), Some(bs)) = (w_sq, b_sq)
|
||||
&& square_is_light(ws) == square_is_light(bs)
|
||||
{
|
||||
return true;
|
||||
if let (Some(ws), Some(bs)) = (w_sq, b_sq) {
|
||||
if square_is_light(ws) == square_is_light(bs) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
|
|
@ -1198,22 +1198,25 @@ impl ChessApp {
|
|||
// On claim, pre-terminate locally so UI reflects immediately.
|
||||
if reason == R_THREEFOLD || reason == R_FIFTY_MOVE {
|
||||
let moves = meta_string_list(&session.metadata, "moves");
|
||||
if let Ok(board) = replay_moves(&moves)
|
||||
&& claim_reason(&board, &moves) == Some(reason.as_str())
|
||||
{
|
||||
let _ =
|
||||
SessionStateMachine::apply_command(&mut session, CMD_DRAW_ACCEPT, false);
|
||||
session
|
||||
.metadata
|
||||
.insert("terminal".into(), JsonValue::String("draw".into()));
|
||||
session
|
||||
.metadata
|
||||
.insert("terminal_reason".into(), JsonValue::String(reason.clone()));
|
||||
session
|
||||
.metadata
|
||||
.insert("turn".into(), JsonValue::String("".into()));
|
||||
session.clear_draw_offer();
|
||||
completed_claim = true;
|
||||
if let Ok(board) = replay_moves(&moves) {
|
||||
if claim_reason(&board, &moves) == Some(reason.as_str()) {
|
||||
let _ = SessionStateMachine::apply_command(
|
||||
&mut session,
|
||||
CMD_DRAW_ACCEPT,
|
||||
false,
|
||||
);
|
||||
session
|
||||
.metadata
|
||||
.insert("terminal".into(), JsonValue::String("draw".into()));
|
||||
session
|
||||
.metadata
|
||||
.insert("terminal_reason".into(), JsonValue::String(reason.clone()));
|
||||
session
|
||||
.metadata
|
||||
.insert("turn".into(), JsonValue::String("".into()));
|
||||
session.clear_draw_offer();
|
||||
completed_claim = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !completed_claim {
|
||||
|
|
@ -1260,11 +1263,11 @@ impl ChessApp {
|
|||
}
|
||||
|
||||
fn handle_draw_decline_out(&self, session_id: &str, identity_id: &str) -> OutgoingResult {
|
||||
if let Some(mut session) = self.get_session(session_id, identity_id)
|
||||
&& SessionStateMachine::apply_command(&mut session, CMD_DRAW_DECLINE, false).is_ok()
|
||||
{
|
||||
session.clear_draw_offer();
|
||||
self.save_session(&session);
|
||||
if let Some(mut session) = self.get_session(session_id, identity_id) {
|
||||
if SessionStateMachine::apply_command(&mut session, CMD_DRAW_DECLINE, false).is_ok() {
|
||||
session.clear_draw_offer();
|
||||
self.save_session(&session);
|
||||
}
|
||||
}
|
||||
OutgoingResult {
|
||||
payload: HashMap::new(),
|
||||
|
|
@ -1529,16 +1532,16 @@ impl GameApp for ChessApp {
|
|||
sender_hash: &str,
|
||||
identity_id: &str,
|
||||
) -> IncomingResult {
|
||||
if command != CMD_ERROR
|
||||
&& let Err(message) = self.validate_incoming_payload(
|
||||
if command != CMD_ERROR {
|
||||
if let Err(message) = self.validate_incoming_payload(
|
||||
session_id,
|
||||
command,
|
||||
payload,
|
||||
identity_id,
|
||||
sender_hash,
|
||||
)
|
||||
{
|
||||
return error_result(ERR_PROTOCOL_ERROR, &message);
|
||||
) {
|
||||
return error_result(ERR_PROTOCOL_ERROR, &message);
|
||||
}
|
||||
}
|
||||
match command {
|
||||
CMD_CHALLENGE => {
|
||||
|
|
@ -1820,14 +1823,13 @@ impl GameApp for ChessApp {
|
|||
SessionStateMachine::check_expiry(&mut session, Some(&Self::ttl_policy()), None);
|
||||
if session.draw_offered_by().is_none() {
|
||||
session.clear_draw_offer();
|
||||
} else if let Some(owner) = session.draw_offered_by()
|
||||
&& owner != session.identity_id
|
||||
&& owner != session.contact_hash
|
||||
{
|
||||
return Err(LrgpError::Validation {
|
||||
code: ERR_PROTOCOL_ERROR.into(),
|
||||
message: "draw offer owner is not a bound participant".into(),
|
||||
});
|
||||
} else if let Some(owner) = session.draw_offered_by() {
|
||||
if owner != session.identity_id && owner != session.contact_hash {
|
||||
return Err(LrgpError::Validation {
|
||||
code: ERR_PROTOCOL_ERROR.into(),
|
||||
message: "draw offer owner is not a bound participant".into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
self.save_session(&session);
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -1106,11 +1106,12 @@ impl GameApp for FourInARowApp {
|
|||
sender_hash: &str,
|
||||
identity_id: &str,
|
||||
) -> IncomingResult {
|
||||
if command != CMD_ERROR
|
||||
&& let Err(message) =
|
||||
if command != CMD_ERROR {
|
||||
if let Err(message) =
|
||||
self.validate_wire_payload(session_id, command, payload, identity_id)
|
||||
{
|
||||
return error_result(ERR_PROTOCOL_ERROR, &message);
|
||||
{
|
||||
return error_result(ERR_PROTOCOL_ERROR, &message);
|
||||
}
|
||||
}
|
||||
match command {
|
||||
CMD_CHALLENGE => self.handle_challenge_in(session_id, sender_hash, identity_id),
|
||||
|
|
|
|||
|
|
@ -758,11 +758,11 @@ impl TicTacToeApp {
|
|||
}
|
||||
|
||||
fn handle_draw_offer_out(&self, session_id: &str, identity_id: &str) -> OutgoingResult {
|
||||
if let Some(mut session) = self.get_session(session_id, identity_id)
|
||||
&& SessionStateMachine::apply_command(&mut session, CMD_DRAW_OFFER, false).is_ok()
|
||||
{
|
||||
session.set_draw_offer(identity_id);
|
||||
self.save_session(&session);
|
||||
if let Some(mut session) = self.get_session(session_id, identity_id) {
|
||||
if SessionStateMachine::apply_command(&mut session, CMD_DRAW_OFFER, false).is_ok() {
|
||||
session.set_draw_offer(identity_id);
|
||||
self.save_session(&session);
|
||||
}
|
||||
}
|
||||
OutgoingResult {
|
||||
payload: HashMap::new(),
|
||||
|
|
@ -786,11 +786,11 @@ impl TicTacToeApp {
|
|||
}
|
||||
|
||||
fn handle_draw_decline_out(&self, session_id: &str, identity_id: &str) -> OutgoingResult {
|
||||
if let Some(mut session) = self.get_session(session_id, identity_id)
|
||||
&& SessionStateMachine::apply_command(&mut session, CMD_DRAW_DECLINE, false).is_ok()
|
||||
{
|
||||
session.clear_draw_offer();
|
||||
self.save_session(&session);
|
||||
if let Some(mut session) = self.get_session(session_id, identity_id) {
|
||||
if SessionStateMachine::apply_command(&mut session, CMD_DRAW_DECLINE, false).is_ok() {
|
||||
session.clear_draw_offer();
|
||||
self.save_session(&session);
|
||||
}
|
||||
}
|
||||
OutgoingResult {
|
||||
payload: HashMap::new(),
|
||||
|
|
@ -1051,11 +1051,12 @@ impl GameApp for TicTacToeApp {
|
|||
sender_hash: &str,
|
||||
identity_id: &str,
|
||||
) -> IncomingResult {
|
||||
if command != CMD_ERROR
|
||||
&& let Err(message) =
|
||||
if command != CMD_ERROR {
|
||||
if let Err(message) =
|
||||
self.validate_incoming_payload(session_id, command, payload, identity_id)
|
||||
{
|
||||
return error_result(ERR_PROTOCOL_ERROR, &message);
|
||||
{
|
||||
return error_result(ERR_PROTOCOL_ERROR, &message);
|
||||
}
|
||||
}
|
||||
match command {
|
||||
CMD_CHALLENGE => {
|
||||
|
|
@ -1270,14 +1271,13 @@ impl GameApp for TicTacToeApp {
|
|||
// Pre-owner persisted records and stray owner metadata cannot
|
||||
// safely authorize a response.
|
||||
session.clear_draw_offer();
|
||||
} else if let Some(owner) = session.draw_offered_by()
|
||||
&& owner != session.identity_id
|
||||
&& owner != session.contact_hash
|
||||
{
|
||||
return Err(LrgpError::Validation {
|
||||
code: ERR_PROTOCOL_ERROR.into(),
|
||||
message: "draw offer owner is not a bound participant".into(),
|
||||
});
|
||||
} else if let Some(owner) = session.draw_offered_by() {
|
||||
if owner != session.identity_id && owner != session.contact_hash {
|
||||
return Err(LrgpError::Validation {
|
||||
code: ERR_PROTOCOL_ERROR.into(),
|
||||
message: "draw offer owner is not a bound participant".into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
self.save_session(&session);
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -234,10 +234,10 @@ impl ReplayDedup {
|
|||
|
||||
pub fn forget_scoped_nonce(&mut self, identity_id: &str, session_id: &str, nonce: &[u8]) {
|
||||
let key = (identity_id.to_string(), session_id.to_string());
|
||||
if let Some(cache) = self.by_session.get_mut(&key)
|
||||
&& let Some(position) = cache.position(nonce)
|
||||
{
|
||||
cache.entries.remove(position);
|
||||
if let Some(cache) = self.by_session.get_mut(&key) {
|
||||
if let Some(position) = cache.position(nonce) {
|
||||
cache.entries.remove(position);
|
||||
}
|
||||
}
|
||||
if self
|
||||
.by_session
|
||||
|
|
|
|||
|
|
@ -135,12 +135,14 @@ impl LrgpRouter {
|
|||
return Ok(IncomingDispatch::Replay);
|
||||
}
|
||||
|
||||
if validated.command == CMD_CHALLENGE
|
||||
&& let Some((owner_app, _)) =
|
||||
if validated.command == CMD_CHALLENGE {
|
||||
if let Some((owner_app, _)) =
|
||||
self.find_session_owner(&validated.session_id, identity_id)
|
||||
&& owner_app != validated.app_id
|
||||
{
|
||||
return Err(LrgpError::SessionExists(validated.session_id));
|
||||
{
|
||||
if owner_app != validated.app_id {
|
||||
return Err(LrgpError::SessionExists(validated.session_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if validated.command == CMD_ERROR {
|
||||
|
|
@ -319,12 +321,12 @@ impl LrgpRouter {
|
|||
if session.contact_hash.is_empty() {
|
||||
return Err(LrgpError::ParticipantRequired);
|
||||
}
|
||||
if let Some(recipient) = recipient_hash
|
||||
&& recipient != session.contact_hash
|
||||
{
|
||||
return Err(LrgpError::UnauthorizedPeer {
|
||||
session_id: session.session_id,
|
||||
});
|
||||
if let Some(recipient) = recipient_hash {
|
||||
if recipient != session.contact_hash {
|
||||
return Err(LrgpError::UnauthorizedPeer {
|
||||
session_id: session.session_id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if command != CMD_ERROR {
|
||||
|
|
@ -362,15 +364,15 @@ impl LrgpRouter {
|
|||
app.handle_outgoing(&effective_session_id, command, payload, identity_id)
|
||||
};
|
||||
|
||||
if command == CMD_CHALLENGE
|
||||
&& let Err(error) = app.bind_session_peer(
|
||||
if command == CMD_CHALLENGE {
|
||||
if let Err(error) = app.bind_session_peer(
|
||||
&effective_session_id,
|
||||
identity_id,
|
||||
recipient_hash.expect("challenge recipient checked above"),
|
||||
)
|
||||
{
|
||||
app.rollback_session(&effective_session_id, identity_id, snapshot);
|
||||
return Err(error);
|
||||
) {
|
||||
app.rollback_session(&effective_session_id, identity_id, snapshot);
|
||||
return Err(error);
|
||||
}
|
||||
}
|
||||
|
||||
let final_envelope = envelope::pack_envelope(
|
||||
|
|
@ -501,9 +503,10 @@ impl LrgpRouter {
|
|||
let _creation_guard = self.session_creation.lock().unwrap();
|
||||
if let Some((owner_app, _)) =
|
||||
self.find_session_owner(&session.session_id, &session.identity_id)
|
||||
&& owner_app != session.app_id
|
||||
{
|
||||
return Err(LrgpError::SessionExists(session.session_id));
|
||||
if owner_app != session.app_id {
|
||||
return Err(LrgpError::SessionExists(session.session_id));
|
||||
}
|
||||
}
|
||||
app.upsert_session(session)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue