simplified Thread representation

doctorpavel
Dawid J. Kubis 3 years ago
parent 31a8bf78cb
commit d8761eee20

155
Cargo.lock generated

@ -11,6 +11,25 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "async-mutex"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e"
dependencies = [
"event-listener",
]
[[package]]
name = "async-rwlock"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "261803dcc39ba9e72760ba6e16d0199b1eef9fc44e81bffabbebb9f5aea3906c"
dependencies = [
"async-mutex",
"event-listener",
]
[[package]] [[package]]
name = "atty" name = "atty"
version = "0.2.14" version = "0.2.14"
@ -49,6 +68,40 @@ version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "cached"
version = "0.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f66c8f6ae13abbdc12268879b8070a441777b69157fb7653b4c7da7f2610c25d"
dependencies = [
"async-mutex",
"async-rwlock",
"cached_proc_macro",
"cached_proc_macro_types",
"hashbrown",
"once_cell",
"thiserror",
"tokio",
]
[[package]]
name = "cached_proc_macro"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc8de7b947777686ee8f3c11f4c3e58c296d6bc598d9b2286a80a9404a538ff8"
dependencies = [
"cached_proc_macro_types",
"darling",
"quote",
"syn",
]
[[package]]
name = "cached_proc_macro_types"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -64,7 +117,7 @@ dependencies = [
"ansi_term", "ansi_term",
"atty", "atty",
"bitflags", "bitflags",
"strsim", "strsim 0.8.0",
"textwrap", "textwrap",
"unicode-width", "unicode-width",
"vec_map", "vec_map",
@ -102,6 +155,53 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "darling"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0d720b8683f8dd83c65155f0530560cba68cd2bf395f6513a483caee57ff7f4"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a340f241d2ceed1deb47ae36c4144b2707ec7dd0b649f894cb39bb595986324"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim 0.10.0",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c41b3b7352feb3211a0d743dc5700a4e3b60f51bd2b368892d1e0f9a95f44b"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "event-listener"
version = "2.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77f3309417938f28bf8228fcff79a4a37103981e3e186d2ccd19c74b38f4eb71"
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]] [[package]]
name = "fs2" name = "fs2"
version = "0.4.3" version = "0.4.3"
@ -132,6 +232,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "hashbrown"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c21d40587b92fa6a6c6e3c1bdbf87d75511db5672f9c93175574b3a00df1758"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.3.3" version = "0.3.3"
@ -150,6 +256,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "instant" name = "instant"
version = "0.1.12" version = "0.1.12"
@ -164,6 +276,7 @@ name = "kchan"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"cached",
"lazy_static", "lazy_static",
"num_cpus", "num_cpus",
"rand", "rand",
@ -223,6 +336,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "once_cell"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]] [[package]]
name = "parking_lot" name = "parking_lot"
version = "0.11.2" version = "0.11.2"
@ -248,6 +367,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "pin-project-lite"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c"
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.15" version = "0.2.15"
@ -399,6 +524,12 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "structopt" name = "structopt"
version = "0.3.25" version = "0.3.25"
@ -472,6 +603,28 @@ dependencies = [
"num_cpus", "num_cpus",
] ]
[[package]]
name = "tokio"
version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af73ac49756f3f7c01172e34a23e5d0216f6c32333757c2c61feb2bbff5a5ee"
dependencies = [
"num_cpus",
"pin-project-lite",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "unicode-segmentation" name = "unicode-segmentation"
version = "1.8.0" version = "1.8.0"

@ -15,3 +15,4 @@ lazy_static = "1.4.0"
sled = "0.34.7" sled = "0.34.7"
bincode = "1.3.3" bincode = "1.3.3"
serde = { version = "1.0.136", features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] }
cached = "0.33.0"

