From ec73c542760b8c7d36d4055774ab3cf74bf2d66c Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Tue, 15 Sep 2026 23:41:39 +0300 Subject: [PATCH] Server code --- Cargo.lock | 7 ++ Cargo.toml | 3 +- src/main.rs | 46 +++++++++++-- src/server/handling.rs | 55 ++++++++++++++++ src/server/mod.rs | 144 ++++++++++++++++++++++++++++++----------- src/server/testing.rs | 5 ++ 6 files changed, 213 insertions(+), 47 deletions(-) create mode 100644 src/server/handling.rs create mode 100644 src/server/testing.rs diff --git a/Cargo.lock b/Cargo.lock index f3028b6..12275b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,6 +170,12 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "litemap" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" + [[package]] name = "log" version = "0.4.34" @@ -261,6 +267,7 @@ name = "remote_marks" version = "0.1.0" dependencies = [ "env_logger", + "litemap", "log", "minicbor", ] diff --git a/Cargo.toml b/Cargo.toml index 51d4f58..b62e4bd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,5 @@ edition = "2024" [dependencies] env_logger = "0.11.11" log = "0.4.34" -minicbor = "2.3.0" \ No newline at end of file +minicbor = "2.3.0" +litemap = "0.8.3" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d7d63a4..b2d77ec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,47 @@ mod server; -use std::any::Any; -use std::collections::HashMap; +use crate::server::{Command, NoteContext}; +use std::io; +use std::io::Read; use std::net::UdpSocket; +use std::sync::mpsc::*; const LISTEN: &str = "127.0.0.1:8080"; - - fn main() { - let listener = UdpSocket::bind(LISTEN).unwrap(); - let mut buffer = [0; 1024]; - while let Ok((inbound, outbound)) = listener.recv_from(&mut buffer) {} + let (cw, cr) = channel::(); + + std::thread::spawn(move || { + let mut context = NoteContext::default(); + while let Ok(message) = cr.recv() { + context.execute_command(message); + } + }); + + let network_cw = cw.clone(); + std::thread::spawn(move || { + let listener = UdpSocket::bind(LISTEN).unwrap(); + let mut buffer = [0; 1024]; + while let Ok((inbound, _outbound)) = listener.recv_from(&mut buffer) { + let command= Command::from(&buffer[..inbound]); + network_cw.send(command).unwrap(); + } + }); + + handle_console_input(cw); +} + +fn handle_console_input(command_channel: Sender) { + let mut key = String::new(); + loop { + io::stdin().read_to_string(&mut key).unwrap(); + if key == "exit" { + break; + } + if key.is_empty(){ + continue; + } + + command_channel.send(Command::Display(key.clone())).unwrap(); + } } diff --git a/src/server/handling.rs b/src/server/handling.rs new file mode 100644 index 0000000..fefe7b3 --- /dev/null +++ b/src/server/handling.rs @@ -0,0 +1,55 @@ +use crate::server::StoredData::*; +use crate::server::{Command, NoteContext, StoredData}; +use litemap::LiteMap; +use std::io::Write; +use std::string::String; + +impl NoteContext { + pub fn execute_command(&mut self, command: Command) { + match command { + Command::Unknown => {} + Command::Set(key, value) => self.set(key, value), + Command::Define(key, schema) => self.define(key, schema), + Command::Push(key, value) => self.push(key, value), + Command::Display(key) => self.display(key), + Command::SetOutput(generator) => self.output = generator(), + } + } + + fn set(&mut self, key: String, value: StoredData) { + if key.is_empty() { + return; + } + self.entries.insert(key, value); + } + + fn push(&mut self, key: String, value: StoredData) { + let mut vectorize: Option = None; + let current = self.entries.get_mut(&key); + if let Some(current) = current { + if let Vec(vec) = current { + vec.push(value); + } else { + vectorize = Some(Vec(vec![current.clone()])); + } + } else { + self.set(key, value); + return; + } + + if let Some(vec) = vectorize { + self.set(key, vec); + } + } + + fn define(&mut self, _key: String, _value: LiteMap) { + todo!() + } + + fn display(&mut self, key: String) { + if let Some(data) = self.entries.get(&key) { + writeln!(self.output, "{}", key).unwrap(); + _ = self.output.write_all(data.to_string().as_bytes()); + } + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs index ff62095..c3bafff 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,29 +1,73 @@ -use minicbor::decode::Error; +mod handling; +mod testing; + +use litemap::LiteMap; use minicbor::Decoder; -use std::any::Any; -use std::collections::HashMap; -use std::fmt::{Debug, Display, Formatter}; use minicbor::data::Type; +use minicbor::decode::Error; +use std::collections::HashMap; +use std::fmt::{Display, Formatter }; +use std::io::Write; -enum Command { +pub enum Command { Unknown, - Set(String, Box), - Push(String, Box), + Set(String, StoredData), + Define(String, LiteMap), + Push(String, StoredData), + Display(String), + SetOutput(fn() -> Box), } -struct Line{ - data: Vec>, +#[derive(Clone)] +pub enum StoredData { + Integer(isize), + Float(f64), + String(String), + Bool(bool), + Line(Line), + Vec(Vec), } -impl From>> for Line { - fn from(value: Vec>) -> Self { - Self{data: value} +impl Display for StoredData { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + StoredData::Integer(v) => v.fmt(f), + StoredData::Float(v) => v.fmt(f), + StoredData::String(v) => v.fmt(f), + StoredData::Bool(v) => v.fmt(f), + StoredData::Line(v) => v.fmt(f), + StoredData::Vec(vec) => { + vec.iter().for_each(|d| { + _ = write!(f, "{}\n", d); + }); + Ok(()) + } + } + } +} + +#[derive(Clone)] +pub struct Line { + data: Vec, +} + +impl From> for Line { + fn from(value: Vec) -> Self { + Self { data: value } } } impl Display for Line { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "[{}]", self.data.iter().map(|i| i.to_string()).collect::>().join(", ")) + write!( + f, + "[{}]", + self.data + .iter() + .map(|i| i.to_string()) + .collect::>() + .join(", ") + ) } } @@ -40,7 +84,7 @@ fn parse_command(data: &[u8]) -> Result { decoder.array()?; let id = decoder.str()?.to_string(); let value = decode_value(&mut decoder)?; - return Ok(Command::Set(id, value)) + return Ok(Command::Set(id, value)); } if data.starts_with(b"PUSH:") { let payload = &data[b"PUSH:".len()..]; @@ -48,32 +92,41 @@ fn parse_command(data: &[u8]) -> Result { decoder.array()?; let id = decoder.str()?.to_string(); let value = decode_value(&mut decoder)?; - return Ok(Command::Push(id, value)) + return Ok(Command::Push(id, value)); + } + if data.starts_with(b"DISPLAY:") { + let payload = &data[b"DISPLAY:".len()..]; + let mut decoder = Decoder::new(&payload); + decoder.array()?; + let id = decoder.str()?.to_string(); + return Ok(Command::Display(id)); } Ok(Command::Unknown) } -fn decode_value(decoder: &mut Decoder) -> Result, Error> { +fn decode_value(decoder: &mut Decoder) -> Result { Ok(match decoder.datatype()? { - Type::Bool => Box::new(decoder.bool()?), - Type::U8 => Box::new(decoder.u8()? as isize), - Type::U16 => Box::new(decoder.u16()? as isize), - Type::U32 => Box::new(decoder.u32()? as isize), - Type::U64 => Box::new(decoder.u64()? as isize), - Type::I8 => Box::new(decoder.i8()? as isize), - Type::I16 => Box::new(decoder.i16()? as isize), - Type::I32 => Box::new(decoder.i32()? as isize), - Type::I64 => Box::new(decoder.i64()? as isize), - Type::Int => Box::new(decoder.i32()? as isize), - Type::F16 => Box::new(decoder.f32()? as f64), - Type::F32 => Box::new(decoder.f32()? as f64), - Type::F64 => Box::new(decoder.f64()?), - Type::String | - Type::StringIndef => Box::new(decoder.str()?.to_string()), - Type::Array | - Type::ArrayIndef => { + Type::Bool => StoredData::Bool(decoder.bool()?), + Type::U8 => StoredData::Integer(decoder.u8()? as isize), + Type::U16 => StoredData::Integer(decoder.u16()? as isize), + Type::U32 => StoredData::Integer(decoder.u32()? as isize), + Type::U64 => StoredData::Integer(decoder.u64()? as isize), + Type::I8 => StoredData::Integer(decoder.i8()? as isize), + Type::I16 => StoredData::Integer(decoder.i16()? as isize), + Type::I32 => StoredData::Integer(decoder.i32()? as isize), + Type::I64 => StoredData::Integer(decoder.i64()? as isize), + Type::Int => StoredData::Integer(decoder.i32()? as isize), + Type::F16 => StoredData::Float(decoder.f32()? as f64), + Type::F32 => StoredData::Float(decoder.f32()? as f64), + Type::F64 => StoredData::Float(decoder.f64()?), + Type::String | Type::StringIndef => StoredData::String(decoder.str()?.to_string()), + Type::Array | Type::ArrayIndef => { let len = decoder.array()?; - let mut array = if let Some(len) = len { Vec::with_capacity(len as usize) } else { vec![] }; + let mut array = if let Some(len) = len { + Vec::with_capacity(len as usize) + } else { + vec![] + }; if array.is_empty() { loop { let dt = decoder.datatype()?; @@ -83,12 +136,25 @@ fn decode_value(decoder: &mut Decoder) -> Result, Error> { array.push(decode_value(decoder)?); } } - Box::new(Line::from(array)) + StoredData::Line(Line::from(array)) + } + _ => { + decoder.skip()?; + StoredData::Bool(false) } - _ => { decoder.skip()?; Box::new(false)} }) } -struct NoteContext { - entries: HashMap>, -} \ No newline at end of file +pub struct NoteContext { + entries: HashMap, + output: Box, +} + +impl NoteContext { + pub fn default() -> Self { + Self{ + entries: Default::default(), + output: Box::new(std::io::stdout()), + } + } +} diff --git a/src/server/testing.rs b/src/server/testing.rs new file mode 100644 index 0000000..df27733 --- /dev/null +++ b/src/server/testing.rs @@ -0,0 +1,5 @@ +#[cfg(test)] +mod test { + #[test] + fn x() {} +}