Files
batch_downloader/src/services/utils.rs

164 lines
4.4 KiB
Rust
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use async_stream::stream;
use bytes::{Buf, Bytes};
use reqwest::Response;
use smartstring::alias::String as SmartString;
use tempfile::SpooledTempFile;
use translit::{gost779b_ru, CharsMapping, Transliterator};
use std::{
error::Error,
io::{Read, Seek, SeekFrom, Write},
};
use crate::structures::{CreateTask, ObjectType};
use super::library_client::{get_author, get_sequence};
pub fn get_key(input_data: CreateTask) -> String {
let mut data = input_data.clone();
data.allowed_langs.sort();
let data_string = serde_json::to_string(&data).unwrap();
format!("{:x}", md5::compute(data_string))
}
pub async fn response_to_tempfile(
res: &mut Response,
) -> Result<(SpooledTempFile, usize), Box<dyn std::error::Error + Send + Sync>> {
let mut tmp_file = tempfile::spooled_tempfile(5 * 1024 * 1024);
let mut data_size: usize = 0;
{
loop {
let chunk = res.chunk().await;
let result = match chunk {
Ok(v) => v,
Err(err) => return Err(Box::new(err)),
};
let data = match result {
Some(v) => v,
None => break,
};
data_size += data.len();
tmp_file.write_all(data.chunk())?;
}
tmp_file.seek(SeekFrom::Start(0)).unwrap();
}
Ok((tmp_file, data_size))
}
pub fn get_stream(
mut temp_file: Box<dyn Read + Send + Sync>,
) -> impl futures_core::Stream<Item = Result<Bytes, Box<dyn Error + Sync>>> {
stream! {
let mut buf = [0; 2048];
while let Ok(count) = temp_file.read(&mut buf) {
if count == 0 {
break;
}
yield Ok(Bytes::copy_from_slice(&buf[0..count]))
}
}
}
pub async fn get_filename(
object_type: ObjectType,
object_id: u32,
file_format: SmartString,
) -> Result<String, Box<dyn std::error::Error + Send + Sync>> {
let result_filename = match object_type {
ObjectType::Sequence => match get_sequence(object_id).await {
Ok(v) => v.name,
Err(err) => {
return Err(err);
}
},
ObjectType::Author | ObjectType::Translator => match get_author(object_id).await {
Ok(v) => vec![
v.first_name,
v.last_name,
v.middle_name.unwrap_or("".to_string()),
]
.into_iter()
.filter(|v| !v.is_empty())
.collect::<Vec<String>>()
.join("_"),
Err(err) => {
return Err(err);
}
},
};
let result_filename = {
let postfix = match object_type {
ObjectType::Sequence => "s",
ObjectType::Author => "a",
ObjectType::Translator => "t",
};
format!("{result_filename}_{postfix}")
};
let final_filename = {
let transliterator = Transliterator::new(gost779b_ru());
let mut filename_without_type = transliterator.convert(&result_filename, false);
"(),….!\"?»«':".get(..).into_iter().for_each(|char| {
filename_without_type = filename_without_type.replace(char, "");
});
let replace_char_map: CharsMapping = [
("", "-"),
("/", "_"),
("", "N"),
(" ", "_"),
("", "-"),
("á", "a"),
(" ", "_"),
("'", ""),
("`", ""),
("[", ""),
("]", ""),
("\"", ""),
]
.to_vec();
let replace_transliterator = Transliterator::new(replace_char_map);
let normal_filename = replace_transliterator.convert(&filename_without_type, false);
let normal_filename = normal_filename.replace(|c: char| !c.is_ascii(), "");
let right_part = format!(".{file_format}.zip");
let normal_filename_slice =
std::cmp::min(64 - right_part.len() - 1, normal_filename.len() - 1);
let left_part = if normal_filename_slice == normal_filename.len() - 1 {
&normal_filename
} else {
normal_filename
.get(..normal_filename_slice)
.unwrap_or_else(|| {
panic!(
"Can't slice left part: {:?} {:?}",
normal_filename, normal_filename_slice
)
})
};
format!("{left_part}{right_part}")
};
Ok(final_filename)
}