1
0
Fork 0

Initial import

This commit is contained in:
Daniele Tricoli 2019-05-06 01:38:59 +02:00
commit c227d67ebb
7 changed files with 171 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

6
Cargo.lock generated Normal file
View File

@ -0,0 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "dotted"
version = "0.1.0"

7
Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "dotted"
version = "0.1.0"
authors = ["Daniele Tricoli <eriol@mornie.org>"]
edition = "2018"
[dependencies]

1
rustfmt.toml Normal file
View File

@ -0,0 +1 @@
max_width = 80

15
src/main.rs Normal file
View File

@ -0,0 +1,15 @@
mod network;
use std::sync::{Arc, RwLock};
use network::SimpleUDPServer;
fn main() {
let shared_state = Arc::new(RwLock::new(String::new()));
let s = shared_state.clone();
let mut server = SimpleUDPServer::new("127.0.0.1:1234", s);
if let Err(e) = server.serve_forever() {
println!("{}", e);
};
}

79
src/network.rs Normal file
View File

@ -0,0 +1,79 @@
mod errors;
use std::io;
use std::net::UdpSocket;
use std::result;
use std::sync::{Arc, RwLock};
use errors::UDPServerError;
// Maximum buffer size for commands.
//
// Traditionally (so excluding neologisms) the longest word in Italian is
// precipitevolissimevolmente, made by 26 letters. For our use case is more
// than enough.
const MAX_BUF_SIZE: usize = 26;
pub type Result<'a, T> = result::Result<T, UDPServerError<'a>>;
// A simple server listening on UDP.
//
// The server receives commands as String, with a max len of MAX_BUF_SIZE.
// Commands that excede MAX_BUF_SIZE are simply truncated.
pub struct SimpleUDPServer<'a> {
address: &'a str,
command: Arc<RwLock<String>>,
socket: Option<io::Result<UdpSocket>>,
}
impl<'a> SimpleUDPServer<'a> {
/// Create a new server.
pub fn new(address: &'a str, command: Arc<RwLock<String>>) -> Self {
SimpleUDPServer {
address,
command,
socket: None,
}
}
fn bind(&mut self) {
self.socket = Some(UdpSocket::bind(&self.address));
}
fn recv_command(&self) -> Result<()> {
let mut buf = [0; MAX_BUF_SIZE];
match self.socket.as_ref() {
Some(socket) => match socket {
Ok(socket) => {
let (bytes, src_addr) = socket.recv_from(&mut buf)?;
let mut command = self.command.write()?;
*command = String::from_utf8(buf[..bytes].to_vec())?;
println!("{:?}", src_addr);
println!("In recv udp {:?}", *command);
Ok(())
}
// io::Error doesn't support Clone trait so we create a new one.
Err(e) => Err(UDPServerError::from(io::Error::new(
e.kind(),
format!(
"Error on binding to {}: {}",
&self.address,
e.to_string()
),
))),
},
None => panic!("SimpleUDPServer::bind must be called!"),
}
}
//
pub fn serve_forever(&mut self) -> Result<()> {
self.bind();
loop {
self.recv_command()?
}
}
}

61
src/network/errors.rs Normal file
View File

@ -0,0 +1,61 @@
use std::fmt;
use std::io;
use std::string;
use std::sync;
// Aggregate all kind of different errors for SimpleUDPServer.
#[derive(Debug)]
pub struct UDPServerError<'a> {
detail: ErrorDetail<'a>,
}
impl fmt::Display for UDPServerError<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.detail)
}
}
impl<'a> From<io::Error> for UDPServerError<'a> {
fn from(err: io::Error) -> UDPServerError<'a> {
UDPServerError {
detail: ErrorDetail::IOError(err),
}
}
}
impl<'a> From<sync::PoisonError<sync::RwLockWriteGuard<'a, String>>>
for UDPServerError<'a>
{
fn from(
err: sync::PoisonError<sync::RwLockWriteGuard<'a, String>>,
) -> UDPServerError {
UDPServerError {
detail: ErrorDetail::PoisonError(err),
}
}
}
impl<'a> From<string::FromUtf8Error> for UDPServerError<'a> {
fn from(err: string::FromUtf8Error) -> UDPServerError<'a> {
UDPServerError {
detail: ErrorDetail::FromUtf8Error(err),
}
}
}
#[derive(Debug)]
enum ErrorDetail<'a> {
IOError(io::Error),
PoisonError(sync::PoisonError<sync::RwLockWriteGuard<'a, String>>),
FromUtf8Error(string::FromUtf8Error),
}
impl fmt::Display for ErrorDetail<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ErrorDetail::IOError(err) => write!(f, "{}", err.to_string()),
ErrorDetail::PoisonError(err) => write!(f, "{}", err.to_string()),
ErrorDetail::FromUtf8Error(err) => write!(f, "{}", err.to_string()),
}
}
}