replace zip command with zip lib, do languages properly i suppose, and other stuff i do not remember
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ Cargo.lock
|
|||||||
/cba
|
/cba
|
||||||
*.jpg
|
*.jpg
|
||||||
*.png
|
*.png
|
||||||
|
*.cbz
|
||||||
|
|||||||
316
src/main.rs
316
src/main.rs
@@ -1,6 +1,11 @@
|
|||||||
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};
|
||||||
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
|
use reqwest_retry::{policies::ExponentialBackoff, RetryTransientMiddleware};
|
||||||
use response_deserializer::{ChapterImages, SearchResult};
|
use response_deserializer::{ChapterImages, SearchResult};
|
||||||
|
use std::fs::File;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::path::Path;
|
||||||
|
use zip::write::{SimpleFileOptions, ZipWriter};
|
||||||
|
use zip::CompressionMethod;
|
||||||
|
|
||||||
mod response_deserializer;
|
mod response_deserializer;
|
||||||
mod select;
|
mod select;
|
||||||
@@ -15,7 +20,7 @@ type Client = ClientWithMiddleware;
|
|||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let config = util::args();
|
let config = util::args();
|
||||||
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(3);
|
let retry_policy = ExponentialBackoff::builder().build_with_max_retries(10);
|
||||||
let client = ClientBuilder::new(
|
let client = ClientBuilder::new(
|
||||||
reqwest::Client::builder()
|
reqwest::Client::builder()
|
||||||
.user_agent("manga-cli/version-0.1")
|
.user_agent("manga-cli/version-0.1")
|
||||||
@@ -32,73 +37,26 @@ async fn main() {
|
|||||||
];
|
];
|
||||||
let limit = config.result_limit;
|
let limit = config.result_limit;
|
||||||
|
|
||||||
let results = if let Some(query) = config.search {
|
let mut choice = 0;
|
||||||
|
let results = if let Some(util::ConfigSearch::Id(set_id)) = config.search {
|
||||||
|
let id_query_result: response_deserializer::IdQueryResult =
|
||||||
|
id_query_get_info(client, &set_id).await;
|
||||||
|
vec![id_query_result.data]
|
||||||
|
} else {
|
||||||
|
let results = if let Some(ref query) = config.search {
|
||||||
match query {
|
match query {
|
||||||
util::ConfigSearch::Query(query) => search(client, &query, &filters, limit).await,
|
util::ConfigSearch::Query(query) => search(client, query, &filters, limit).await,
|
||||||
util::ConfigSearch::Id(_) => todo!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let input = util::get_input("Enter search query: ");
|
let input = util::get_input("Enter search query: ");
|
||||||
search(client, &input, &filters, limit).await
|
search(client, &input, &filters, limit).await
|
||||||
};
|
};
|
||||||
let cover_ex = match config.cover_size {
|
choice = select_manga_from_search(client, &config, &results).await;
|
||||||
util::CoverSize::Full => "",
|
results.data
|
||||||
util::CoverSize::W256 => ".256.jpg",
|
|
||||||
util::CoverSize::W512 => ".512.jpg",
|
|
||||||
};
|
};
|
||||||
|
let manga = &results[choice as usize];
|
||||||
|
|
||||||
let mut entry_futures = Vec::new();
|
|
||||||
for result in results.data.iter() {
|
|
||||||
let mut entry = Entry::new(result.attributes.title.en.clone());
|
|
||||||
if let Some(year) = result.attributes.year {
|
|
||||||
entry.add_info("year", year);
|
|
||||||
}
|
|
||||||
let id = result.id.to_string();
|
|
||||||
entry.add_info("id", &id);
|
|
||||||
entry.add_info("status", result.attributes.status.to_string());
|
|
||||||
entry.add_info(
|
|
||||||
"content rating",
|
|
||||||
result.attributes.content_rating.to_string(),
|
|
||||||
);
|
|
||||||
if let Some(chapters) = result.attributes.last_chapter {
|
|
||||||
entry.add_info("chapters", chapters);
|
|
||||||
}
|
|
||||||
if let Some(volumes) = result.attributes.last_volume {
|
|
||||||
entry.add_info("volumes", volumes);
|
|
||||||
}
|
|
||||||
if let Some(cover_data) = &result.relationships[2].attributes {
|
|
||||||
// The lib used for converting to sixel is abysmally slow for larger images, this
|
|
||||||
// should be in a future to allow for multithreaded work
|
|
||||||
let future = async move {
|
|
||||||
let image_url = format!(
|
|
||||||
"https://uploads.mangadex.org/covers/{id}/{}{cover_ex}",
|
|
||||||
&cover_data.file_name
|
|
||||||
);
|
|
||||||
let data = client
|
|
||||||
.get(&image_url)
|
|
||||||
.send()
|
|
||||||
.await
|
|
||||||
.unwrap()
|
|
||||||
.bytes()
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
let result = util::convert_to_sixel(&data);
|
|
||||||
|
|
||||||
entry.set_image(result);
|
|
||||||
entry
|
|
||||||
};
|
|
||||||
entry_futures.push(future);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let entries = futures::future::join_all(entry_futures).await;
|
|
||||||
let choice = match select::select(&entries) {
|
|
||||||
Ok(v) => v,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("ERROR: Failed to select: {:?}", e);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let choice_id = &results.data[choice as usize].id;
|
|
||||||
let bonus = if let Some(bonus) = config.bonus {
|
let bonus = if let Some(bonus) = config.bonus {
|
||||||
bonus
|
bonus
|
||||||
} else {
|
} else {
|
||||||
@@ -111,6 +69,24 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
let selection_type = if let Some(selection_type) = config.selection_type {
|
let selection_type = if let Some(selection_type) = config.selection_type {
|
||||||
|
if let Some(selection_range) = config.selection_range {
|
||||||
|
match selection_type {
|
||||||
|
util::ConfigSelectionType::Volume => {
|
||||||
|
if let Some(selection) = util::choose_volumes(selection_range.as_str()) {
|
||||||
|
util::SelectionType::Volume(selection)
|
||||||
|
} else {
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
util::ConfigSelectionType::Chapter => {
|
||||||
|
if let Some(selection) = util::choose_chapters(selection_range.as_str()) {
|
||||||
|
util::SelectionType::Chapter(selection)
|
||||||
|
} else {
|
||||||
|
std::process::exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
match selection_type {
|
match selection_type {
|
||||||
util::ConfigSelectionType::Volume => loop {
|
util::ConfigSelectionType::Volume => loop {
|
||||||
let input = util::get_input("Choose volumes: ").replace(" ", "");
|
let input = util::get_input("Choose volumes: ").replace(" ", "");
|
||||||
@@ -125,28 +101,49 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
'outer: loop {
|
'outer: loop {
|
||||||
match util::get_input("Select by volume or chapter? [v/c] : ").as_str() {
|
match util::get_input("Select by volume or chapter? [v/c] : ").as_str() {
|
||||||
"v" | "volume" => loop {
|
"v" | "volume" => {
|
||||||
|
if let Some(ref selection_range) = config.selection_range {
|
||||||
|
if let Some(selection) = util::choose_volumes(selection_range.as_str()) {
|
||||||
|
break util::SelectionType::Volume(selection);
|
||||||
|
} else {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loop {
|
||||||
let input = util::get_input("Choose volumes: ").replace(" ", "");
|
let input = util::get_input("Choose volumes: ").replace(" ", "");
|
||||||
if let Some(selection) = util::choose_volumes(input.as_str()) {
|
if let Some(selection) = util::choose_volumes(input.as_str()) {
|
||||||
break 'outer util::SelectionType::Volume(selection);
|
break 'outer util::SelectionType::Volume(selection);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
"c" | "chapter" => {
|
"c" | "chapter" => {
|
||||||
|
if let Some(ref selection_range) = config.selection_range {
|
||||||
|
if let Some(selection) = util::choose_chapters(selection_range.as_str()) {
|
||||||
|
break util::SelectionType::Chapter(selection);
|
||||||
|
} else {
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
loop {
|
||||||
let input = util::get_input("Choose chapters: ").replace(" ", "");
|
let input = util::get_input("Choose chapters: ").replace(" ", "");
|
||||||
if let Some(selection) = util::choose_chapters(input.as_str()) {
|
if let Some(selection) = util::choose_chapters(input.as_str()) {
|
||||||
break 'outer util::SelectionType::Chapter(selection);
|
break 'outer util::SelectionType::Chapter(selection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("Invalid input");
|
eprintln!("Invalid input");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut chapters = match get_chapters(client, choice_id).await {
|
let mut chapters = match get_chapters(client, &manga.id).await {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("ERROR: {:#?}", e);
|
eprintln!("ERROR: {:#?}", e);
|
||||||
@@ -166,11 +163,9 @@ async fn main() {
|
|||||||
let selected_chapters =
|
let selected_chapters =
|
||||||
util::get_chapters_from_selection(util::Selection::new(selection_type, bonus), &chapters);
|
util::get_chapters_from_selection(util::Selection::new(selection_type, bonus), &chapters);
|
||||||
|
|
||||||
let mut chapters_image_data = Vec::new();
|
let title = &manga.attributes.title;
|
||||||
let mut i = 0;
|
for (i, chapter) in selected_chapters.iter().enumerate() {
|
||||||
for chapter in &selected_chapters {
|
let chapter_image_data = loop {
|
||||||
// rate limits beware
|
|
||||||
let r = loop {
|
|
||||||
let json = client
|
let json = client
|
||||||
.get(format!("{BASE}/at-home/server/{}", chapter.id))
|
.get(format!("{BASE}/at-home/server/{}", chapter.id))
|
||||||
.send()
|
.send()
|
||||||
@@ -195,40 +190,40 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
std::thread::sleep(std::time::Duration::from_millis(800));
|
println!(
|
||||||
println!("downloaded chapter json image data: {i}");
|
"\x1b[1A\x1b[2Kdownloaded chapter json image data: [{i}/{}]",
|
||||||
i += 1;
|
selected_chapters.len()
|
||||||
chapters_image_data.push(r);
|
);
|
||||||
}
|
let chapter =
|
||||||
|
download_chapter_images(client, &chapter_image_data, selected_chapters[i]).await;
|
||||||
let chapters = futures::future::join_all(
|
|
||||||
chapters_image_data
|
|
||||||
.iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(i, image_data)| {
|
|
||||||
download_chapter_images(client, image_data, selected_chapters[i])
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
for (i, chapter) in chapters.iter().enumerate() {
|
|
||||||
match chapter {
|
match chapter {
|
||||||
Ok(chapter) => {
|
Ok(chapter) => {
|
||||||
for (j, image) in chapter.iter().enumerate() {
|
for (j, image) in chapter.iter().enumerate() {
|
||||||
let chapter_n = selected_chapters[i].attributes.chapter.unwrap();
|
let chapter_n = selected_chapters[i].attributes.chapter;
|
||||||
|
let chapter_text = if let Some(n) = chapter_n {
|
||||||
|
n.to_string()
|
||||||
|
} else {
|
||||||
|
String::from("_none")
|
||||||
|
};
|
||||||
|
let chapter_path = format!(
|
||||||
|
"images/{}/chapter_{:0>3}_image_{:0>3}.png",
|
||||||
|
title.en, chapter_text, j
|
||||||
|
);
|
||||||
let path = if let Some(v) = selected_chapters[i].attributes.volume {
|
let path = if let Some(v) = selected_chapters[i].attributes.volume {
|
||||||
|
if create_volumes {
|
||||||
format!(
|
format!(
|
||||||
"images/{}/volume_{:0>3}/chapter{:0>3}_image_{:0>3}.png",
|
"images/{}/volume_{:0>3}/chapter_{:0>3}_image_{:0>3}.png",
|
||||||
results.data[choice as usize].attributes.title.en, v, chapter_n, j
|
title.en, v, chapter_text, j
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(
|
chapter_path
|
||||||
"images/{}/chapter{:0>3}_image_{:0>3}.png",
|
}
|
||||||
results.data[choice as usize].attributes.title.en, chapter_n, j
|
} else {
|
||||||
)
|
chapter_path
|
||||||
};
|
};
|
||||||
let path = std::path::Path::new(&path);
|
let path = std::path::Path::new(&path);
|
||||||
if selected_chapters[i].attributes.volume.is_some()
|
if create_volumes
|
||||||
|
&& selected_chapters[i].attributes.volume.is_some()
|
||||||
&& !&path.parent().unwrap().exists()
|
&& !&path.parent().unwrap().exists()
|
||||||
{
|
{
|
||||||
if !path.parent().unwrap().parent().unwrap().exists() {
|
if !path.parent().unwrap().parent().unwrap().exists() {
|
||||||
@@ -245,7 +240,6 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = &results.data[choice as usize].attributes.title;
|
|
||||||
if create_volumes {
|
if create_volumes {
|
||||||
let mut volumes = Vec::new();
|
let mut volumes = Vec::new();
|
||||||
selected_chapters
|
selected_chapters
|
||||||
@@ -257,14 +251,55 @@ async fn main() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
for volume in volumes {
|
for volume in volumes {
|
||||||
let path = format!("images/{}/volume_{:0>3}", title.en, volume);
|
let image_paths = format!("images/{}/volume_{:0>3}", title.en, volume);
|
||||||
let file_name = format!("{} - Volume {:0>3}.cbz", title.en, volume);
|
let image_paths = Path::new(&image_paths);
|
||||||
std::process::Command::new("/usr/bin/zip")
|
|
||||||
.args(["-j", "-r", &file_name, &path])
|
let zip_file_path = format!("{} - Volume {:0>3}.cbz", title.en, volume);
|
||||||
.spawn()
|
let zip_file_path = Path::new(&zip_file_path);
|
||||||
.unwrap()
|
let zip_file = File::create(&zip_file_path).unwrap();
|
||||||
.wait()
|
|
||||||
|
let mut zip = ZipWriter::new(zip_file);
|
||||||
|
let options =
|
||||||
|
SimpleFileOptions::default().compression_method(CompressionMethod::Deflated);
|
||||||
|
|
||||||
|
for entry in std::fs::read_dir(image_paths).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
zip.start_file(entry.file_name().to_str().unwrap(), options)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
let buffer = std::fs::read(entry.path()).unwrap();
|
||||||
|
zip.write_all(&buffer).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.finish().unwrap();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for chapter in selected_chapters {
|
||||||
|
let chapter = chapter.attributes.chapter.unwrap();
|
||||||
|
let image_paths = format!("images/{}", title.en);
|
||||||
|
let image_paths = Path::new(&image_paths);
|
||||||
|
|
||||||
|
let zip_file_path = format!("{} - Chapter {:0>3}.cbz", title.en, chapter);
|
||||||
|
let zip_file_path = Path::new(&zip_file_path);
|
||||||
|
let zip_file = File::create(&zip_file_path).unwrap();
|
||||||
|
|
||||||
|
let mut zip = ZipWriter::new(zip_file);
|
||||||
|
let options =
|
||||||
|
SimpleFileOptions::default().compression_method(CompressionMethod::Deflated);
|
||||||
|
|
||||||
|
for entry in std::fs::read_dir(image_paths).unwrap() {
|
||||||
|
let entry = entry.unwrap();
|
||||||
|
if entry.path().is_dir() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
zip.start_file(entry.file_name().to_str().unwrap(), options)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let buffer = std::fs::read(entry.path()).unwrap();
|
||||||
|
zip.write_all(&buffer).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
zip.finish().unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -289,7 +324,7 @@ async fn download_chapter_images(
|
|||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
println!(
|
println!(
|
||||||
"Downloaded volume: {:?}, chapter: {:?}, title: {}, [{}/{}]",
|
"\x1b[1A\x1b[2KDownloaded volume: {:?}, chapter: {:?}, title: {}, [{}/{}]",
|
||||||
chapter.attributes.volume,
|
chapter.attributes.volume,
|
||||||
chapter.attributes.chapter,
|
chapter.attributes.chapter,
|
||||||
chapter.attributes.title,
|
chapter.attributes.title,
|
||||||
@@ -356,3 +391,80 @@ async fn search(
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
response_deserializer::deserializer(&json)
|
response_deserializer::deserializer(&json)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn select_manga_from_search(
|
||||||
|
client: &Client,
|
||||||
|
config: &util::Config,
|
||||||
|
results: &SearchResult,
|
||||||
|
) -> u16 {
|
||||||
|
let cover_ex = match config.cover_size {
|
||||||
|
util::CoverSize::Full => "",
|
||||||
|
util::CoverSize::W256 => ".256.jpg",
|
||||||
|
util::CoverSize::W512 => ".512.jpg",
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut entry_futures = Vec::new();
|
||||||
|
for result in results.data.iter() {
|
||||||
|
let mut entry = Entry::new(result.attributes.title.en.clone());
|
||||||
|
if let Some(year) = result.attributes.year {
|
||||||
|
entry.add_info("year", year);
|
||||||
|
}
|
||||||
|
let id = result.id.to_string();
|
||||||
|
entry.add_info("id", &id);
|
||||||
|
entry.add_info("status", result.attributes.status.to_string());
|
||||||
|
entry.add_info(
|
||||||
|
"content rating",
|
||||||
|
result.attributes.content_rating.to_string(),
|
||||||
|
);
|
||||||
|
if let Some(chapters) = result.attributes.last_chapter {
|
||||||
|
entry.add_info("chapters", chapters);
|
||||||
|
}
|
||||||
|
if let Some(volumes) = result.attributes.last_volume {
|
||||||
|
entry.add_info("volumes", volumes);
|
||||||
|
}
|
||||||
|
if let Some(cover_data) = &result.relationships[2].attributes {
|
||||||
|
// The lib used for converting to sixel is abysmally slow for larger images, this
|
||||||
|
// should be in a future to allow for multithreaded work
|
||||||
|
let future = async move {
|
||||||
|
let image_url = format!(
|
||||||
|
"https://uploads.mangadex.org/covers/{id}/{}{cover_ex}",
|
||||||
|
&cover_data.file_name
|
||||||
|
);
|
||||||
|
let data = client
|
||||||
|
.get(&image_url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.bytes()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
let result = util::convert_to_sixel(&data);
|
||||||
|
|
||||||
|
entry.set_image(result);
|
||||||
|
entry
|
||||||
|
};
|
||||||
|
entry_futures.push(future);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let entries = futures::future::join_all(entry_futures).await;
|
||||||
|
let choice = match select::select(&entries) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("ERROR: Failed to select: {:?}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
choice
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn id_query_get_info(client: &Client, id: &Id) -> response_deserializer::IdQueryResult {
|
||||||
|
let json = client
|
||||||
|
.get(format!("{BASE}/manga/{id}"))
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.text()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
response_deserializer::deserialize_id_query(&json)
|
||||||
|
}
|
||||||
|
|||||||
@@ -12,36 +12,243 @@ pub enum ResponseResult {
|
|||||||
Ok,
|
Ok,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://api.mangadex.org/docs/3-enumerations/
|
||||||
|
// https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
|
||||||
|
// https://www.loc.gov/standards/iso639-2/php/English_list.php
|
||||||
|
// This is a fucking mess, I hate this. I could have just used a library, but I would have to adapt
|
||||||
|
// it to the special cases which mangadex require.
|
||||||
|
// The two-letter codes are not unique, and the code esentially just choose the first one in the
|
||||||
|
// list. Why have identifiers if they are not unique? Who knows what they were smoking when making
|
||||||
|
// this. Also Why is there Bouth SouthNdebele and Ndebele, South? Who knows? Why is there Bokmål
|
||||||
|
// and Norwegian Bokmål, many questions to ask, but few answers to get. Best part of this is that
|
||||||
|
// the updated ISO pdf is behind a paywall. https://www.iso.org/standard/74575.html
|
||||||
|
// If the code works 98% of the time and is 95% correct I guess that is "good enough".
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Language {
|
pub enum Language {
|
||||||
Tagalog,
|
Abkhazian,
|
||||||
SimplifiedChinese,
|
Afar,
|
||||||
Greek,
|
Afrikaans,
|
||||||
Persian,
|
Akan,
|
||||||
TraditionalChinese,
|
Albanian,
|
||||||
Ukranian,
|
Amharic,
|
||||||
Romanian,
|
|
||||||
Arabic,
|
Arabic,
|
||||||
German,
|
Aragonese,
|
||||||
Vietnamese,
|
Armenian,
|
||||||
French,
|
Assamese,
|
||||||
Turkish,
|
Avaric,
|
||||||
Korean,
|
Avestan,
|
||||||
SpanishLatinAmerican,
|
Aymara,
|
||||||
Hungarian,
|
Azerbaijani,
|
||||||
|
Bambara,
|
||||||
|
Bashkir,
|
||||||
|
Basque,
|
||||||
|
Belarusian,
|
||||||
|
Bengali,
|
||||||
|
Bislama,
|
||||||
|
Bosnian,
|
||||||
BrazilianPortugese,
|
BrazilianPortugese,
|
||||||
English,
|
Breton,
|
||||||
Japanese,
|
|
||||||
JapaneseRomaji,
|
|
||||||
Italian,
|
|
||||||
Russian,
|
|
||||||
Indonesian,
|
|
||||||
Bulgarian,
|
Bulgarian,
|
||||||
Hebrew,
|
Burmese,
|
||||||
Spanish,
|
Castilian,
|
||||||
Esperanto,
|
CastilianSpanish,
|
||||||
Polish,
|
Catalan,
|
||||||
|
Central,
|
||||||
|
Chamorro,
|
||||||
|
Chechen,
|
||||||
|
Chewa,
|
||||||
|
Chichewa,
|
||||||
|
SimplifiedChinese,
|
||||||
|
TraditionalChinese,
|
||||||
|
Chuang,
|
||||||
|
ChurchSlavic,
|
||||||
|
ChurchSlavonic,
|
||||||
|
Chuvash,
|
||||||
|
Cornish,
|
||||||
|
Corsican,
|
||||||
|
Cree,
|
||||||
Croatian,
|
Croatian,
|
||||||
|
Czech,
|
||||||
|
Danish,
|
||||||
|
Dhivehi,
|
||||||
|
Divehi,
|
||||||
|
Dutch,
|
||||||
|
Dzongkha,
|
||||||
|
English,
|
||||||
|
Esperanto,
|
||||||
|
Estonian,
|
||||||
|
Ewe,
|
||||||
|
Faroese,
|
||||||
|
Fijian,
|
||||||
|
Finnish,
|
||||||
|
Flemish,
|
||||||
|
French,
|
||||||
|
Fulah,
|
||||||
|
Gaelic,
|
||||||
|
Galician,
|
||||||
|
Ganda,
|
||||||
|
Georgian,
|
||||||
|
German,
|
||||||
|
Gikuyu,
|
||||||
|
Greek,
|
||||||
|
Greenlandic,
|
||||||
|
Guarani,
|
||||||
|
Gujarati,
|
||||||
|
Gwic,
|
||||||
|
Haitian,
|
||||||
|
Hausa,
|
||||||
|
Hebrew,
|
||||||
|
Herero,
|
||||||
|
Hindi,
|
||||||
|
Hiri,
|
||||||
|
Hungarian,
|
||||||
|
Icelandic,
|
||||||
|
Ido,
|
||||||
|
Igbo,
|
||||||
|
Indonesian,
|
||||||
|
Interlingua,
|
||||||
|
Interlingue,
|
||||||
|
Inuktitut,
|
||||||
|
Inupiaq,
|
||||||
|
Irish,
|
||||||
|
Italian,
|
||||||
|
Japanese,
|
||||||
|
Javanese,
|
||||||
|
Kalaallisut,
|
||||||
|
Kannada,
|
||||||
|
Kanuri,
|
||||||
|
Kashmiri,
|
||||||
|
Kazakh,
|
||||||
|
Kikuyu,
|
||||||
|
Kinyarwanda,
|
||||||
|
Kirghiz,
|
||||||
|
Komi,
|
||||||
|
Kongo,
|
||||||
|
Korean,
|
||||||
|
Kuanyama,
|
||||||
|
Kurdish,
|
||||||
|
Kwanyama,
|
||||||
|
Kyrgyz,
|
||||||
|
Lao,
|
||||||
|
Latin,
|
||||||
|
LatinAmericanSpanish,
|
||||||
|
Latvian,
|
||||||
|
Letzeburgesch,
|
||||||
|
Limburgan,
|
||||||
|
Limburger,
|
||||||
|
Limburgish,
|
||||||
|
Lingala,
|
||||||
|
Lithuanian,
|
||||||
|
LubaKatanga,
|
||||||
|
Luxembourgish,
|
||||||
|
Macedonian,
|
||||||
|
Malagasy,
|
||||||
|
Malay,
|
||||||
|
Malayalam,
|
||||||
|
Maldivian,
|
||||||
|
Maltese,
|
||||||
|
Manx,
|
||||||
|
Maori,
|
||||||
|
Marathi,
|
||||||
|
Marshallese,
|
||||||
|
MiMoldavian,
|
||||||
|
Moldovan,
|
||||||
|
Mongolian,
|
||||||
|
NNauru,
|
||||||
|
Navaho,
|
||||||
|
Navajo,
|
||||||
|
NdebeleNorth,
|
||||||
|
NdebeleSouth,
|
||||||
|
Ndonga,
|
||||||
|
Nepali,
|
||||||
|
North,
|
||||||
|
Northern,
|
||||||
|
Norwegian,
|
||||||
|
NorwegianBokmål,
|
||||||
|
NorwegianNynorsk,
|
||||||
|
Nuosu,
|
||||||
|
Nyanja,
|
||||||
|
Nynors,
|
||||||
|
Occidental,
|
||||||
|
Occitan,
|
||||||
|
Ojibwa,
|
||||||
|
OldBulgarian,
|
||||||
|
OldChurchSlavonic,
|
||||||
|
OldSlavonic,
|
||||||
|
Oriya,
|
||||||
|
Oromo,
|
||||||
|
Ossetian,
|
||||||
|
Ossetic,
|
||||||
|
Pali,
|
||||||
|
Panjabi,
|
||||||
|
Pashto,
|
||||||
|
Persian,
|
||||||
|
Polish,
|
||||||
|
Portuguese,
|
||||||
|
ProvençPunjabi,
|
||||||
|
Pushto,
|
||||||
|
Quechua,
|
||||||
|
Romanian,
|
||||||
|
RomanizedJapanese,
|
||||||
|
RomanizedKorean,
|
||||||
|
RomanizedChinese,
|
||||||
|
Romansh,
|
||||||
|
Rundi,
|
||||||
|
Russian,
|
||||||
|
Samoan,
|
||||||
|
Sango,
|
||||||
|
Sanskrit,
|
||||||
|
Sardinian,
|
||||||
|
Scottish,
|
||||||
|
Serbian,
|
||||||
|
Shona,
|
||||||
|
Sichuan,
|
||||||
|
Sindhi,
|
||||||
|
Sinhala,
|
||||||
|
Sinhalese,
|
||||||
|
Slovak,
|
||||||
|
Slovenian,
|
||||||
|
Somali,
|
||||||
|
Sotho,
|
||||||
|
SouthNdebele,
|
||||||
|
Spanish,
|
||||||
|
Sundanese,
|
||||||
|
Swahili,
|
||||||
|
Swati,
|
||||||
|
Swedish,
|
||||||
|
Tagalog,
|
||||||
|
Tahitian,
|
||||||
|
Tajik,
|
||||||
|
Tamil,
|
||||||
|
Tatar,
|
||||||
|
Telugu,
|
||||||
|
Thai,
|
||||||
|
Tibetan,
|
||||||
|
Tigrinya,
|
||||||
|
Tonga,
|
||||||
|
Tsonga,
|
||||||
|
Tswana,
|
||||||
|
Turkish,
|
||||||
|
Turkmen,
|
||||||
|
Twi,
|
||||||
|
Uighur,
|
||||||
|
Ukrainian,
|
||||||
|
Urdu,
|
||||||
|
Uyghur,
|
||||||
|
Uzbek,
|
||||||
|
Valencian,
|
||||||
|
Venda,
|
||||||
|
Vietnamese,
|
||||||
|
Volapük,
|
||||||
|
Walloon,
|
||||||
|
Welsh,
|
||||||
|
Western,
|
||||||
|
Wolof,
|
||||||
|
Xhosa,
|
||||||
|
Yiddish,
|
||||||
|
Yoruba,
|
||||||
|
Zhuang,
|
||||||
|
Zulu,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -76,6 +283,7 @@ pub enum State {
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Response {
|
pub enum Response {
|
||||||
Collection,
|
Collection,
|
||||||
|
Entity,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -99,6 +307,13 @@ pub struct SearchResult {
|
|||||||
pub data: Vec<Manga>,
|
pub data: Vec<Manga>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct IdQueryResult {
|
||||||
|
pub result: ResponseResult,
|
||||||
|
pub response: Response,
|
||||||
|
pub data: Manga,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
struct ChapterImagesContent {
|
struct ChapterImagesContent {
|
||||||
@@ -216,6 +431,7 @@ pub struct Chapter {
|
|||||||
pub relationships: Vec<ChapterRelationShip>,
|
pub relationships: Vec<ChapterRelationShip>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ChapterAttributes {
|
pub struct ChapterAttributes {
|
||||||
pub volume: Option<u32>,
|
pub volume: Option<u32>,
|
||||||
pub chapter: Option<f32>,
|
pub chapter: Option<f32>,
|
||||||
@@ -247,6 +463,13 @@ struct SearchResponse {
|
|||||||
total: u32,
|
total: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct IdQueryResponse {
|
||||||
|
result: String,
|
||||||
|
response: String,
|
||||||
|
data: ContentData,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct ContentData {
|
struct ContentData {
|
||||||
id: String,
|
id: String,
|
||||||
@@ -430,34 +653,232 @@ impl TryInto<Language> for &str {
|
|||||||
|
|
||||||
fn try_into(self) -> Result<Language, ()> {
|
fn try_into(self) -> Result<Language, ()> {
|
||||||
Ok(match self {
|
Ok(match self {
|
||||||
"ja" => Language::Japanese,
|
"ab" => Language::Abkhazian,
|
||||||
"ja-ro" => Language::JapaneseRomaji,
|
"aa" => Language::Afar,
|
||||||
"en" => Language::English,
|
"af" => Language::Afrikaans,
|
||||||
"ru" => Language::Russian,
|
"ak" => Language::Akan,
|
||||||
"pt-br" => Language::BrazilianPortugese,
|
"sq" => Language::Albanian,
|
||||||
"tr" => Language::Turkish,
|
"am" => Language::Amharic,
|
||||||
"it" => Language::Italian,
|
|
||||||
"es-la" => Language::SpanishLatinAmerican,
|
|
||||||
"hu" => Language::Hungarian,
|
|
||||||
"bg" => Language::Bulgarian,
|
|
||||||
"id" => Language::Indonesian,
|
|
||||||
"he" => Language::Hebrew,
|
|
||||||
"es" => Language::Spanish,
|
|
||||||
"eo" => Language::Esperanto,
|
|
||||||
"pl" => Language::Polish,
|
|
||||||
"ko" => Language::Korean,
|
|
||||||
"fr" => Language::French,
|
|
||||||
"vi" => Language::Vietnamese,
|
|
||||||
"de" => Language::German,
|
|
||||||
"ar" => Language::Arabic,
|
"ar" => Language::Arabic,
|
||||||
"ro" => Language::Romanian,
|
"an" => Language::Aragonese,
|
||||||
"uk" => Language::Ukranian,
|
"hy" => Language::Armenian,
|
||||||
"zh-hk" => Language::TraditionalChinese,
|
"as" => Language::Assamese,
|
||||||
"fa" => Language::Persian,
|
"av" => Language::Avaric,
|
||||||
"el" => Language::Greek,
|
"ae" => Language::Avestan,
|
||||||
|
"ay" => Language::Aymara,
|
||||||
|
"az" => Language::Azerbaijani,
|
||||||
|
"bm" => Language::Bambara,
|
||||||
|
"ba" => Language::Bashkir,
|
||||||
|
"eu" => Language::Basque,
|
||||||
|
"be" => Language::Belarusian,
|
||||||
|
"bn" => Language::Bengali,
|
||||||
|
"bi" => Language::Bislama,
|
||||||
|
"nb" => Language::NorwegianBokmål,
|
||||||
|
"bs" => Language::Bosnian,
|
||||||
|
"br" => Language::Breton,
|
||||||
|
"bg" => Language::Bulgarian,
|
||||||
|
"my" => Language::Burmese,
|
||||||
|
"es" => Language::Castilian,
|
||||||
|
"ca" => Language::Catalan,
|
||||||
|
"km" => Language::Central,
|
||||||
|
"ch" => Language::Chamorro,
|
||||||
|
"ce" => Language::Chechen,
|
||||||
|
"ny" => Language::Chewa,
|
||||||
|
"ny" => Language::Chichewa,
|
||||||
"zh" => Language::SimplifiedChinese,
|
"zh" => Language::SimplifiedChinese,
|
||||||
"tl" => Language::Tagalog,
|
"za" => Language::Chuang,
|
||||||
|
"cu" => Language::ChurchSlavic,
|
||||||
|
"cu" => Language::ChurchSlavonic,
|
||||||
|
"cv" => Language::Chuvash,
|
||||||
|
"kw" => Language::Cornish,
|
||||||
|
"co" => Language::Corsican,
|
||||||
|
"cr" => Language::Cree,
|
||||||
"hr" => Language::Croatian,
|
"hr" => Language::Croatian,
|
||||||
|
"cs" => Language::Czech,
|
||||||
|
"da" => Language::Danish,
|
||||||
|
"dv" => Language::Dhivehi,
|
||||||
|
"dv" => Language::Divehi,
|
||||||
|
"nl" => Language::Dutch,
|
||||||
|
"dz" => Language::Dzongkha,
|
||||||
|
"en" => Language::English,
|
||||||
|
"eo" => Language::Esperanto,
|
||||||
|
"et" => Language::Estonian,
|
||||||
|
"ee" => Language::Ewe,
|
||||||
|
"fo" => Language::Faroese,
|
||||||
|
"fj" => Language::Fijian,
|
||||||
|
"fi" => Language::Finnish,
|
||||||
|
"nl" => Language::Flemish,
|
||||||
|
"fr" => Language::French,
|
||||||
|
"ff" => Language::Fulah,
|
||||||
|
"gd" => Language::Gaelic,
|
||||||
|
"gl" => Language::Galician,
|
||||||
|
"lg" => Language::Ganda,
|
||||||
|
"ka" => Language::Georgian,
|
||||||
|
"de" => Language::German,
|
||||||
|
"ki" => Language::Gikuyu,
|
||||||
|
"el" => Language::Greek,
|
||||||
|
"kl" => Language::Greenlandic,
|
||||||
|
"gn" => Language::Guarani,
|
||||||
|
"gu" => Language::Gujarati,
|
||||||
|
"ht" => Language::Haitian,
|
||||||
|
"ha" => Language::Hausa,
|
||||||
|
"he" => Language::Hebrew,
|
||||||
|
"hz" => Language::Herero,
|
||||||
|
"hi" => Language::Hindi,
|
||||||
|
"ho" => Language::Hiri,
|
||||||
|
"hu" => Language::Hungarian,
|
||||||
|
"is" => Language::Icelandic,
|
||||||
|
"io" => Language::Ido,
|
||||||
|
"ig" => Language::Igbo,
|
||||||
|
"id" => Language::Indonesian,
|
||||||
|
"ia" => Language::Interlingua,
|
||||||
|
"ie" => Language::Interlingue,
|
||||||
|
"iu" => Language::Inuktitut,
|
||||||
|
"ik" => Language::Inupiaq,
|
||||||
|
"ga" => Language::Irish,
|
||||||
|
"it" => Language::Italian,
|
||||||
|
"ja" => Language::Japanese,
|
||||||
|
"jv" => Language::Javanese,
|
||||||
|
"kl" => Language::Kalaallisut,
|
||||||
|
"kn" => Language::Kannada,
|
||||||
|
"kr" => Language::Kanuri,
|
||||||
|
"ks" => Language::Kashmiri,
|
||||||
|
"kk" => Language::Kazakh,
|
||||||
|
"ki" => Language::Kikuyu,
|
||||||
|
"rw" => Language::Kinyarwanda,
|
||||||
|
"ky" => Language::Kirghiz,
|
||||||
|
"kv" => Language::Komi,
|
||||||
|
"kg" => Language::Kongo,
|
||||||
|
"ko" => Language::Korean,
|
||||||
|
"kj" => Language::Kuanyama,
|
||||||
|
"ku" => Language::Kurdish,
|
||||||
|
"kj" => Language::Kwanyama,
|
||||||
|
"ky" => Language::Kyrgyz,
|
||||||
|
"lo" => Language::Lao,
|
||||||
|
"la" => Language::Latin,
|
||||||
|
"lv" => Language::Latvian,
|
||||||
|
"lb" => Language::Letzeburgesch,
|
||||||
|
"li" => Language::Limburgan,
|
||||||
|
"li" => Language::Limburger,
|
||||||
|
"li" => Language::Limburgish,
|
||||||
|
"ln" => Language::Lingala,
|
||||||
|
"lt" => Language::Lithuanian,
|
||||||
|
"lu" => Language::LubaKatanga,
|
||||||
|
"lb" => Language::Luxembourgish,
|
||||||
|
"mk" => Language::Macedonian,
|
||||||
|
"mg" => Language::Malagasy,
|
||||||
|
"ms" => Language::Malay,
|
||||||
|
"ml" => Language::Malayalam,
|
||||||
|
"dv" => Language::Maldivian,
|
||||||
|
"mt" => Language::Maltese,
|
||||||
|
"gv" => Language::Manx,
|
||||||
|
"mi" => Language::Maori,
|
||||||
|
"mr" => Language::Marathi,
|
||||||
|
"mh" => Language::Marshallese,
|
||||||
|
"ro" => Language::MiMoldavian,
|
||||||
|
"ro" => Language::Moldovan,
|
||||||
|
"mn" => Language::Mongolian,
|
||||||
|
"na" => Language::NNauru,
|
||||||
|
"nv" => Language::Navaho,
|
||||||
|
"nv" => Language::Navajo,
|
||||||
|
"nd" => Language::NdebeleNorth,
|
||||||
|
"nr" => Language::NdebeleSouth,
|
||||||
|
"ng" => Language::Ndonga,
|
||||||
|
"ne" => Language::Nepali,
|
||||||
|
"nd" => Language::North,
|
||||||
|
"se" => Language::Northern,
|
||||||
|
"no" => Language::Norwegian,
|
||||||
|
"nb" => Language::NorwegianBokmål,
|
||||||
|
"nn" => Language::NorwegianNynorsk,
|
||||||
|
"ii" => Language::Nuosu,
|
||||||
|
"ny" => Language::Nyanja,
|
||||||
|
"nn" => Language::NorwegianNynorsk,
|
||||||
|
"ie" => Language::Occidental,
|
||||||
|
"oc" => Language::Occitan,
|
||||||
|
"oj" => Language::Ojibwa,
|
||||||
|
"cu" => Language::OldBulgarian,
|
||||||
|
"cu" => Language::OldChurchSlavonic,
|
||||||
|
"cu" => Language::OldSlavonic,
|
||||||
|
"or" => Language::Oriya,
|
||||||
|
"om" => Language::Oromo,
|
||||||
|
"os" => Language::Ossetian,
|
||||||
|
"os" => Language::Ossetic,
|
||||||
|
"pi" => Language::Pali,
|
||||||
|
"pa" => Language::Panjabi,
|
||||||
|
"ps" => Language::Pashto,
|
||||||
|
"fa" => Language::Persian,
|
||||||
|
"pl" => Language::Polish,
|
||||||
|
"pt" => Language::Portuguese,
|
||||||
|
"pa" => Language::ProvençPunjabi,
|
||||||
|
"ps" => Language::Pushto,
|
||||||
|
"qu" => Language::Quechua,
|
||||||
|
"ro" => Language::Romanian,
|
||||||
|
"rm" => Language::Romansh,
|
||||||
|
"rn" => Language::Rundi,
|
||||||
|
"ru" => Language::Russian,
|
||||||
|
"sm" => Language::Samoan,
|
||||||
|
"sg" => Language::Sango,
|
||||||
|
"sa" => Language::Sanskrit,
|
||||||
|
"sc" => Language::Sardinian,
|
||||||
|
"gd" => Language::Scottish,
|
||||||
|
"sr" => Language::Serbian,
|
||||||
|
"sn" => Language::Shona,
|
||||||
|
"ii" => Language::Sichuan,
|
||||||
|
"sd" => Language::Sindhi,
|
||||||
|
"si" => Language::Sinhala,
|
||||||
|
"si" => Language::Sinhalese,
|
||||||
|
"sk" => Language::Slovak,
|
||||||
|
"sl" => Language::Slovenian,
|
||||||
|
"so" => Language::Somali,
|
||||||
|
"st" => Language::Sotho,
|
||||||
|
"es" => Language::Spanish,
|
||||||
|
"su" => Language::Sundanese,
|
||||||
|
"sw" => Language::Swahili,
|
||||||
|
"ss" => Language::Swati,
|
||||||
|
"sv" => Language::Swedish,
|
||||||
|
"tl" => Language::Tagalog,
|
||||||
|
"ty" => Language::Tahitian,
|
||||||
|
"tg" => Language::Tajik,
|
||||||
|
"ta" => Language::Tamil,
|
||||||
|
"tt" => Language::Tatar,
|
||||||
|
"te" => Language::Telugu,
|
||||||
|
"th" => Language::Thai,
|
||||||
|
"bo" => Language::Tibetan,
|
||||||
|
"ti" => Language::Tigrinya,
|
||||||
|
"to" => Language::Tonga,
|
||||||
|
"ts" => Language::Tsonga,
|
||||||
|
"tn" => Language::Tswana,
|
||||||
|
"tr" => Language::Turkish,
|
||||||
|
"tk" => Language::Turkmen,
|
||||||
|
"tw" => Language::Twi,
|
||||||
|
"ug" => Language::Uighur,
|
||||||
|
"uk" => Language::Ukrainian,
|
||||||
|
"ur" => Language::Urdu,
|
||||||
|
"ug" => Language::Uyghur,
|
||||||
|
"uz" => Language::Uzbek,
|
||||||
|
"ca" => Language::Valencian,
|
||||||
|
"ve" => Language::Venda,
|
||||||
|
"vi" => Language::Vietnamese,
|
||||||
|
"vo" => Language::Volapük,
|
||||||
|
"wa" => Language::Walloon,
|
||||||
|
"cy" => Language::Welsh,
|
||||||
|
"fy" => Language::Western,
|
||||||
|
"wo" => Language::Wolof,
|
||||||
|
"xh" => Language::Xhosa,
|
||||||
|
"yi" => Language::Yiddish,
|
||||||
|
"yo" => Language::Yoruba,
|
||||||
|
"za" => Language::Zhuang,
|
||||||
|
"zu" => Language::Zulu,
|
||||||
|
|
||||||
|
"zh-ro" => Language::RomanizedChinese,
|
||||||
|
"zh" => Language::SimplifiedChinese,
|
||||||
|
"zh-hk" => Language::TraditionalChinese,
|
||||||
|
"pt" => Language::Portuguese,
|
||||||
|
"es" => Language::CastilianSpanish,
|
||||||
|
"es-la" => Language::LatinAmericanSpanish,
|
||||||
|
"ja-ro" => Language::RomanizedJapanese,
|
||||||
|
"pt-br" => Language::BrazilianPortugese,
|
||||||
|
|
||||||
_ => return Err(()),
|
_ => return Err(()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -523,6 +944,7 @@ impl TryInto<Response> for &str {
|
|||||||
fn try_into(self) -> Result<Response, ()> {
|
fn try_into(self) -> Result<Response, ()> {
|
||||||
match self {
|
match self {
|
||||||
"collection" => Ok(Response::Collection),
|
"collection" => Ok(Response::Collection),
|
||||||
|
"entity" => Ok(Response::Entity),
|
||||||
_ => Err(()),
|
_ => Err(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -781,6 +1203,29 @@ fn convert_attributes(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *****
|
||||||
|
pub fn deserialize_id_query(json: &str) -> IdQueryResult {
|
||||||
|
let id_query_response: IdQueryResponse = match serde_json::from_str(json) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
std::fs::write("out.json", json).unwrap();
|
||||||
|
eprintln!("ERROR: {:#?}", e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
convert_some_test(id_query_response).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_some_test(input: IdQueryResponse) -> Result<IdQueryResult, AttributeConversionError> {
|
||||||
|
Ok(IdQueryResult {
|
||||||
|
result: input.result.as_str().try_into().unwrap(),
|
||||||
|
response: input.response.as_str().try_into().unwrap(),
|
||||||
|
data: convert_data_to_manga(input.data).unwrap(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// *****
|
||||||
|
|
||||||
pub fn deserialize_chapter_feed(json: &str) -> ChapterFeed {
|
pub fn deserialize_chapter_feed(json: &str) -> ChapterFeed {
|
||||||
let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) {
|
let chapter_feed_response: ChapterFeedResponse = match serde_json::from_str(json) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
@@ -957,3 +1402,61 @@ pub fn deserialize_chapter_images(json: &str) -> Result<ChapterImages, ChapterIm
|
|||||||
};
|
};
|
||||||
Ok(convert_chapter_images(chapter_images).unwrap())
|
Ok(convert_chapter_images(chapter_images).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn convert_data_to_manga(m: ContentData) -> Result<Manga, ResponseConversionError> {
|
||||||
|
Ok(Manga {
|
||||||
|
id: Id(m.id.clone()),
|
||||||
|
data_type: (m.type_name.as_str()).try_into().map_err(|_| {
|
||||||
|
ResponseConversionError::AttributeError(AttributeConversionError::DataType(
|
||||||
|
m.type_name.clone(),
|
||||||
|
))
|
||||||
|
})?,
|
||||||
|
attributes: convert_attributes(&m.attributes)
|
||||||
|
.map_err(ResponseConversionError::AttributeError)?,
|
||||||
|
relationships: m
|
||||||
|
.relationships
|
||||||
|
.iter()
|
||||||
|
.map(|m| {
|
||||||
|
Ok(RelationShip {
|
||||||
|
id: Id(m.id.clone()),
|
||||||
|
data_type: (m.type_name.as_str())
|
||||||
|
.try_into()
|
||||||
|
.map_err(|_| AttributeConversionError::DataType(m.type_name.clone()))?,
|
||||||
|
attributes: {
|
||||||
|
if let Some(attributes) = &m.attributes {
|
||||||
|
Some(CoverAttributes {
|
||||||
|
created_at: DateTime::parse_from_rfc3339(&attributes.created_at)
|
||||||
|
.map_err(|_| {
|
||||||
|
AttributeConversionError::CreatedAtDateTime(
|
||||||
|
attributes.created_at.clone(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
updated_at: DateTime::parse_from_rfc3339(&attributes.created_at)
|
||||||
|
.map_err(|_| {
|
||||||
|
AttributeConversionError::CreatedAtDateTime(
|
||||||
|
attributes.created_at.clone(),
|
||||||
|
)
|
||||||
|
})?,
|
||||||
|
// TODO: Something should probably be done here
|
||||||
|
description: String::new(),
|
||||||
|
file_name: Id(attributes.file_name.clone()),
|
||||||
|
locale: (attributes.locale.as_str()).try_into().map_err(|_| {
|
||||||
|
AttributeConversionError::Locale(attributes.locale.clone())
|
||||||
|
})?,
|
||||||
|
version: attributes.version,
|
||||||
|
volume: match &attributes.volume {
|
||||||
|
Some(v) => v.parse().ok(),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
related: m.related.clone(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<RelationShip>, AttributeConversionError>>()
|
||||||
|
.map_err(ResponseConversionError::AttributeError)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
33
src/util.rs
33
src/util.rs
@@ -320,11 +320,28 @@ pub fn args() -> Config {
|
|||||||
let mut config = Config::new();
|
let mut config = Config::new();
|
||||||
while args.len() != 0 {
|
while args.len() != 0 {
|
||||||
match args.next().unwrap().as_ref() {
|
match args.next().unwrap().as_ref() {
|
||||||
|
"--selection-range" => {
|
||||||
|
if config.selection_range.is_some() {
|
||||||
|
eprintln!("Value selection range already set.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
config.selection_range = match args.next() {
|
||||||
|
Some(selection_type) => Some(selection_type),
|
||||||
|
None => {
|
||||||
|
eprintln!("Missing value for selection range, type: String");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
"-t" | "--selection-type" => {
|
"-t" | "--selection-type" => {
|
||||||
|
if config.selection_type.is_some() {
|
||||||
|
eprintln!("Value selection type already set.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
config.selection_type = match args.next() {
|
config.selection_type = match args.next() {
|
||||||
Some(selection_type) => Some(match selection_type.as_str() {
|
Some(selection_type) => Some(match selection_type.as_str() {
|
||||||
"volume" => ConfigSelectionType::Volume,
|
"v" | "volume" => ConfigSelectionType::Volume,
|
||||||
"chapter" => ConfigSelectionType::Chapter,
|
"c" | "chapter" => ConfigSelectionType::Chapter,
|
||||||
_ => {
|
_ => {
|
||||||
eprintln!("Invalid value for selection type, type: SelectionType");
|
eprintln!("Invalid value for selection type, type: SelectionType");
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
@@ -337,6 +354,10 @@ pub fn args() -> Config {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
"-i" | "--id" => {
|
"-i" | "--id" => {
|
||||||
|
if config.search.is_some() {
|
||||||
|
eprintln!("Conflicting arguments for search.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
if let Some(id) = args.next() {
|
if let Some(id) = args.next() {
|
||||||
config.search = Some(ConfigSearch::Id(Id(id)));
|
config.search = Some(ConfigSearch::Id(Id(id)));
|
||||||
} else {
|
} else {
|
||||||
@@ -345,6 +366,10 @@ pub fn args() -> Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"-s" | "--search" => {
|
"-s" | "--search" => {
|
||||||
|
if config.search.is_some() {
|
||||||
|
eprintln!("Conflicting arguments for search.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
if let Some(query) = args.next() {
|
if let Some(query) = args.next() {
|
||||||
config.search = Some(ConfigSearch::Query(query));
|
config.search = Some(ConfigSearch::Query(query));
|
||||||
} else {
|
} else {
|
||||||
@@ -353,6 +378,10 @@ pub fn args() -> Config {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"-b" | "--bonus" => {
|
"-b" | "--bonus" => {
|
||||||
|
if config.bonus.is_some() {
|
||||||
|
eprintln!("Value bonus already set.");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
config.bonus = match args.next() {
|
config.bonus = match args.next() {
|
||||||
Some(a) => Some(match a.as_str() {
|
Some(a) => Some(match a.as_str() {
|
||||||
"true" => true,
|
"true" => true,
|
||||||
|
|||||||
Reference in New Issue
Block a user