Added checks if the account already exists in the database

This commit is contained in:
StNicolay 2023-06-23 12:36:36 +03:00
parent be76b09427
commit 497e0ba5bf
Signed by: StNicolay
GPG Key ID: 9693D04DCD962B0D
3 changed files with 53 additions and 10 deletions

7
Cargo.lock generated
View File

@ -1345,6 +1345,7 @@ dependencies = [
"migration",
"parking_lot 0.12.1",
"pretty_env_logger",
"rustc-hash",
"sea-orm",
"serde",
"serde_json",
@ -1775,6 +1776,12 @@ dependencies = [
"serde_json",
]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.0"

View File

@ -23,6 +23,7 @@ log = "0.4.17"
migration = { version = "0.2.0", path = "migration" }
parking_lot = "0.12.1"
pretty_env_logger = "0.5.0"
rustc-hash = "1.1.0"
sea-orm = { version = "0.11.3", features = ["sqlx-mysql", "runtime-tokio-rustls"] }
serde = "1.0.163"
serde_json = "1.0.96"

View File

@ -1,12 +1,14 @@
use crate::{
errors::HandlerUsed,
errors::{HandlerUsed, NoUserInfo},
markups::deletion_markup,
models::{DecryptedAccount, User},
utils::delete_optional,
MainDialogue,
};
use futures::TryStreamExt;
use entity::prelude::*;
use futures::{future::try_join, TryStreamExt};
use itertools::Itertools;
use rustc_hash::FxHashSet;
use sea_orm::prelude::*;
use std::fmt::Write;
use teloxide::{adaptors::Throttle, net::Download, prelude::*, types::Document};
@ -27,7 +29,10 @@ async fn download_file(bot: &Throttle<Bot>, document: &Document) -> crate::Resul
}
#[inline]
fn process_accounts(accounts: &mut [DecryptedAccount]) -> crate::Result<Result<(), String>> {
fn process_accounts(
accounts: &mut [DecryptedAccount],
existing_names: FxHashSet<String>,
) -> crate::Result<Result<(), String>> {
for account in accounts.iter_mut() {
account.name.trim_in_place();
account.login.trim_in_place();
@ -37,20 +42,30 @@ fn process_accounts(accounts: &mut [DecryptedAccount]) -> crate::Result<Result<(
accounts.sort_unstable_by(|a, b| a.name.cmp(&b.name));
let mut duplicates = Vec::new();
let mut existing = Vec::new();
let mut invalid = Vec::new();
accounts
.iter()
.dedup_by_with_count(|a, b| a.name == b.name)
.for_each(|(count, account)| {
if count != 1 {
let duplicate = count != 1;
let exists = existing_names.contains(&account.name);
if duplicate {
duplicates.push(account.name.as_str());
} else if !account.validate() {
}
if exists {
existing.push(account.name.as_str())
}
// If it already exists or if it is a duplicate there's no need to check the account's validity
if !duplicate && !exists && !account.validate() {
invalid.push(account.name.as_str());
}
});
if duplicates.is_empty() && invalid.is_empty() {
drop(existing_names);
if duplicates.is_empty() && invalid.is_empty() && existing.is_empty() {
return Ok(Ok(()));
}
@ -64,6 +79,14 @@ fn process_accounts(accounts: &mut [DecryptedAccount]) -> crate::Result<Result<(
)?
}
if !existing.is_empty() {
write!(
error_text,
"\n\nAccounts with these names already exist in the database:\n{:?}",
existing.into_iter().format("\n")
)?
}
if !invalid.is_empty() {
write!(
error_text,
@ -78,10 +101,13 @@ fn process_accounts(accounts: &mut [DecryptedAccount]) -> crate::Result<Result<(
}
#[inline]
fn user_from_vec(vector: Vec<u8>) -> crate::Result<Result<User, String>> {
fn user_from_vec(
vector: Vec<u8>,
existing_names: FxHashSet<String>,
) -> crate::Result<Result<User, String>> {
let mut user: User = serde_json::from_slice(&vector)?;
drop(vector);
match process_accounts(&mut user.accounts)? {
match process_accounts(&mut user.accounts, existing_names)? {
Ok(()) => Ok(Ok(user)),
Err(error_text) => Ok(Err(error_text)),
}
@ -95,6 +121,7 @@ pub async fn get_user(
dialogue: MainDialogue,
next: super::PackagedHandler<User>,
) -> crate::Result<()> {
let user_id = msg.from().ok_or(NoUserInfo)?.id.0;
let mut handler = next.lock().await;
delete_optional(&bot, handler.previous.as_ref()).await;
@ -136,9 +163,17 @@ pub async fn get_user(
}
}
let data = download_file(&bot, document).await?;
let existing_names = async {
Account::get_names(user_id, &db)
.await?
.try_collect::<FxHashSet<_>>()
.await
.map_err(Into::into)
};
let user = match spawn_blocking(move || user_from_vec(data)).await? {
let (data, existing_names) = try_join(download_file(&bot, document), existing_names).await?;
let user = match spawn_blocking(move || user_from_vec(data, existing_names)).await? {
Ok(Ok(user)) => user,
Ok(Err(error_text)) => {
let msg = bot.send_message(msg.chat.id, error_text).await?;