feat: implement client

This commit is contained in:
2026-02-12 13:29:15 -08:00
parent 0d0a548a46
commit 5aa6b98742
23 changed files with 645 additions and 164 deletions

18
client/Cargo.toml Normal file
View File

@@ -0,0 +1,18 @@
[package]
name = "client"
version = "0.1.0"
edition = "2024"
[dependencies]
console = "0.16.2"
futures = "0.3.31"
futures-util = "0.3.31"
serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149"
thiserror = "2.0.18"
thiserror-ext = "0.3.0"
tokio = { version = "1.49.0", features = ["macros", "rt-multi-thread"] }
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-native-roots"] }
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.22", features = ["env-filter"] }
registry = { path = "../registry" }

View File

@@ -1 +0,0 @@
fn main() {}

20
client/src/error.rs Normal file
View File

@@ -0,0 +1,20 @@
use thiserror::Error;
use thiserror_ext::{Box, Construct};
use tokio_tungstenite::tungstenite;
#[derive(Error, Debug, Box, Construct)]
#[thiserror_ext(newtype(name = Error))]
pub enum ErrorKind {
#[error("error connecting to websocket")]
WsConnect {
url: String,
#[source]
source: tungstenite::Error,
},
#[error("error reading websocket msg")]
WsRead(#[source] tungstenite::Error),
#[error("error deserializing json")]
DeserializeJson(#[source] serde_json::Error),
}
pub type Result<T> = core::result::Result<T, Error>;

63
client/src/main.rs Normal file
View File

@@ -0,0 +1,63 @@
use console::style;
use futures::StreamExt;
use registry::PeerMessage;
use thiserror_ext::AsReport;
use tokio_tungstenite::tungstenite::Message;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::{
EnvFilter, fmt::format::FmtSpan, layer::SubscriberExt, util::SubscriberInitExt,
};
use crate::error::Error;
mod error;
async fn run() -> crate::error::Result<()> {
let url = get_api_from_env();
let (ws_stream, response) = tokio_tungstenite::connect_async(&url)
.await
.map_err(|e| Error::ws_connect(e, &url))?;
let (_, mut read) = ws_stream.split();
tracing::info!("connected, response: {:?}", response.status());
while let Some(msg) = read.next().await {
match msg.map_err(|e| Error::ws_read(e))? {
Message::Text(text) => {
let server_msg: PeerMessage =
serde_json::from_str(&text).map_err(|e| Error::deserialize_json(e))?;
match server_msg {
PeerMessage::HydratePeers { peers } => {}
PeerMessage::PeerUpdate { peer } => {}
}
}
_ => {}
}
}
Ok(())
}
#[tokio::main]
async fn main() {
let tracing_env_filter = EnvFilter::builder()
.with_default_directive(LevelFilter::INFO.into())
.from_env_lossy();
tracing_subscriber::registry()
.with(tracing_env_filter)
.with(
tracing_subscriber::fmt::layer()
.compact()
.with_span_events(FmtSpan::CLOSE),
)
.init();
if let Err(e) = run().await {
eprintln!("{}: {}", style("error").red(), e.as_report());
std::process::exit(1)
}
}
fn get_api_from_env() -> String {
return "ws://localhost:8080/ws/peers".to_string();
}