diff --git a/migrations/2026-05-28-142613-0000_servers/up.sql b/migrations/2026-05-28-142613-0000_servers/up.sql index 3b6dbfd..fbdeb6f 100644 --- a/migrations/2026-05-28-142613-0000_servers/up.sql +++ b/migrations/2026-05-28-142613-0000_servers/up.sql @@ -1,5 +1,5 @@ CREATE TABLE "servers" ( - "id" int PRIMARY KEY, + "id" BIGSERIAL PRIMARY KEY, "name" varchar(255) UNIQUE NOT NULL, "volume_path" varchar(255) UNIQUE NOT NULL, "last_login" timestamp, diff --git a/src/main.rs b/src/main.rs index aa7c8fc..4914910 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,42 +1,55 @@ -//use tokio::net::TcpListener; -use std::io; +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* main.rs :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tomoron +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/29 21:22:17 by tomoron #+# #+# */ +/* Updated: 2026/05/29 21:31:39 by tomoron ### ########.fr */ +/* */ +/* ************************************************************************** */ -use bollard::{ - query_parameters::{ - ListImagesOptionsBuilder, - ListContainersOptionsBuilder, - CreateContainerOptionsBuilder - }, - models::{ - ContainerCreateBody - }, - Docker -}; +use tokio::net::TcpListener; +use tokio::io; +//use bollard::{ +// query_parameters::{ +// ListImagesOptionsBuilder, +// ListContainersOptionsBuilder, +// CreateContainerOptionsBuilder +// }, +// models::{ +// ContainerCreateBody +// }, +// Docker +//}; -//mod minecraft; -//use minecraft::process_mc_socket; +mod minecraft; +use minecraft::process_mc_socket; -//mod rpc; -//use rpc::process_rpc_socket; +mod rpc; +use rpc::process_rpc_socket; use diesel::{prelude::*, r2d2::{ConnectionManager, Pool}}; -mod models; +//mod models; -use dockermcmgr::schema; +//use dockermcmgr::schema; -use crate::models::{ Servers, CreateServer }; +//use crate::models::{ Servers, CreateServer }; use std::env; -pub mod status; +//pub mod status; -use status::ServerStatus; +//use status::ServerStatus; +// +pub type DbPool = Pool>; -fn get_connection_pool() -> Pool> { +fn get_connection_pool() -> DbPool { let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set"); let manager = ConnectionManager::::new(&database_url); @@ -47,13 +60,37 @@ fn get_connection_pool() -> Pool> { #[tokio::main(flavor = "current_thread")] async fn main() -> io::Result<()> { -// let conn = DB_CONN.get().expect("failed to get the connection from the pool"); + let pool = get_connection_pool(); + let mc_listener = TcpListener::bind("0.0.0.0:25565").await?; + let rpc_listener = TcpListener::bind("0.0.0.0:8080").await?; + + loop { + tokio::select! { + Ok((socket, _)) = mc_listener.accept() => { + let cloned = pool.clone(); + tokio::spawn(async move { + if let Err(e) = process_mc_socket(socket, cloned).await { + eprintln!("mc error: {:?}", e); + } + }); + } + + Ok((socket, _)) = rpc_listener.accept() => { + let cloned = pool.clone(); + tokio::spawn(async move { + if let Err(e) = process_rpc_socket(socket, cloned).await { + eprintln!("rpc error: {:?}", e); + } + }); + } + } + } + /* + let conn = &mut pool.get().unwrap(); - let servers = schema::servers::table.select(Servers::as_select()).load(conn); - println!("servers : {:?}", servers); let new_server = CreateServer { name: "potato", @@ -64,7 +101,24 @@ async fn main() -> io::Result<()> { redirect_ip: None }; + match diesel::insert_into(schema::servers::table) + .values(&new_server) + .execute(conn) { + Ok(a) => { println!("{:?}", a); }, + Err(reason) => {println!("got an error : {:?}", reason) } + } + + let start = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + let servers = schema::servers::table.select(Servers::as_select()).load(conn).unwrap(); + let end = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + println!("servers : {:?}", servers); + println!("retreived in {:?}", end - start); + + + println!("server id : {}",servers[0].id); + Ok(()) + */ /* println!("{:?}", docker); let options = ListImagesOptionsBuilder::default() @@ -95,27 +149,5 @@ async fn main() -> io::Result<()> { let start_res = docker.start_container("rust-created-container", None).await; */ /* - let mc_listener = TcpListener::bind("0.0.0.0:25565").await?; - let rpc_listener = TcpListener::bind("0.0.0.0:8080").await?; - - loop { - tokio::select! { - Ok((socket, _)) = mc_listener.accept() => { - tokio::spawn(async move { - if let Err(e) = process_mc_socket(socket).await { - eprintln!("mc error: {:?}", e); - } - }); - } - - Ok((socket, _)) = rpc_listener.accept() => { - tokio::spawn(async move { - if let Err(e) = process_rpc_socket(socket).await { - eprintln!("rpc error: {:?}", e); - } - }); - } - } - } */ } diff --git a/src/minecraft/client.rs b/src/minecraft/client.rs deleted file mode 100644 index de1d6e6..0000000 --- a/src/minecraft/client.rs +++ /dev/null @@ -1,228 +0,0 @@ -/* ************************************************************************** */ -/* */ -/* ::: :::::::: */ -/* client.rs :+: :+: :+: */ -/* +:+ +:+ +:+ */ -/* By: tomoron +#+ +:+ +#+ */ -/* +#+#+#+#+#+ +#+ */ -/* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */ -/* Updated: 2026/05/20 17:54:32 by tomoron ### ########.fr */ -/* */ -/* ************************************************************************** */ - -use json::object; -use tokio::net::TcpStream; -use std::collections::VecDeque; -use crate::minecraft::handshake::Handshake; -use crate::minecraft::varint::{varint_read, varint_write}; -use std::{fmt, io}; - -pub struct Client { - pub in_stream: TcpStream, - - read_buf: Vec, - buffer: Vec, - out_stream: Option, - handshake: Option -} - -impl Client { - pub fn create(stream: TcpStream) -> Self { - Self { - in_stream: stream, - - read_buf: vec![0u8; 1024 * 8], - buffer: vec![], - out_stream: None, - handshake: None - } - } - - pub async fn in_read(&mut self) -> Result { - loop - { - match self.in_stream.try_read(&mut self.read_buf) { - Ok(n) => { - if let Some(out_stream) = &self.out_stream { - let _ = out_stream.writable().await; - let _ = out_stream.try_write(&self.read_buf[..n]); - } else { - self.buffer_append((&self.read_buf[..n]).to_vec()).await?; - } - if n == 0 { - return Ok(0) - } - } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - return Ok(1); - } - Err(_e) => { - return Err("read failed".to_string()) - } - } - } - } - - pub async fn out_readable(&self) { - if let Some(stream) = &self.out_stream { - stream.readable().await.expect("readable call failed"); - } else { - std::future::pending::<()>().await; - }; - } - - pub async fn out_read(&mut self) -> Result { - if let None = self.out_stream { - return Ok(1); - } - - - match self.out_stream.as_mut().unwrap().try_read(&mut self.read_buf) { - Ok(n) => { - let _ = self.in_stream.writable().await; - let _ = self.in_stream.try_write(&self.read_buf[..n]); - if n == 0 { Ok(0) } else { Ok(1) } - } - Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { - Ok(1) - } - - Err(_e) => { - Err("Failed to read".to_string()) - } - } - } - - pub async fn buffer_append(&mut self, data: Vec) -> Result<(),String> { - if data.len() == 0 { - return Ok(()); - } - - - if self.buffer.len() + data.len() > 65536 { - return Err("buffer full".to_string()); - } - - let _ = self.buffer.extend(data); - - while self.buffer.len() != 0 && self.buffer.len() - 1 >= self.buffer[0] as usize { - if self.buffer[0] == 0 { - return Err("invalid packet".to_string()); - } - - - let len = varint_read(&mut self.buffer.clone().into())? as usize; - self.handle_packet(self.buffer[1..=len].to_vec().into()).await?; - self.buffer.drain(..len + 1); - - if let Some(out_stream) = &self.out_stream { - if self.buffer.len() != 0 { - let _ = out_stream.writable().await; - let _ = out_stream.try_write(self.buffer.as_slice()); - self.buffer.drain(..); - } - } - } - Ok(()) - } - - async fn send_packet(&self, data: Vec) { - let mut sent_data: Vec = varint_write(data.len() as i32); - - sent_data.extend(data); - let _ = self.in_stream.writable().await; - match self.in_stream.try_write(sent_data.as_slice()) { - Err(e) => { eprintln!("error while sending response {:?}", e); }, - _ => { } - } - } - - async fn handle_packet(&mut self, mut packet: VecDeque) -> Result<(), String> { - let packet_id = varint_read(&mut packet)?; - - - if self.handshake.is_none() { - if packet_id != 0 { - return Err("packet 0 expected. invalid packet received".to_string()); - } - self.handshake = Some(Handshake::from_packet(&mut packet)?); - - if self.handshake.as_ref().unwrap().intent == 2 { - self.login_intent_handle(&mut packet, packet_id).await?; - } - - return Ok(()); - } - - let intent = self.handshake.as_ref().unwrap().intent; - - if intent == 1 { - self.status_intent_handle(&mut packet, packet_id).await?; - } - Ok(()) - } - - async fn status_intent_handle(&self, packet: &mut VecDeque, packet_id: i32) -> Result<(),String> { - let status_response = object! { - "version": { - "name": "idk", - "protocol" : self.handshake.as_ref().unwrap().protocol_version, - }, - "players": { - "max": 420, - "online": 69 - }, - "description": "§c".to_string() + &self.handshake.as_ref().unwrap().server_address.clone() + "§r:§a" + &self.handshake.as_ref().unwrap().server_port.to_string(), - }; - if packet_id == 0 { - let mut response : Vec = vec![]; - - let response_json = json::stringify(status_response); - let mut response_json_len = varint_write(response_json.len() as i32); - let mut response_packet_id = varint_write(0); - - response.append(&mut response_packet_id); - response.append(&mut response_json_len); - response.append(&mut response_json.as_bytes().into()); - println!("[{}] sent status response to client", self); - - self.send_packet(response).await; - } else if packet_id == 1 { - let mut response = varint_write(1); - response.extend(packet.drain(..)); - self.send_packet(response).await; - println!("[{}] sent ping response to client", self); - } - Ok(()) - } - - async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque, _packet_id: i32) -> Result<(),String> { - let stream = TcpStream::connect("play.hypixel.net:25565").await.map_err(|_| "failed to connect to remote host".to_string())?; - let mut new_handshake = self.handshake.as_ref().unwrap().clone(); - new_handshake.server_address = "play.hypixel.net".to_string(); - new_handshake.server_port = 25565; - - let handshake_packet = new_handshake.to_packet(); - let mut packet = Vec::new(); - packet.extend(varint_write(handshake_packet.len() as i32)); - packet.extend(handshake_packet); - - self.out_stream = Some(stream); - let _ = self.out_stream.as_ref().unwrap().writable().await; - - let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice()); - println!("[{}] sent handshake to server", self); - Ok(()) - } -} - -impl fmt::Display for Client { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(handshake) = &self.handshake { - write!(f, "{}", handshake.server_address) - } else { - use std::os::unix::io::AsRawFd; - write!(f, "{}", self.in_stream.as_raw_fd()) - } - } -} diff --git a/src/minecraft/client/buffer.rs b/src/minecraft/client/buffer.rs new file mode 100644 index 0000000..7d17cf9 --- /dev/null +++ b/src/minecraft/client/buffer.rs @@ -0,0 +1,122 @@ +use crate::minecraft::client::client::Client; + +use crate::minecraft::varint::varint_read; +use crate::minecraft::handshake::Handshake; +use std::collections::VecDeque; + +use std::io; + +impl Client { + pub async fn in_read(&mut self) -> Result { + loop + { + match self.in_stream.try_read(&mut self.read_buf) { + Ok(n) => { + if let Some(out_stream) = &self.out_stream { + let _ = out_stream.writable().await; + let _ = out_stream.try_write(&self.read_buf[..n]); + } else { + self.buffer_append((&self.read_buf[..n]).to_vec()).await?; + } + if n == 0 { + return Ok(0) + } + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + return Ok(1); + } + Err(_e) => { + return Err("read failed".to_string()) + } + } + } + } + + pub async fn out_readable(&self) { + if let Some(stream) = &self.out_stream { + stream.readable().await.expect("readable call failed"); + } else { + std::future::pending::<()>().await; + }; + } + + pub async fn out_read(&mut self) -> Result { + if let None = self.out_stream { + return Ok(1); + } + + + match self.out_stream.as_mut().unwrap().try_read(&mut self.read_buf) { + Ok(n) => { + let _ = self.in_stream.writable().await; + let _ = self.in_stream.try_write(&self.read_buf[..n]); + if n == 0 { Ok(0) } else { Ok(1) } + } + Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { + Ok(1) + } + + Err(_e) => { + Err("Failed to read".to_string()) + } + } + } + + pub async fn buffer_append(&mut self, data: Vec) -> Result<(),String> { + if data.len() == 0 { + return Ok(()); + } + + + if self.buffer.len() + data.len() > 65536 { + return Err("buffer full".to_string()); + } + + let _ = self.buffer.extend(data); + + while self.buffer.len() != 0 && self.buffer.len() - 1 >= self.buffer[0] as usize { + if self.buffer[0] == 0 { + return Err("invalid packet".to_string()); + } + + + let len = varint_read(&mut self.buffer.clone().into())? as usize; + self.handle_packet(self.buffer[1..=len].to_vec().into()).await?; + self.buffer.drain(..len + 1); + + if let Some(out_stream) = &self.out_stream { + if self.buffer.len() != 0 { + let _ = out_stream.writable().await; + let _ = out_stream.try_write(self.buffer.as_slice()); + self.buffer.drain(..); + } + } + } + Ok(()) + } + + async fn handle_packet(&mut self, mut packet: VecDeque) -> Result<(), String> { + let packet_id = varint_read(&mut packet)?; + + + if self.handshake.is_none() { + if packet_id != 0 { + return Err("packet 0 expected. invalid packet received".to_string()); + } + self.handshake = Some(Handshake::from_packet(&mut packet)?); + + if self.handshake.as_ref().unwrap().intent == 2 { + self.login_intent_handle(&mut packet, packet_id).await?; + } + + return Ok(()); + } + + let intent = self.handshake.as_ref().unwrap().intent; + + if intent == 1 { + self.status_intent_handle(&mut packet, packet_id).await?; + } + Ok(()) + } +} diff --git a/src/minecraft/client/client.rs b/src/minecraft/client/client.rs new file mode 100644 index 0000000..df57aa8 --- /dev/null +++ b/src/minecraft/client/client.rs @@ -0,0 +1,67 @@ +/* ************************************************************************** */ +/* */ +/* ::: :::::::: */ +/* client.rs :+: :+: :+: */ +/* +:+ +:+ +:+ */ +/* By: tomoron +#+ +:+ +#+ */ +/* +#+#+#+#+#+ +#+ */ +/* Created: 2026/05/07 17:23:09 by tomoron #+# #+# */ +/* Updated: 2026/05/30 00:27:19 by tomoron ### ########.fr */ +/* */ +/* ************************************************************************** */ + +use tokio::net::TcpStream; +use crate::minecraft::handshake::Handshake; +use crate::minecraft::varint::varint_write; +use std::fmt; + +pub struct Client { + pub in_stream: TcpStream, + + pub read_buf: Vec, + pub buffer: Vec, + pub out_stream: Option, + pub handshake: Option, + pub dbPool: DbPool +} + +use crate::DbPool; + +impl Client { + pub fn create(stream: TcpStream, pool: DbPool) -> Self { + Self { + in_stream: stream, + + read_buf: vec![0u8; 1024 * 8], + buffer: vec![], + out_stream: None, + handshake: None, + + dbPool: pool + } + } + + + pub async fn send_packet(&self, data: Vec) { + let mut sent_data: Vec = varint_write(data.len() as i32); + + sent_data.extend(data); + let _ = self.in_stream.writable().await; + match self.in_stream.try_write(sent_data.as_slice()) { + Err(e) => { eprintln!("error while sending response {:?}", e); }, + _ => { } + } + } + +} + +impl fmt::Display for Client { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(handshake) = &self.handshake { + write!(f, "{}", handshake.server_address) + } else { + use std::os::unix::io::AsRawFd; + write!(f, "{}", self.in_stream.as_raw_fd()) + } + } +} diff --git a/src/minecraft/client/login.rs b/src/minecraft/client/login.rs new file mode 100644 index 0000000..fd86cb2 --- /dev/null +++ b/src/minecraft/client/login.rs @@ -0,0 +1,26 @@ +use crate::minecraft::client::client::Client; + +use crate::minecraft::varint::varint_write; +use std::collections::VecDeque; +use tokio::net::TcpStream; + +impl Client { + pub async fn login_intent_handle(&mut self, mut _packet: &mut VecDeque, _packet_id: i32) -> Result<(),String> { + let stream = TcpStream::connect("play.hypixel.net:25565").await.map_err(|_| "failed to connect to remote host".to_string())?; + let mut new_handshake = self.handshake.as_ref().unwrap().clone(); + new_handshake.server_address = "play.hypixel.net".to_string(); + new_handshake.server_port = 25565; + + let handshake_packet = new_handshake.to_packet(); + let mut packet = Vec::new(); + packet.extend(varint_write(handshake_packet.len() as i32)); + packet.extend(handshake_packet); + + self.out_stream = Some(stream); + let _ = self.out_stream.as_ref().unwrap().writable().await; + + let _ = self.out_stream.as_ref().unwrap().try_write(packet.as_slice()); + println!("[{}] sent handshake to server", self); + Ok(()) + } +} diff --git a/src/minecraft/client/mod.rs b/src/minecraft/client/mod.rs new file mode 100644 index 0000000..c1a1572 --- /dev/null +++ b/src/minecraft/client/mod.rs @@ -0,0 +1,4 @@ +pub mod client; +mod buffer; +mod status; +mod login; diff --git a/src/minecraft/client/status.rs b/src/minecraft/client/status.rs new file mode 100644 index 0000000..3551feb --- /dev/null +++ b/src/minecraft/client/status.rs @@ -0,0 +1,42 @@ +use crate::minecraft::client::client::Client; + +use json::object; + +use crate::minecraft::varint::varint_write; +use std::collections::VecDeque; + +impl Client { + pub async fn status_intent_handle(&self, packet: &mut VecDeque, packet_id: i32) -> Result<(),String> { + let status_response = object! { + "version": { + "name": "idk", + "protocol" : self.handshake.as_ref().unwrap().protocol_version, + }, + "players": { + "max": 420, + "online": 69 + }, + "description": "§c".to_string() + &self.handshake.as_ref().unwrap().server_address.clone() + "§r:§a" + &self.handshake.as_ref().unwrap().server_port.to_string(), + }; + if packet_id == 0 { + let mut response : Vec = vec![]; + + let response_json = json::stringify(status_response); + let mut response_json_len = varint_write(response_json.len() as i32); + let mut response_packet_id = varint_write(0); + + response.append(&mut response_packet_id); + response.append(&mut response_json_len); + response.append(&mut response_json.as_bytes().into()); + println!("[{}] sent status response to client", self); + + self.send_packet(response).await; + } else if packet_id == 1 { + let mut response = varint_write(1); + response.extend(packet.drain(..)); + self.send_packet(response).await; + println!("[{}] sent ping response to client", self); + } + Ok(()) + } +} diff --git a/src/minecraft/mod.rs b/src/minecraft/mod.rs index 990b33c..e0ffa9a 100644 --- a/src/minecraft/mod.rs +++ b/src/minecraft/mod.rs @@ -1,6 +1,6 @@ pub mod varint; pub mod handshake; -pub mod client; +mod client; mod socket; diff --git a/src/minecraft/socket.rs b/src/minecraft/socket.rs index 68d2f16..e0d7ffb 100644 --- a/src/minecraft/socket.rs +++ b/src/minecraft/socket.rs @@ -1,10 +1,12 @@ -use crate::minecraft::client::Client; +use crate::minecraft::client::client::Client; use tokio::net::TcpStream; -pub async fn process_mc_socket(stream: TcpStream) -> Result<(), String> { +use crate::DbPool; + +pub async fn process_mc_socket(stream: TcpStream, pool: DbPool) -> Result<(), String> { println!("new client {:?}", stream); - let mut client = Client::create(stream); + let mut client = Client::create(stream, pool); loop { tokio::select! { diff --git a/src/models.rs b/src/models.rs index 6e48eb7..a66fb5f 100644 --- a/src/models.rs +++ b/src/models.rs @@ -11,12 +11,12 @@ use crate::status::ServerStatus; #[diesel(table_name = dockermcmgr::schema::servers)] #[diesel(check_for_backend(diesel::pg::Pg))] pub struct Servers { - pub id: i32, + pub id: i64, pub name: String, pub volume_path: String, pub last_login: Option, pub container_id: Option, - pub status: i16, + pub status: ServerStatus, pub redirect_ip: Option } diff --git a/src/rpc/socket.rs b/src/rpc/socket.rs index 9cb6a21..24e22a9 100644 --- a/src/rpc/socket.rs +++ b/src/rpc/socket.rs @@ -1,6 +1,8 @@ use std::io; use tokio::net::TcpStream; +use crate::DbPool; + /* * request_format : @@ -16,6 +18,6 @@ use tokio::net::TcpStream; * - data: json object containing the response data */ -pub async fn process_rpc_socket(_stream: TcpStream) -> io::Result<()> { +pub async fn process_rpc_socket(_stream: TcpStream, _pool: DbPool) -> io::Result<()> { panic!("not implemented"); } diff --git a/src/schema.rs b/src/schema.rs index 7fdaf2e..e24f103 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -2,7 +2,7 @@ diesel::table! { servers (id) { - id -> Int4, + id -> Int8, #[max_length = 255] name -> Varchar, #[max_length = 255] diff --git a/src/status.rs b/src/status.rs index 9fbc23b..642add3 100644 --- a/src/status.rs +++ b/src/status.rs @@ -1,39 +1,37 @@ -use serde::{Deserialize, Serialize}; +use diesel::deserialize::{self, FromSql, FromSqlRow}; +use diesel::pg::PgValue; +use diesel::expression::AsExpression; +use diesel::pg::Pg; +use diesel::serialize::{self, ToSql, Output}; +use diesel::sql_types::SmallInt; +use std::io::Write; -#[derive(Clone, Copy, PartialEq, Eq, Serialize, Deserialize, diesel::expression::AsExpression, diesel::deserialize::FromSqlRow)] -#[diesel(sql_type = diesel::sql_types::SmallInt)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, AsExpression, FromSqlRow, serde::Serialize, serde::Deserialize)] +#[diesel(sql_type = SmallInt)] pub enum ServerStatus { - Archived, - Stopped, - Stopping, - Starting, - Running, - Unknown, + Archived = 0, + Stopped = 1, + Stopping = 2, + Starting = 3, + Running = 4, + Unknown = 5, } -impl diesel::serialize::ToSql for ServerStatus -where - ::RawValue: for<'a> diesel::serialize::ToSql> + ?Sized, -{ - fn to_sql<'b>(&'b self, out: &mut diesel::serialize::Output<'b, '_, DB>) -> diesel::serialize::Result { - let val = match self { - ServerStatus::Archived => 0i16, - ServerStatus::Stopped => 1i16, - ServerStatus::Stopping => 2i16, - ServerStatus::Starting => 3i16, - ServerStatus::Running => 4i16, - ServerStatus::Unknown => -1i16, - }; - diesel::serialize::ToSql::::to_sql(&val, out) +impl ToSql for ServerStatus { + fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + let val = *self as i16; + out.write_all(&val.to_be_bytes())?; + Ok(serialize::IsNull::No) } } -impl diesel::deserialize::FromSql for ServerStatus -where - ::RawValue: for<'a> diesel::deserialize::FromSql<'a, diesel::sql_types::SmallInt, DB> + ?Sized, -{ - fn from_sql(bytes: ::RawValue<'_>) -> diesel::deserialize::Result { - let val = i16::from_sql(bytes)?; +impl FromSql for ServerStatus { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let bytes = value.as_bytes(); + if bytes.len() != 2 { + return Err("Invalid length for i16".into()); + } + let val = i16::from_be_bytes([bytes[0], bytes[1]]); match val { 0 => Ok(ServerStatus::Archived), 1 => Ok(ServerStatus::Stopped),