fix(client): remove non-Windows tunnel stub

The Windows client now fails fast outside Windows before tunnel setup because
its real MVP runtime depends on TAP-Windows6 and Windows route protection. The
old non-Windows runtime branch still built a control-event loop and printed
that TAP pumping was not wired yet, which described an obsolete smoke-test path
rather than current behavior.

Replace that branch with an unreachable guard and compile the related runtime
formatting/status helpers only for Windows or unit tests. Non-Windows builds
still type-check the client, but the runtime behavior is now only the explicit
unsupported-platform error.

Test Plan:
- cargo fmt
- cargo test -p lanparty-client-win
- cargo clippy --workspace --all-targets -- -D warnings
- cargo test --workspace

Refs: MVP Windows client runtime gate
This commit is contained in:
2026-05-22 09:01:33 +02:00
parent 5a65b62a3d
commit 91a781cd7b
+27 -35
View File
@@ -1,8 +1,11 @@
#[cfg(any(windows, test))]
use std::collections::BTreeMap;
#[cfg(any(windows, test))]
use std::sync::{
Arc,
atomic::{AtomicBool, AtomicU32, Ordering},
};
use std::{collections::BTreeMap, fs, net::IpAddr, path::PathBuf};
use std::{fs, net::IpAddr, path::PathBuf};
#[cfg(windows)]
use std::{sync::mpsc, thread, time::Duration};
@@ -21,12 +24,13 @@ use lanparty_client_route::{
#[cfg(windows)]
use lanparty_client_tap::TapAdapter;
use lanparty_client_tap::TapAdapterInfo;
use lanparty_ctrl::{ControlMessage, PeerInfo, Role, RoomCode};
use lanparty_ctrl::RoomCode;
#[cfg(any(windows, test))]
use lanparty_ctrl::{ControlMessage, PeerInfo, Role};
use lanparty_net::RelayEndpoint;
use lanparty_obs::{
ClientDiagnostics, DropReason, FrameAction, FrameDirection, FrameLog, RelayDiagnostics,
TapDiagnostics, UserDiagnostic, UserDiagnosticLevel,
};
#[cfg(any(windows, test))]
use lanparty_obs::{ClientDiagnostics, RelayDiagnostics, UserDiagnostic, UserDiagnosticLevel};
use lanparty_obs::{DropReason, FrameAction, FrameDirection, FrameLog, TapDiagnostics};
use lanparty_proto::{EthernetFrame, MacAddr};
#[cfg(windows)]
@@ -371,30 +375,8 @@ async fn run_client(
}
#[cfg(not(windows))]
async fn run_client(session: &ClientSession) -> Result<()> {
let relay_status = ClientRelayStatus::from_welcome(session);
open_tap_adapter(session);
print_and_report_client_diagnostics(
session,
&client_diagnostics_snapshot(
session,
false,
&relay_status,
TapDiagnostics::new(false, None, None, None),
),
)
.await;
println!("TAP frame pump and route pinning are not wired yet; press Ctrl-C to stop");
let control_events = run_control_event_log(session, relay_status);
tokio::pin!(control_events);
let shutdown = tokio::signal::ctrl_c();
tokio::pin!(shutdown);
tokio::select! {
result = &mut control_events => result,
result = &mut shutdown => result.context("failed to wait for Ctrl-C"),
}
async fn run_client(_session: &ClientSession) -> Result<()> {
unreachable!("ensure_supported_platform rejects non-Windows before tunnel setup")
}
#[cfg(windows)]
@@ -678,6 +660,7 @@ fn format_tap_adapter_list(adapters: &[TapAdapterInfo]) -> Vec<String> {
lines
}
#[cfg(windows)]
fn client_diagnostics_snapshot(
session: &ClientSession,
route_pinned: bool,
@@ -705,6 +688,7 @@ fn tap_diagnostics_with_ip(base: &TapDiagnostics, ip: Option<IpAddr>) -> TapDiag
TapDiagnostics::new(base.adapter_found(), base.mac(), base.mtu(), ip)
}
#[cfg(windows)]
fn print_client_diagnostics(diagnostics: &ClientDiagnostics) {
println!("{}", format_client_diagnostics(diagnostics));
for diagnostic in diagnostics.user_diagnostics() {
@@ -712,6 +696,7 @@ fn print_client_diagnostics(diagnostics: &ClientDiagnostics) {
}
}
#[cfg(windows)]
async fn print_and_report_client_diagnostics(
session: &ClientSession,
diagnostics: &ClientDiagnostics,
@@ -722,6 +707,7 @@ async fn print_and_report_client_diagnostics(
}
}
#[cfg(any(windows, test))]
fn format_client_diagnostics(diagnostics: &ClientDiagnostics) -> String {
let stats = diagnostics.stats();
format!(
@@ -793,6 +779,7 @@ fn client_frame_log_line(
)
}
#[cfg(any(windows, test))]
const fn yes_no(value: bool) -> &'static str {
if value { "yes" } else { "no" }
}
@@ -805,14 +792,17 @@ fn gateway_status_label(gateway_connected: bool, gateway_peer_id: Option<u32>) -
}
}
#[cfg(any(windows, test))]
fn optional_label<T: std::fmt::Display>(value: Option<T>) -> String {
value.map_or_else(|| "unknown".to_string(), |value| value.to_string())
}
#[cfg(any(windows, test))]
fn optional_millis_label(value: Option<u64>) -> String {
value.map_or_else(|| "unknown".to_string(), |value| format!("{value} ms"))
}
#[cfg(any(windows, test))]
fn format_user_diagnostic(diagnostic: &UserDiagnostic) -> String {
match diagnostic.level() {
UserDiagnosticLevel::Info => diagnostic.message().to_owned(),
@@ -821,6 +811,7 @@ fn format_user_diagnostic(diagnostic: &UserDiagnostic) -> String {
}
}
#[cfg(windows)]
async fn run_control_event_log(
session: &ClientSession,
relay_status: ClientRelayStatus,
@@ -842,12 +833,14 @@ fn format_control_event(event: &ControlMessage) -> String {
ControlEventFormatter::default().format(event, &relay_status)
}
#[cfg(any(windows, test))]
#[derive(Debug, Clone)]
struct ClientRelayStatus {
gateway_connected: Arc<AtomicBool>,
gateway_peer_id: Arc<AtomicU32>,
}
#[cfg(any(windows, test))]
impl ClientRelayStatus {
const UNKNOWN_GATEWAY_PEER_ID: u32 = 0;
@@ -858,6 +851,7 @@ impl ClientRelayStatus {
}
}
#[cfg(windows)]
fn from_welcome(session: &ClientSession) -> Self {
let status = Self::new(session.welcome().gateway_connected());
status.set_gateway_peer_id(session.welcome().gateway_peer_id());
@@ -891,11 +885,13 @@ impl ClientRelayStatus {
}
}
#[cfg(any(windows, test))]
#[derive(Debug, Default)]
struct ControlEventFormatter {
peers: BTreeMap<u32, PeerInfo>,
}
#[cfg(any(windows, test))]
impl ControlEventFormatter {
fn format(&mut self, event: &ControlMessage, relay_status: &ClientRelayStatus) -> String {
match event {
@@ -944,6 +940,7 @@ impl ControlEventFormatter {
}
}
#[cfg(any(windows, test))]
fn format_peer_joined(peer: &PeerInfo) -> String {
match peer.role() {
Role::Gateway => {
@@ -1259,11 +1256,6 @@ fn read_and_relay_tap_frame(
Ok(())
}
#[cfg(not(windows))]
fn open_tap_adapter(_session: &ClientSession) {
println!("Windows TAP adapter opening is compiled only on Windows");
}
#[cfg(test)]
mod tests {
use std::{