fix: made rustc happy, now compiles

This commit is contained in:
əlemi 2024-01-02 03:12:29 +01:00
parent 2e97bffc82
commit d8fa2173cb
Signed by: alemi
GPG key ID: A4895B84D311642C
4 changed files with 33 additions and 47 deletions

View file

@ -60,8 +60,11 @@ async fn main() {
let storage = Box::new(JsonFileStorageStrategy::new("./storage.json")); let storage = Box::new(JsonFileStorageStrategy::new("./storage.json"));
let state = Context::new(storage) let overrides = CliServeOverrides { author, public };
.register(Box::new(ConsoleTracingNotifier {}));
let mut state = Context::new(storage, overrides);
state.register(Box::new(ConsoleTracingNotifier {}));
let router = routes::create_router_with_app_routes(state); let router = routes::create_router_with_app_routes(state);

View file

@ -35,10 +35,10 @@ impl From<Page> for PageView {
hasher.update(page.contact.as_deref().unwrap_or(&Uuid::new_v4().to_string()).as_bytes()); hasher.update(page.contact.as_deref().unwrap_or(&Uuid::new_v4().to_string()).as_bytes());
let avatar = format!("{:x}", hasher.finalize()); let avatar = format!("{:x}", hasher.finalize());
let url = match page.contact { let url = match page.contact.as_deref() {
None => None, None => None,
Some(c) => if c.starts_with("http") { Some(c) => if c.starts_with("http") {
Some(c) Some(c.to_string())
} else if c.contains('@') { } else if c.contains('@') {
Some(format!("mailto:{}", c)) Some(format!("mailto:{}", c))
} else if c.contains('.') { } else if c.contains('.') {
@ -74,12 +74,12 @@ pub struct PageInsertion {
impl PageInsertion { impl PageInsertion {
pub fn sanitize(&mut self) { 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.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.map(|x| html_escape::encode_safe(&x.chars().take(CONTACT_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(); 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(); self.sanitize();
let mut page = Page { let mut page = Page {
@ -90,8 +90,8 @@ impl PageInsertion {
public: true, public: true,
}; };
if let Some(author) = overrides.author { if let Some(author) = &overrides.author {
page.author = author; page.author = author.to_string();
} }
if let Some(public) = overrides.public { if let Some(public) = overrides.public {
page.public = public; page.public = public;

View file

@ -1,71 +1,54 @@
use std::sync::Arc; use std::sync::Arc;
use axum::{Json, Form, Router, routing::{put, post, get}, extract::{State, Query}, response::Redirect}; 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 { pub fn create_router_with_app_routes(state: Context) -> Router {
Router::new() Router::new()
.route("/api", get(get_suggestion)) .route("/api", get(get_suggestion))
.route("/api", post(send_suggestion_form)) .route("/api", post(send_suggestion_form))
.route("/api", put(send_suggestion_json)) .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 { pub struct Context {
providers: Vec<Box<dyn NotificationProcessor<Page>>>, providers: Vec<Box<dyn NotificationProcessor<Page>>>,
storage: Box<dyn StorageStrategy<Page>>, storage: Box<dyn StorageStrategy<Page>>,
overrides: CliServeOverrides,
} }
impl Context { impl Context {
pub fn new(storage: Box<dyn StorageStrategy<Page>>) -> Self { pub fn new(storage: Box<dyn StorageStrategy<Page>>, overrides: CliServeOverrides) -> Self {
Context { providers: Vec::new(), storage } 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.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> { async fn send_suggestion(payload: PageInsertion, state: Arc<Context>) -> Result<Redirect, String> {
let page = Page::from(payload); let page = payload.convert(&state.overrides);
// lock state, process and archive new page for p in state.providers.iter() {
let mut lock = state.write().await; p.process(&page).await;
lock.process(&page).await; }
match lock.storage.archive(page).await { match state.storage.archive(page).await {
Ok(()) => Ok(Redirect::to("/")), Ok(()) => Ok(Redirect::to("/")),
Err(e) => Err(e.to_string()), 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_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<SafeContext>, Form(payload): Form<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 offset = page.offset.unwrap_or(0);
let limit = std::cmp::min(page.limit.unwrap_or(20), 20); 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)), Ok(x) => Ok(Json(x)),
Err(e) => Err(e.to_string()), 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()
}

View file

@ -13,7 +13,7 @@ pub enum StorageStrategyError {
#[async_trait::async_trait] #[async_trait::async_trait]
pub trait StorageStrategy<T> : Send + Sync { 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>; async fn extract(&self, offset: usize, window: usize) -> Result<Vec<T>, StorageStrategyError>;
} }
@ -33,19 +33,19 @@ impl JsonFileStorageStrategy {
#[async_trait::async_trait] #[async_trait::async_trait]
impl StorageStrategy<Page> for JsonFileStorageStrategy { 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 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)?; let mut current_content : Vec<Page> = serde_json::from_str(&file_content)?;
current_content.push(payload); current_content.push(payload);
let updated_content = serde_json::to_string(&current_content)?; let updated_content = serde_json::to_string(&current_content)?;
std::fs::write(*path, updated_content)?; std::fs::write(&*path, updated_content)?;
Ok(()) Ok(())
} }
async fn extract(&self, offset: usize, window: usize) -> Result<Vec<Page>, StorageStrategyError> { async fn extract(&self, offset: usize, window: usize) -> Result<Vec<Page>, StorageStrategyError> {
let path = self.path.read().await; 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 current_content : Vec<Page> = serde_json::from_str(&file_content)?;
let mut out = Vec::new(); let mut out = Vec::new();
for sugg in current_content.iter().rev().skip(offset) { for sugg in current_content.iter().rev().skip(offset) {