@ -1,9 +1,8 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::{FromStr, Lines};
const HTTP_VERSION: &str = "HTTP/1.1"; const HTTP_VERSION: &'static str = "HTTP/1.1";
// TODO use &str
#[derive(Debug)] #[derive(Debug)]
pub struct Request { pub struct Request {
pub method: String, pub method: String,
@ -26,7 +25,7 @@ pub enum Status {
} }
impl Status { impl Status {
pub fn get_message(&self) -> &'static str { pub fn message(&self) -> &'static str {
match self { match self {
Self::Ok => "OK", Self::Ok => "OK",
Self::NotFound => "NOT FOUND", Self::NotFound => "NOT FOUND",
@ -34,16 +33,15 @@ impl Status {
} }
} }
// TODO implement this with display and enums
// TODO proper errors // TODO proper errors
impl FromStr for Request { impl FromStr for Request {
type Err = String; type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> { fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut iter = dbg!(s).lines(); let mut iter = s.lines();
let mut first = iter.nth(0).unwrap().split_whitespace(); let mut first = iter.next().unwrap().split_whitespace();
let method = first.next().unwrap().to_string(); let method = first.next().unwrap();
let uri = first.next().unwrap().to_string(); let uri = first.next().unwrap();
let mut headers: Vec<(String, String)> = vec![]; let mut headers: Vec<(String, String)> = vec![];
for line in &mut iter { for line in &mut iter {
@ -56,10 +54,10 @@ impl FromStr for Request {
let body: String = iter.fold(String::new(), |a, b| format!("{}{}", a, b)); let body: String = iter.fold(String::new(), |a, b| format!("{}{}", a, b));
Ok(Self { Ok(Self {
method, method: method.to_string(),
uri, uri: uri.to_string(),
headers, headers: headers,
body, body: body.to_string(),
}) })
} }
} }
@ -87,7 +85,7 @@ impl fmt::Display for Response {
"{} {} {}\n{}\n\n{}", "{} {} {}\n{}\n\n{}",
HTTP_VERSION, HTTP_VERSION,
self.status as i32, self.status as i32,
self.status.get_message(), self.status.message(),
headers, headers,
self.body, self.body,
) )

@ -5,20 +5,23 @@ use crate::http::{Request, Response, Status};
use crate::post::{Post, Thread}; use crate::post::{Post, Thread};
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream}; use std::net::{TcpListener, TcpStream, SocketAddr};
use std::path::PathBuf; use std::path::PathBuf;
use std::str; use std::str;
use std::str::from_utf8; use std::str::from_utf8;
use std::sync::{Arc, Mutex, MutexGuard}; use std::sync::{Arc, Mutex, MutexGuard};
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use cached::proc_macro::cached;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use structopt::StructOpt; use structopt::StructOpt;
use threadpool::ThreadPool; use threadpool::ThreadPool;
// statically linked index and favicon
const INDEX: &'static str = include_str!("www/index.html"); const INDEX: &'static str = include_str!("www/index.html");
const FAVICON: &'static [u8] = include_bytes!("www/favicon.ico"); const FAVICON: &'static [u8] = include_bytes!("www/favicon.ico");
// represents command line arguments
#[derive(StructOpt, Debug)] #[derive(StructOpt, Debug)]
struct Opt { struct Opt {
#[structopt(short, long, default_value = "8000")] #[structopt(short, long, default_value = "8000")]
@ -41,43 +44,80 @@ lazy_static! {
static ref OPT: Opt = Opt::from_args(); static ref OPT: Opt = Opt::from_args();
} }
// TODO cache /// top level handling of requests
//#[derive(Debug)] fn handle(request: Request, database: Arc<Mutex<sled::Db>>, ip: SocketAddr) -> Response {
//struct Cache<'a>(&'a str);
fn handle(request: Request, database: Arc<Mutex<sled::Db>>) -> String {
let database = database.lock().unwrap(); let database = database.lock().unwrap();
match request.method.as_str() { match request.method.as_str() {
"GET" => get(&request.uri, database), "GET" => get(&request.uri, database),
"POST" => "HTTP/1.1 200 OK".to_string(), "POST" => {
println!("{:?}", request);
Response::new(Status::Ok, vec![], "".to_string())
},
// check admin hash // check admin hash
"DELETE" => "HTTP/1.1 200 OK".to_string(), "DELETE" => Response::new(Status::Ok, vec![], "".to_string()),
_ => "".to_string(), _ => Response::new(Status::Ok, vec![], "".to_string()),
} }
} }
fn get(path: &str, database: MutexGuard<sled::Db>) -> String { /// get "/" returns head of every thread
/// or returns posts from thread with id in uri
fn get(path: &str, database: MutexGuard<sled::Db>) -> Response {
match path { match path {
// list threads in this case // list threads in this case
"/" => { "/" => {
let content = INDEX.to_string().replace( let content = content(
"{}", "index",
&*database &database
.iter() .iter()
.map(|x| x.unwrap()) .map(|x| x.unwrap())
.map(|x| (x.0, deserialize::<Thread>(&x.1).unwrap())) .map(|x| (x.0, deserialize::<Thread>(&x.1).unwrap()))
.map(|x| x.1.head(from_utf8(&x.0).unwrap())) .map(|x| x.1.head())
.fold(String::from(""), |a, b| format!("{}{}", a, b)), .fold(String::from(""), |a, b| format!("{}{}", a, b)),
); );
Response::new(Status::Ok, vec![], content).to_string() Response::new(Status::Ok, vec![], content)
} }
// list specific thread here // list specific thread here
s => "".to_string(), // FIXME unwrap hell
s => {
let content = content(
s,
&deserialize::<Thread>(&database.get(&s.as_bytes()).unwrap().unwrap())
.unwrap()
.to_string()
);
Response::new(Status::Ok, vec![], content)
},
}
}
fn post(path: &str, database: MutexGuard<sled::Db>) -> Response {
match path {
// means we wish to create a new thread
"/" => {
// first we generate unique id
},
// means we wish to post in specific thread
s => {
},
} }
} }
fn delete(path: &str, database: Arc<Mutex<sled::Db>>) -> Response {
todo!();
}
fn content(name: &str, main: &str) -> String {
INDEX
.replace("{name}", &name)
.replace("{}", &main)
}
fn main() { fn main() {
// TODO do this without unwrap // TODO do this without unwrap
// bind listener to local adress and port // bind listener to local adress and port
@ -94,7 +134,7 @@ fn main() {
let mut stream = stream.unwrap(); let mut stream = stream.unwrap();
let database = Arc::clone(&db); let database = Arc::clone(&db);
pool.execute(move || { pool.execute(move || {
let ip = stream.peer_addr(); let ip = stream.peer_addr().unwrap();
// TODO check if valid ip before reading request // TODO check if valid ip before reading request
let mut buffer = [0; 1 << 10]; // 2 to the power of 10 let mut buffer = [0; 1 << 10]; // 2 to the power of 10
// how do I implement images with this? // how do I implement images with this?
@ -107,7 +147,7 @@ fn main() {
// handle request // handle request
// add database and cache here // add database and cache here
stream.write(handle(request, database).as_bytes()).unwrap(); stream.write(handle(request, database, ip).to_string().as_bytes()).unwrap();
}); });
} }
} }

@ -2,47 +2,83 @@ use std::fmt;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Image {
pub filename: String,
pub img: Box<[u8]>,
}
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Post { pub struct Post {
pub id: u64, pub id: u64,
pub ip: [u8; 4], pub ip: [u8; 4],
pub date: String, pub date: String,
pub img: Option<Image>,
pub body: String, pub body: String,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread(Vec<Post>);
impl Thread {
pub fn head(&self) -> String {
let first = self.0[0];
format!(
"<article><div><span class=\"info\">{}</span></div>{}</article>",
first.id,
first.to_string(),
)
}
}
impl Post {
fn new(id: u64, ip: [u8; 4], date: String, img: Option<Image>, body: String) -> Self {
Self {
id,
ip,
date,
img,
body,
}
}
}
impl fmt::Display for Post { impl fmt::Display for Post {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"<article><div><span class=\"info\">{} {}</span></div><p>{}</p></article>", "<article>\
self.id, self.date, self.body, <img src=\"{}\" alt=\"img\">\
<div>\
<div class=\"meta\">\
<div>\
{}&nbsp;\
<strong>{}</strong>&nbsp;\
</div>\
<nav>\
<a href=\"#\">open</a>\
<a href=\"#\">quote</a>\
<a href=\"#\">reply</a>\
</nav>\
</div>\
<div class=\"image-info\">{}</div>\
{}\
</div>\
</article>",
self.id, self.date, self.id,
if let Some(s) = self.img {s.filename} else {"".to_string()},
self.body,
) )
} }
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread {
pub topic: String,
pub posts: Vec<Post>,
}
impl fmt::Display for Thread { impl fmt::Display for Thread {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let posts: String = self let posts: String = self
.posts .0
.iter() .iter()
.fold(String::from(""), |a, b| format!("{}{}", a, b)); .fold(String::from(""), |a, b| format!("{}{}", a, b));
write!(f, "{}", posts) write!(f, "{}", posts)
} }
} }
impl Thread {
pub fn head(&self, id: &str) -> String {
format!(
"<article><div><span class=\"info\">{} {}</span></div>{}</article>",
id,
self.topic,
self.posts[0].to_string(),
)
}
}

@ -18,44 +18,7 @@
</header> </header>
<main> <main>
<div class="wrap"> <div class="wrap">
<article> {}
<img src="placeholder.jpg" alt="img">
<div>
<div class="meta">
<div>
{ post_datetime }&nbsp;
<strong>{ post_id }</strong>&nbsp;
</div>
<nav>
<a href="#">open</a>
<a href="#">quote</a>
<a href="#">reply</a>
</nav>
</div>
<div class="image-info">{ image_filename }</div>
<div class="title">{ post_title }</div>
{ post_content }
</div>
</article>
<article>
<img src="placeholder.jpg" alt="img">
<div>
<div class="meta">
<div>
{ post_datetime }&nbsp;
<strong>{ post_id }</strong>&nbsp;
</div>
<nav>
<a href="#">open</a>
<a href="#">quote</a>
<a href="#">reply</a>
</nav>
</div>
<div class="image-info">{ image_filename }</div>
<div class="title">{ post_title }</div>
{ post_content }
</div>
</article>
</div> </div>
</main> </main>
</head> </head>

Loading…
Cancel
Save