add problem 05
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
use anyhow::bail;
|
||||
use tokio::{
|
||||
io::{AsyncReadExt, AsyncWriteExt, Result},
|
||||
net::TcpStream,
|
||||
};
|
||||
|
||||
const TONYS_ADDRESS: &str = "7YWHMfk9JZe0LM0g1ZauHuiSxhI";
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
let listener = tokio::net::TcpListener::bind("0.0.0.0:10000").await?;
|
||||
|
||||
loop {
|
||||
let (socket, _) = listener.accept().await?;
|
||||
tokio::spawn(async move {
|
||||
match handle_connection(socket).await {
|
||||
Ok(_) => (),
|
||||
Err(err) => {
|
||||
dbg!(err);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_connection(socket: TcpStream) -> anyhow::Result<()> {
|
||||
let (mut downstream_reader, mut downstream_writer) = tokio::io::split(socket);
|
||||
|
||||
let upstream = tokio::net::TcpStream::connect("chat.protohackers.com:16963").await?;
|
||||
let (mut upstream_reader, mut upstream_writer) = tokio::io::split(upstream);
|
||||
|
||||
let mut downstream_read_buffer = Vec::with_capacity(1024);
|
||||
let mut upstream_read_buffer = Vec::with_capacity(1024);
|
||||
|
||||
loop {
|
||||
tokio::select! {
|
||||
read_amount = read_to_buffer(&mut upstream_reader, &mut upstream_read_buffer) => {
|
||||
match read_amount {
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let message = match handle_message(&mut upstream_read_buffer) {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
downstream_writer.write_all(&message.into_bytes()).await?;
|
||||
}
|
||||
// receive from downstream, send to upstream
|
||||
read_amount = read_to_buffer(&mut downstream_reader, &mut downstream_read_buffer) => {
|
||||
match read_amount {
|
||||
Ok(_) => (),
|
||||
Err(_) => {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
let message = match handle_message(&mut downstream_read_buffer) {
|
||||
Ok(s) => s,
|
||||
Err(_) => continue,
|
||||
};
|
||||
|
||||
upstream_writer.write_all(&message.into_bytes()).await?;
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn find_next_newline(read_buffer: &[u8]) -> Option<usize> {
|
||||
read_buffer.iter().position(|c| *c == b'\n')
|
||||
}
|
||||
|
||||
async fn read_to_buffer(
|
||||
reader: &mut tokio::io::ReadHalf<TcpStream>,
|
||||
read_buffer: &mut Vec<u8>,
|
||||
) -> anyhow::Result<usize> {
|
||||
let mut read_amount = 0;
|
||||
|
||||
while !read_buffer.contains(&b'\n') {
|
||||
read_amount += reader.read_buf(read_buffer).await?;
|
||||
if read_amount == 0 {
|
||||
bail!("user disconnected");
|
||||
}
|
||||
}
|
||||
Ok(read_amount)
|
||||
}
|
||||
|
||||
fn replace_boguscoin_addresses(s: String) -> String {
|
||||
let trimmed = s.trim_end_matches('\n');
|
||||
let mut result = String::new();
|
||||
|
||||
let mut words = trimmed.split(' ').peekable();
|
||||
|
||||
while let Some(word) = words.next() {
|
||||
if is_boguscoin_addres(word) {
|
||||
result.push_str(TONYS_ADDRESS);
|
||||
} else {
|
||||
result.push_str(word);
|
||||
}
|
||||
if words.peek().is_some() {
|
||||
result.push(' ');
|
||||
}
|
||||
}
|
||||
|
||||
result.push('\n');
|
||||
result
|
||||
}
|
||||
|
||||
fn is_boguscoin_addres(s: &str) -> bool {
|
||||
s.starts_with('7')
|
||||
&& (26..=35).contains(&s.len())
|
||||
&& s.chars().all(|c| c.is_ascii_alphanumeric())
|
||||
}
|
||||
|
||||
fn handle_message(read_buffer: &mut Vec<u8>) -> anyhow::Result<String> {
|
||||
let newline = match find_next_newline(read_buffer) {
|
||||
Some(idx) => idx,
|
||||
None => bail!("continue"),
|
||||
};
|
||||
let before_newline = read_buffer.drain(0..=newline).collect();
|
||||
|
||||
let message = String::from_utf8(before_newline)?;
|
||||
let message = replace_boguscoin_addresses(message);
|
||||
|
||||
Ok(message)
|
||||
}
|
||||
Reference in New Issue
Block a user