Skip to content

Commit

Permalink
refactor: restructure project error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
doinkythederp committed Mar 8, 2024
1 parent 8917fdc commit c084262
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 282 deletions.
3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mc-varint = "0.1"
rand = { version = "0.8", optional = true }
serde = { version = "1.0", optional = true, features = ["serde_derive"] }
serde_json = { version = "1.0", optional = true }
snafu = { version = "0.7", features = ["backtraces-impl-backtrace-crate"] }
snafu = { version = "0.8.1", features = ["backtraces-impl-backtrace-crate"] }
tokio = { version = "1.21", features = [
"net",
"io-util",
Expand All @@ -35,7 +35,6 @@ tokio = { version = "1.21", features = [
], optional = true }
tracing = "0.1"
trust-dns-resolver = { version = "0.23", optional = true }
#void = { version = "1.0", optional = true }

[dev-dependencies]
ctor = "0.2.4"
Expand Down
60 changes: 13 additions & 47 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
use snafu::{Backtrace, Snafu};
use std::time::Duration;
#[cfg(feature = "java_connect")]
use tokio::net::{lookup_host, TcpStream};
use tracing::{debug, info, instrument};

#[cfg(feature = "java_connect")]
pub mod mc_string;
#[cfg(feature = "java_connect")]
pub mod protocol;
#[cfg(feature = "java_connect")]
use crate::protocol::{ping_error, protocol_error, ProtocolError};
#[cfg(feature = "simple")]
pub use protocol::PingError;
pub use crate::protocol::connect;
#[cfg(feature = "java_connect")]
pub use protocol::SlpProtocol;

Expand All @@ -22,46 +18,16 @@ pub use parse::JavaServerInfo;
#[cfg(feature = "bedrock")]
pub mod bedrock;

#[cfg(feature = "java_connect")]
#[instrument]
pub async fn connect(mut addrs: (String, u16)) -> Result<SlpProtocol, ProtocolError> {
use tracing::debug;
use trust_dns_resolver::TokioAsyncResolver;

let resolver = TokioAsyncResolver::tokio_from_system_conf()?;
if let Ok(records) = resolver
.srv_lookup(format!("_minecraft._tcp.{}", addrs.0))
.await
{
if let Some(record) = records.iter().next() {
let record = record.target().to_utf8();
debug!("Found SRV record: {} -> {}", addrs.0, record);
addrs.0 = record;
}
}

// lookup_host can return multiple but we just need one so we discard the rest
let socket_addrs = match lookup_host(addrs.clone()).await?.next() {
Some(socket_addrs) => socket_addrs,
None => {
info!("DNS lookup failed for address");
return Err(protocol_error::DNSLookupFailedSnafu {
address: format!("{:?}", addrs),
}
.build());
}
};

match TcpStream::connect(socket_addrs).await {
Ok(stream) => {
info!("Connected to SLP server");
Ok(SlpProtocol::new(addrs.0, addrs.1, stream))
}
Err(error) => {
info!("Failed to connect to SLP server: {}", error);
Err(error.into())
}
}
#[cfg(feature = "simple")]
#[derive(Snafu, Debug)]
pub enum PingError {
#[snafu(display("connection failed: {source}"), context(false))]
Protocol {
#[snafu(backtrace)]
source: crate::protocol::ProtocolError,
},
#[snafu(display("connection did not respond in time"))]
Timeout { backtrace: Backtrace },
}

#[cfg(feature = "simple")]
Expand All @@ -86,7 +52,7 @@ pub async fn ping_or_timeout(
select! {
biased;
info = ping(addrs) => info,
_ = sleep => Err(ping_error::TimeoutSnafu.build()),
_ = sleep => TimeoutSnafu.fail(),
}
}

Expand Down
63 changes: 31 additions & 32 deletions src/mc_string.rs
Original file line number Diff line number Diff line change
@@ -1,54 +1,53 @@
use bytes::Buf;
use mc_varint::{VarInt, VarIntRead, VarIntWrite};
use snafu::{OptionExt, Snafu};
use std::io::Cursor;

mod error {
use super::*;
use snafu::Snafu;

#[derive(Snafu, Debug)]
pub enum McStringError {
#[snafu(display("io error: {source}"))]
Io { source: std::io::Error },
#[snafu(display(
"string is too long (is {length} bytes, but expected less than {} bytes)",
MAX_LEN
))]
TooLong { length: usize },
#[snafu(display("invalid string format"))]
InvalidFormat,
}
#[derive(Snafu, Debug)]
pub enum McStringError {
#[snafu(display("io error: {source}"), context(false))]
Io {
source: std::io::Error,
backtrace: snafu::Backtrace,
},
#[snafu(display(
"string is too long (is {length} bytes, but expected less than {} bytes)",
MAX_LEN
))]
TooLong {
length: usize,
backtrace: snafu::Backtrace,
},
#[snafu(display("invalid string format"))]
InvalidFormat { backtrace: snafu::Backtrace },
}

pub use error::McStringError;

pub const MAX_LEN: i32 = i32::MAX;

pub fn encode_mc_string(string: &str) -> Result<Vec<u8>, McStringError> {
let len = string.len();
// VarInt max length is 5 bytes
let mut bytes = Vec::with_capacity(len + 5);
bytes
.write_var_int(VarInt::from(
i32::try_from(len)
.ok()
.ok_or(McStringError::TooLong { length: len })?,
))
.map_err(|io| McStringError::Io { source: io })?;
bytes.write_var_int(VarInt::from(
i32::try_from(len)
.ok()
.context(TooLongSnafu { length: len })?,
))?;
bytes.extend_from_slice(string.as_bytes());
Ok(bytes)
}

pub fn decode_mc_string(cursor: &mut Cursor<&[u8]>) -> Result<String, McStringError> {
let len: i32 = cursor
.read_var_int()
.map_err(|io| McStringError::Io { source: io })?
.into();
let len = usize::try_from(len).map_err(|_| McStringError::InvalidFormat)?;
let len: i32 = cursor.read_var_int()?.into();
let len = usize::try_from(len).ok().context(InvalidFormatSnafu)?;

let bytes = cursor.chunk();
let string = std::str::from_utf8(&bytes[..len])
.map_err(|_| McStringError::InvalidFormat)?
if len > bytes.len() {
return InvalidFormatSnafu.fail();
}
let string = std::str::from_utf8(bytes.get(..len).context(InvalidFormatSnafu)?)
.ok()
.context(InvalidFormatSnafu)?
.to_string();
cursor.advance(len);
Ok(string)
Expand Down
Loading

0 comments on commit c084262

Please sign in to comment.