mirror of
https://git.alemi.dev/guestbook.rs.git
synced 2024-11-12 19:39:28 +01:00
fix: made rustc happy, now compiles
This commit is contained in:
parent
2e97bffc82
commit
d8fa2173cb
4 changed files with 33 additions and 47 deletions
|
@ -60,8 +60,11 @@ async fn main() {
|
|||
|
||||
let storage = Box::new(JsonFileStorageStrategy::new("./storage.json"));
|
||||
|
||||
let state = Context::new(storage)
|
||||
.register(Box::new(ConsoleTracingNotifier {}));
|
||||
let overrides = CliServeOverrides { author, public };
|
||||
|
||||
let mut state = Context::new(storage, overrides);
|
||||
|
||||
state.register(Box::new(ConsoleTracingNotifier {}));
|
||||
|
||||
let router = routes::create_router_with_app_routes(state);
|
||||
|
||||
|
|
14
src/model.rs
14
src/model.rs
|
@ -35,10 +35,10 @@ impl From<Page> for PageView {
|
|||
hasher.update(page.contact.as_deref().unwrap_or(&Uuid::new_v4().to_string()).as_bytes());
|
||||
let avatar = format!("{:x}", hasher.finalize());
|
||||
|
||||
let url = match page.contact {
|
||||
let url = match page.contact.as_deref() {
|
||||
None => None,
|
||||
Some(c) => if c.starts_with("http") {
|
||||
Some(c)
|
||||
Some(c.to_string())
|
||||
} else if c.contains('@') {
|
||||
Some(format!("mailto:{}", c))
|
||||
} else if c.contains('.') {
|
||||
|
@ -74,12 +74,12 @@ pub struct PageInsertion {
|
|||
|
||||
impl PageInsertion {
|
||||
pub fn sanitize(&mut self) {
|
||||
self.author = self.author.map(|x| html_escape::encode_safe(&x.chars().take(AUTHOR_MAX_CHARS).collect::<String>()).to_string());
|
||||
self.contact = self.contact.map(|x| html_escape::encode_safe(&x.chars().take(CONTACT_MAX_CHARS).collect::<String>()).to_string());
|
||||
self.author = self.author.as_mut().map(|x| html_escape::encode_safe(&x.chars().take(AUTHOR_MAX_CHARS).collect::<String>()).to_string());
|
||||
self.contact = self.contact.as_mut().map(|x| html_escape::encode_safe(&x.chars().take(CONTACT_MAX_CHARS).collect::<String>()).to_string());
|
||||
self.body = html_escape::encode_safe(&self.body.chars().take(BODY_MAX_CHARS).collect::<String>()).to_string();
|
||||
}
|
||||
|
||||
pub fn convert(mut self, overrides: crate::CliServeOverrides) -> Page {
|
||||
pub fn convert(mut self, overrides: &crate::CliServeOverrides) -> Page {
|
||||
self.sanitize();
|
||||
|
||||
let mut page = Page {
|
||||
|
@ -90,8 +90,8 @@ impl PageInsertion {
|
|||
public: true,
|
||||
};
|
||||
|
||||
if let Some(author) = overrides.author {
|
||||
page.author = author;
|
||||
if let Some(author) = &overrides.author {
|
||||
page.author = author.to_string();
|
||||
}
|
||||
if let Some(public) = overrides.public {
|
||||
page.public = public;
|
||||
|
|
|
@ -1,71 +1,54 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use axum::{Json, Form, Router, routing::{put, post, get}, extract::{State, Query}, response::Redirect};
|
||||
use chrono::Utc;
|
||||
use md5::{Md5, Digest};
|
||||
use tokio::sync::RwLock;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{notifications::NotificationProcessor, model::{Page, PageOptions, PageInsertion}, storage::StorageStrategy};
|
||||
use crate::{notifications::NotificationProcessor, model::{Page, PageOptions, PageInsertion}, storage::StorageStrategy, CliServeOverrides};
|
||||
|
||||
pub fn create_router_with_app_routes(state: Context) -> Router {
|
||||
Router::new()
|
||||
.route("/api", get(get_suggestion))
|
||||
.route("/api", post(send_suggestion_form))
|
||||
.route("/api", put(send_suggestion_json))
|
||||
.with_state(Arc::new(RwLock::new(state)))
|
||||
.with_state(Arc::new(state))
|
||||
}
|
||||
|
||||
type SafeContext = Arc<RwLock<Context>>;
|
||||
|
||||
pub struct Context {
|
||||
providers: Vec<Box<dyn NotificationProcessor<Page>>>,
|
||||
storage: Box<dyn StorageStrategy<Page>>,
|
||||
overrides: CliServeOverrides,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(storage: Box<dyn StorageStrategy<Page>>) -> Self {
|
||||
Context { providers: Vec::new(), storage }
|
||||
pub fn new(storage: Box<dyn StorageStrategy<Page>>, overrides: CliServeOverrides) -> Self {
|
||||
Context { providers: Vec::new(), storage, overrides }
|
||||
}
|
||||
|
||||
pub fn register(mut self, notifier: Box<dyn NotificationProcessor<Page>>) -> Self {
|
||||
pub fn register(&mut self, notifier: Box<dyn NotificationProcessor<Page>>) {
|
||||
self.providers.push(notifier);
|
||||
self
|
||||
}
|
||||
|
||||
async fn process(&self, x: &Page) {
|
||||
for p in self.providers.iter() {
|
||||
p.process(x).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_suggestion(payload: PageInsertion, state: SafeContext) -> Result<Redirect, String> {
|
||||
let page = Page::from(payload);
|
||||
// lock state, process and archive new page
|
||||
let mut lock = state.write().await;
|
||||
lock.process(&page).await;
|
||||
match lock.storage.archive(page).await {
|
||||
async fn send_suggestion(payload: PageInsertion, state: Arc<Context>) -> Result<Redirect, String> {
|
||||
let page = payload.convert(&state.overrides);
|
||||
for p in state.providers.iter() {
|
||||
p.process(&page).await;
|
||||
}
|
||||
match state.storage.archive(page).await {
|
||||
Ok(()) => Ok(Redirect::to("/")),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_suggestion_json(State(state): State<SafeContext>, Json(payload): Json<PageInsertion>) -> Result<Redirect, String> { send_suggestion(payload, state).await }
|
||||
async fn send_suggestion_form(State(state): State<SafeContext>, Form(payload): Form<PageInsertion>) -> Result<Redirect, String> { send_suggestion(payload, state).await }
|
||||
async fn send_suggestion_json(State(state): State<Arc<Context>>, Json(payload): Json<PageInsertion>) -> Result<Redirect, String> { send_suggestion(payload, state).await }
|
||||
async fn send_suggestion_form(State(state): State<Arc<Context>>, Form(payload): Form<PageInsertion>) -> Result<Redirect, String> { send_suggestion(payload, state).await }
|
||||
|
||||
|
||||
async fn get_suggestion(State(state): State<SafeContext>, Query(page): Query<PageOptions>) -> Result<Json<Vec<Page>>, String> {
|
||||
async fn get_suggestion(State(state): State<Arc<Context>>, Query(page): Query<PageOptions>) -> Result<Json<Vec<Page>>, String> {
|
||||
let offset = page.offset.unwrap_or(0);
|
||||
let limit = std::cmp::min(page.limit.unwrap_or(20), 20);
|
||||
|
||||
match state.read().await.storage.extract(offset, limit).await {
|
||||
match state.storage.extract(offset, limit).await {
|
||||
Ok(x) => Ok(Json(x)),
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn limit_string(s: &str, l: usize) -> String {
|
||||
// TODO is there a better way? slicing doesn't work when l > s.len
|
||||
s.chars().take(l).collect()
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ pub enum StorageStrategyError {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
pub trait StorageStrategy<T> : Send + Sync {
|
||||
async fn archive(&mut self, payload: T) -> Result<(), StorageStrategyError>;
|
||||
async fn archive(&self, payload: T) -> Result<(), StorageStrategyError>;
|
||||
async fn extract(&self, offset: usize, window: usize) -> Result<Vec<T>, StorageStrategyError>;
|
||||
}
|
||||
|
||||
|
@ -33,19 +33,19 @@ impl JsonFileStorageStrategy {
|
|||
|
||||
#[async_trait::async_trait]
|
||||
impl StorageStrategy<Page> for JsonFileStorageStrategy {
|
||||
async fn archive(&mut self, payload: Page) -> Result<(), StorageStrategyError> {
|
||||
async fn archive(&self, payload: Page) -> Result<(), StorageStrategyError> {
|
||||
let path = self.path.write().await;
|
||||
let file_content = std::fs::read_to_string(*path)?;
|
||||
let file_content = std::fs::read_to_string(&*path)?;
|
||||
let mut current_content : Vec<Page> = serde_json::from_str(&file_content)?;
|
||||
current_content.push(payload);
|
||||
let updated_content = serde_json::to_string(¤t_content)?;
|
||||
std::fs::write(*path, updated_content)?;
|
||||
std::fs::write(&*path, updated_content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn extract(&self, offset: usize, window: usize) -> Result<Vec<Page>, StorageStrategyError> {
|
||||
let path = self.path.read().await;
|
||||
let file_content = std::fs::read_to_string(*path)?;
|
||||
let file_content = std::fs::read_to_string(&*path)?;
|
||||
let current_content : Vec<Page> = serde_json::from_str(&file_content)?;
|
||||
let mut out = Vec::new();
|
||||
for sugg in current_content.iter().rev().skip(offset) {
|
||||
|
|
Loading…
Reference in a new issue