Preallocations

This commit is contained in:
Adrian Woźniak 2021-08-12 16:10:30 +02:00
parent 146be0184b
commit c8730485d0
No known key found for this signature in database
GPG Key ID: DE43476F72AD3F6C
14 changed files with 129 additions and 48 deletions

4
Cargo.lock generated
View File

@ -2042,9 +2042,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.94"
version = "0.2.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
checksum = "a7f823d141fe0a24df1e23b4af4e3c7ba9e5966ec514ea068c93024aa7deb765"
[[package]]
name = "line-wrap"

View File

@ -16,6 +16,7 @@ use jirs_data::{User, UserId, WsMsg};
use websocket_actor::server::InnerMsg::BroadcastToChannel;
use websocket_actor::server::WsServer;
#[cfg(feature = "aws-s3")]
#[post("/")]
pub async fn upload(
mut payload: Multipart,
@ -80,11 +81,74 @@ pub async fn upload(
}
}
#[cfg(not(feature = "aws-s3"))]
#[post("/")]
pub async fn upload(
mut payload: Multipart,
db: Data<Addr<DbExecutor>>,
ws: Data<Addr<WsServer>>,
fs: Data<Addr<filesystem_actor::FileSystemExecutor>>,
) -> Result<HttpResponse, Error> {
let mut user_id: Option<UserId> = None;
let mut avatar_url: Option<String> = None;
while let Ok(Some(field)) = payload.try_next().await {
let disposition: ContentDisposition = match field.content_disposition() {
Some(d) => d,
_ => continue,
};
if !disposition.is_form_data() {
return Ok(HttpResponse::BadRequest().finish());
}
match disposition.get_name() {
Some("token") => {
user_id = Some(handle_token(field, db.clone()).await?);
}
Some("avatar") => {
let id = user_id.ok_or_else(|| HttpResponse::Unauthorized().finish())?;
avatar_url = Some(
crate::handlers::upload_avatar_image::handle_image(
id,
field,
disposition,
fs.clone(),
)
.await?,
);
}
_ => continue,
};
}
let user_id = match user_id {
Some(id) => id,
_ => return Ok(HttpResponse::Unauthorized().finish()),
};
let project_id = match block_on(db.send(CurrentUserProject { user_id })) {
Ok(Ok(user_project)) => user_project.project_id,
_ => return Ok(HttpResponse::UnprocessableEntity().finish()),
};
match (user_id, avatar_url) {
(user_id, Some(avatar_url)) => {
let user = update_user_avatar(user_id, avatar_url.clone(), db).await?;
ws.send(BroadcastToChannel(
project_id,
WsMsg::AvatarUrlChanged(user.id, avatar_url),
))
.await
.map_err(|_| HttpResponse::UnprocessableEntity().finish())?;
Ok(HttpResponse::NoContent().finish())
}
_ => Ok(HttpResponse::UnprocessableEntity().finish()),
}
}
async fn update_user_avatar(
user_id: UserId,
new_url: String,
db: Data<Addr<DbExecutor>>,
) -> Result<User, Error> {
) -> Result<User, actix_web::Error> {
match db
.send(UpdateAvatarUrl {
user_id,
@ -96,16 +160,23 @@ async fn update_user_avatar(
Ok(Err(e)) => {
error!("{:?}", e);
Err(HttpResponse::Unauthorized().finish().into())
Err(actix_web::Error::from(
HttpResponse::Unauthorized().finish(),
))
}
Err(e) => {
error!("{:?}", e);
Err(HttpResponse::Unauthorized().finish().into())
Err(actix_web::Error::from(
HttpResponse::Unauthorized().finish(),
))
}
}
}
async fn handle_token(mut field: Field, db: Data<Addr<DbExecutor>>) -> Result<UserId, Error> {
async fn handle_token(
mut field: Field,
db: Data<Addr<DbExecutor>>,
) -> Result<UserId, actix_web::Error> {
let mut f: Vec<u8> = vec![];
while let Some(chunk) = field.next().await {
let data = chunk.unwrap();

View File

@ -29,9 +29,9 @@ flate2 = { version = "*" }
syntect = { version = "*" }
lazy_static = { version = "*" }
log = "0.4"
pretty_env_logger = "0.4"
env_logger = "0.7"
log = { version = "0.4" }
pretty_env_logger = { version = "0.4" }
env_logger = { version = "0.7" }
uuid = { version = "0.8.1", features = ["serde", "v4", "v5"] }

View File

@ -7,10 +7,8 @@ use jirs_data::msg::WsError;
use jirs_data::{Token, WsMsg};
use mail_actor::welcome::Welcome;
use crate::{
db_or_debug_and_return, db_or_debug_or_fallback, mail_or_debug_and_return, WebSocketActor,
WsHandler, WsResult,
};
use crate::server::InnerMsg;
use crate::{db_or_debug_and_return, db_or_debug_or_fallback, mail_or_debug_and_return, *};
pub struct Authenticate {
pub name: String,
@ -69,7 +67,7 @@ impl WsHandler<CheckAuthToken> for WebSocketActor {
self.current_user_project = self.load_user_project().ok();
self.current_project = self.load_project().ok();
block_on(self.join_channel(ctx.address().recipient()));
block_on(self.join_channel(ctx.address().recipient::<InnerMsg>()));
Ok(Some(WsMsg::AuthorizeLoaded(Ok((user, setting)))))
}
}

View File

@ -3,7 +3,7 @@ use jirs_data::{
DescriptionString, EndsAt, EpicId, IssueType, NameString, StartsAt, UserProject, WsMsg,
};
use crate::{db_or_debug_and_return, WebSocketActor, WsHandler, WsResult};
use crate::{db_or_debug_and_return, *};
pub struct LoadEpics;

View File

@ -26,7 +26,7 @@ trait WsMessageSender {
fn send_msg(&mut self, msg: &jirs_data::WsMsg);
}
struct WebSocketActor {
pub struct WebSocketActor {
db: Data<Addr<DbExecutor>>,
mail: Data<Addr<MailExecutor>>,
addr: Addr<WsServer>,
@ -36,13 +36,20 @@ struct WebSocketActor {
current_project: Option<jirs_data::Project>,
}
impl Actor for WebSocketActor {
type Context = ws::WebsocketContext<WebSocketActor>;
pub type WsCtx = ws::WebsocketContext<WebSocketActor>;
impl actix::Actor for WebSocketActor {
type Context = WsCtx;
}
impl WsMessageSender for ws::WebsocketContext<WebSocketActor> {
fn send_msg(&mut self, msg: &WsMsg) {
self.binary(bincode::serialize(msg).unwrap())
match bincode::serialize(msg) {
Err(err) => {
log::error!("{}", err);
}
Ok(v) => self.binary(v),
}
}
}
@ -328,7 +335,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
self.addr.do_send(InnerMsg::Leave(
up.project_id,
user.id,
ctx.address().recipient(),
ctx.address().recipient::<InnerMsg>(),
));
}
ctx.stop()
@ -337,7 +344,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for WebSocketActor {
pub trait WsHandler<Message>
where
Self: Actor,
Self: actix::Actor<Context = WsCtx>,
{
fn handle_msg(&mut self, msg: Message, _ctx: &mut <Self as Actor>::Context) -> WsResult;
}

View File

@ -280,7 +280,7 @@ impl Model {
issue_form: None,
project_form: None,
comment_form: None,
comments_by_project_id: Default::default(),
comments_by_project_id: HashMap::with_capacity(1_000),
page_content: page.build_content(),
page,
host_url,
@ -291,22 +291,22 @@ impl Model {
messages_tooltip_visible: false,
issues: vec![],
users: vec![],
users_by_id: Default::default(),
users_by_id: HashMap::with_capacity(1_000),
user_settings: None,
comments: vec![],
comments_by_id: Default::default(),
comments_by_id: HashMap::with_capacity(1_000),
issue_statuses: vec![],
issue_statuses_by_id: Default::default(),
issue_statuses_by_name: Default::default(),
issue_statuses_by_id: HashMap::with_capacity(1_000),
issue_statuses_by_name: HashMap::with_capacity(1_000),
messages: vec![],
user_projects: vec![],
projects: vec![],
epics: vec![],
issues_by_id: Default::default(),
issues_by_id: HashMap::with_capacity(1_000),
show_extras: false,
epics_by_id: Default::default(),
epics_by_id: HashMap::with_capacity(1_000),
modals_stack: vec![],
modals: Default::default(),
modals: Modals::default(),
key_triggers: std::rc::Rc::new(std::cell::RefCell::new(HashMap::with_capacity(20))),
distinct_key_up: crate::shared::on_event::distinct(),
}

View File

@ -16,12 +16,15 @@ impl EpicsPage {
}
pub fn build_issues_per_epic(model: &Model) -> HashMap<EpicId, Vec<IssueId>> {
model.issues().iter().fold(HashMap::new(), |mut h, issue| {
model.issues().iter().fold(
HashMap::with_capacity(model.issues().len()),
|mut h, issue| {
if let Some(epic_id) = issue.epic_id.as_ref() {
h.entry(*epic_id).or_default().push(issue.id);
}
h
})
},
)
}
pub fn issues(&self, epic_id: EpicId) -> Option<&Vec<IssueId>> {

View File

@ -130,7 +130,7 @@ async fn update_avatar(data: FormData, host_url: String) -> Option<Msg> {
let path = format!("{}/avatar/", host_url);
let result = Request::new(path)
.method(Method::Post)
.body(data.into())
.body(&data)
.fetch()
.await;
let response = match result {

View File

@ -68,10 +68,15 @@ impl ProjectPage {
issues.collect()
};
let issues_per_epic_id = issues.into_iter().fold(HashMap::new(), |mut m, issue| {
let issues_per_epic_id = {
let issues_len = issues.len();
issues
.into_iter()
.fold(HashMap::with_capacity(issues_len), |mut m, issue| {
m.entry(issue.epic_id).or_insert_with(Vec::new).push(issue);
m
});
})
};
epics
.map(|epic| {

View File

@ -1,7 +1,6 @@
use std::collections::HashSet;
use jirs_data::{IssueStatus, IssueStatusId, ProjectFieldId, UpdateProjectPayload, WsMsg};
use seed::error;
use seed::prelude::Orders;
use crate::components::styled_select::StyledSelectChanged;

View File

@ -15,10 +15,7 @@ path = "./src/main.rs"
[features]
aws-s3 = ["amazon-actor"]
local-storage = ["filesystem-actor"]
default = [
"aws-s3",
"local-storage",
]
default = ["local-storage"]
[dependencies]
actix = { version = "0.10.0" }

View File

@ -20,9 +20,10 @@ hi = []
mail = []
web = ["aws-s3", "local-storage"]
websocket = []
default = ["local-storage", "database", "hi", "mail", "web", "websocket"]
[dependencies]
serde = "*"
serde = { version = "*" }
toml = { version = "*" }
# Amazon S3

View File

@ -17,8 +17,8 @@ backend = ["diesel", "actix", "derive_enum_sql"]
frontend = []
[dependencies]
serde = "*"
serde_json = "*"
serde = { version = "*" }
serde_json = { version = "*" }
chrono = { version = "*", features = ["serde"] }
uuid = { version = ">=0.7.0, <0.9.0", features = ["serde"] }