diff --git a/Cargo.lock b/Cargo.lock index 5aa212c..30145eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2603,7 +2603,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.14", ] [[package]] @@ -3188,9 +3188,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" dependencies = [ "memchr", ] diff --git a/rustfmt.toml b/rustfmt.toml index 7ede0c1..f574633 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -2,7 +2,7 @@ unstable_features = true # Width formatting options -max_width = 188 +max_width = 190 # General formatting options trailing_comma = "Never" diff --git a/src/commands/fun/xkcd.rs b/src/commands/fun/xkcd.rs index 96cf717..b271184 100644 --- a/src/commands/fun/xkcd.rs +++ b/src/commands/fun/xkcd.rs @@ -14,16 +14,16 @@ struct XkcdComic { /// Retrieves the latest or a specific comic from xkcd. #[command(slash_command)] -pub async fn xkcd(ctx: Context<'_>, #[description = "The specific comic no. to retrieve."] number: Option) -> Result<(), Error> { +pub async fn xkcd(context: Context<'_>, #[description = "The specific comic no. to retrieve."] number: Option) -> Result<(), Error> { let comic = match number { None => "https://xkcd.com/info.0.json", Some(number) => &format!("https://xkcd.com/{number}/info.0.json") }; - let client = &ctx.data().reqwest_container; + let client = &context.data().reqwest_container; let request = client.get(comic).send().await?; if request.status() == StatusCode::NOT_FOUND { - ctx.reply("You did not provide a valid comic id.").await?; + context.reply("You did not provide a valid comic id.").await?; return Ok(()); } @@ -40,7 +40,7 @@ pub async fn xkcd(ctx: Context<'_>, #[description = "The specific comic no. to r .footer(CreateEmbedFooter::new(format!("xkcd comic no. {num}"))); let links = CreateActionRow::Buttons(vec![CreateButton::new_link(page).label("View on xkcd"), CreateButton::new_link(wiki).label("View wiki")]); - ctx.send(poise::CreateReply::default().embed(embed).components(vec![links])).await?; + context.send(poise::CreateReply::default().embed(embed).components(vec![links])).await?; Ok(()) } diff --git a/src/commands/search/tmdb/mod.rs b/src/commands/search/tmdb/mod.rs index dc181cd..c44e133 100644 --- a/src/commands/search/tmdb/mod.rs +++ b/src/commands/search/tmdb/mod.rs @@ -1,8 +1,15 @@ -use crate::{Context, Error}; +use crate::{ + models::tmdb::Movie, + utils::{format_int, locale}, + Context, Error +}; use chrono::NaiveDate; +use humantime::format_duration; +use itertools::Itertools; use poise::CreateReply; use serde::Deserialize; -use serenity::all::{CreateActionRow, CreateButton, CreateEmbed}; +use serenity::all::{CreateActionRow, CreateButton, CreateEmbed, CreateEmbedFooter}; +use std::time::Duration; #[derive(Deserialize, Debug)] pub struct SearchResponse { @@ -30,22 +37,22 @@ pub struct SimplifiedMovie { pub title: String // The title of the movie. } -#[poise::command(slash_command, subcommands("collection"))] -pub async fn tmdb(_ctx: Context<'_>) -> Result<(), Error> { +#[poise::command(slash_command, subcommands("collection", "movie"))] +pub async fn tmdb(_context: Context<'_>) -> Result<(), Error> { Ok(()) } /// Retrieves information about a collection on TMDb. #[poise::command(slash_command)] -pub async fn collection(ctx: Context<'_>, #[description = "The name of the collection."] name: String) -> Result<(), Error> { - let data = &ctx.data(); +pub async fn collection(context: Context<'_>, #[description = "The name of the collection."] name: String) -> Result<(), Error> { + let data = &context.data(); let client = &data.reqwest_container; let api_key = &data.config.api.entertainment.tmdb; let search_response = client.get("https://api.themoviedb.org/3/search/collection").query(&[("api_key", api_key), ("query", &name)]); let search_result: SearchResponse = search_response.send().await?.json().await?; let search_results = search_result.results; if search_results.is_empty() { - ctx.reply(format!("Nothing found for `{name}`. Please try another name.")).await?; + context.reply(format!("Nothing found for `{name}`. Please try another name.")).await?; return Ok(()); } @@ -76,7 +83,120 @@ pub async fn collection(ctx: Context<'_>, #[description = "The name of the colle }).collect())).collect(); let embed = CreateEmbed::new().title(name).url(url).thumbnail(poster).color(0x0001_d277).description(overview).fields(fields); - ctx.send(CreateReply::default().embed(embed).components(rows)).await?; + context.send(CreateReply::default().embed(embed).components(rows)).await?; + + Ok(()) +} + +/// Retrieves detailed information about a film from TMDb. +#[poise::command(slash_command)] +pub async fn movie(context: Context<'_>, #[description = "The film name."] name: String, #[description = "The film release year."] year: Option) -> Result<(), Error> { + let data = &context.data(); + let api_key = &data.config.api.entertainment.tmdb; + let client = &data.reqwest_container; + let endpoint = "https://api.themoviedb.org/3/search/movie"; + let response = match year { + Some(year) => client.get(endpoint).query(&[("api_key", api_key), ("query", &name), ("year", &year.to_string())]), + None => client.get(endpoint).query(&[("api_key", api_key), ("query", &name)]) + }; + + let result: SearchResponse = response.send().await?.json().await?; + let results = result.results; + if results.is_empty() { + context.say(format!("No results found for `{name}`. Please try looking for another movie.")).await?; + } + + let id = results.first().unwrap().id; + let endpoint = format!("https://api.themoviedb.org/3/movie/{id}"); + let response = client.get(&endpoint).query(&[("api_key", &api_key)]).send().await?; + let result: Movie = response.json().await?; + + let tagline = match result.tagline { + Some(tagline) => { + if tagline.is_empty() { + String::new() + } else { + format!("*{tagline}*") + } + } + None => String::new() + }; + + let overview = match result.overview { + Some(overview) => { + if !tagline.is_empty() { + format!("\n\n{overview}") + } else { + overview + } + } + None => String::new() + }; + + let studios = if result.production_companies.is_empty() { + "No Known Studios".to_string() + } else { + result.production_companies.iter().map(|c| &c.name).join("\n") + }; + + let collection = match result.belongs_to_collection { + Some(collection) => collection.name, + None => "N/A".to_string() + }; + + let homepage = match result.homepage { + Some(homepage) => { + if homepage.is_empty() { + "No Website".to_string() + } else { + format!("[Website]({homepage})") + } + } + None => "No Website".to_string() + }; + + let id = result.id.to_string(); + let title = result.title.as_str(); + let status = result.status; + let language = locale::get_language_name_from_iso(&result.original_language).to_string(); + let release_date = result.release_date.unwrap().format("%B %e, %Y").to_string(); + let budget = format_int(result.budget); + let revenue = format_int(result.revenue); + let imdb = format!("[IMDb](https://www.imdb.com/title/{})", result.imdb_id.unwrap()); + let url = format!("https://www.themoviedb.org/movie/{id}"); + let genres = result.genres.iter().map(|g| &g.name).join("\n"); + let popularity = format!("{}%", result.popularity.round()); + let poster_uri = result.poster_path.unwrap(); + let poster = format!("https://image.tmdb.org/t/p/original/{}", &poster_uri.replace('/', "")); + let user_score = format!("{}/100", (result.vote_average * 10.0).round()); + let user_score_count = result.vote_count; + let runtime = format_duration(Duration::from_secs(result.runtime.unwrap() * 60)).to_string(); + let external_links = format!("{homepage} | {imdb}"); + + let embed = CreateEmbed::new() + .title(title) + .url(url) + .color(0x01b4e4) + .thumbnail(poster) + .description(format!("{tagline}{overview}")) + .fields(vec![ + ("Status", status, true), + ("Film ID", id, true), + ("Language", language, true), + ("Runtime", runtime, true), + ("Release Date", release_date, true), + ("Collection", collection, true), + ("Popularity", popularity, true), + ("User Score", format!("{user_score} ({user_score_count} votes)"), true), + ("Budget", format!("${budget}"), true), + ("Box Office", format!("${revenue}"), true), + ("Genres", genres, true), + ("Studios", studios, true), + ("External Links", external_links, false), + ]) + .footer(CreateEmbedFooter::new("Powered by the The Movie Database API.")); + + context.send(CreateReply::default().embed(embed)).await?; Ok(()) } diff --git a/src/commands/utilities/mod.rs b/src/commands/utilities/mod.rs index 0ff914d..85c8746 100644 --- a/src/commands/utilities/mod.rs +++ b/src/commands/utilities/mod.rs @@ -3,22 +3,22 @@ use poise::command; /// Shows the help menu. #[command(slash_command, track_edits)] -pub async fn help(ctx: Context<'_>, #[description = "Specific command to show help about"] command: Option) -> Result<(), Error> { +pub async fn help(context: Context<'_>, #[description = "Specific command to show help about"] command: Option) -> Result<(), Error> { let config = poise::builtins::HelpConfiguration { ..Default::default() }; - poise::builtins::help(ctx, command.as_deref(), config).await?; + poise::builtins::help(context, command.as_deref(), config).await?; Ok(()) } /// Says hello to the user who initializes the command. #[command(slash_command)] -pub async fn hello(ctx: Context<'_>) -> Result<(), Error> { - ctx.say(format!("Hello, **{}**!", ctx.author().name)).await?; +pub async fn hello(context: Context<'_>) -> Result<(), Error> { + context.say(format!("Hello, **{}**!", context.author().name)).await?; Ok(()) } /// Posts a message containing a link to the bot's source code on GitHub. #[command(slash_command)] -pub async fn source(ctx: Context<'_>) -> Result<(), Error> { - ctx.reply("GitHub repository: ").await?; +pub async fn source(context: Context<'_>) -> Result<(), Error> { + context.reply("GitHub repository: ").await?; Ok(()) } diff --git a/src/main.rs b/src/main.rs index 77122d5..9304683 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + mod commands; mod config; mod constants; @@ -61,11 +63,11 @@ async fn main() -> Result<(), Error> { ], ..Default::default() }) - .setup(move |ctx, _ready, framework| { + .setup(move |context, _ready, framework| { Box::pin(async move { // register all commands upon startup to avoid having to register them // manually. - poise::builtins::register_globally(ctx, &framework.options().commands).await?; + poise::builtins::register_globally(context, &framework.options().commands).await?; Ok(Data { config: read_config("config.toml"), reqwest_container: Client::builder().user_agent(REQWEST_USER_AGENT).redirect(Policy::none()).build()? diff --git a/src/models/tmdb/mod.rs b/src/models/tmdb/mod.rs index 0cc8ac9..6245956 100644 --- a/src/models/tmdb/mod.rs +++ b/src/models/tmdb/mod.rs @@ -1,2 +1,157 @@ -pub mod movie; -pub mod show; +use chrono::prelude::NaiveDate; +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Movie { + pub adult: bool, // Whether or not the movie has an adult rating. + pub belongs_to_collection: Option, // The movie's collection, if applicable. + pub backdrop_path: Option, // The URL of the movie's backdrop. + pub budget: u64, // The movie's total budget. + pub genres: Vec, // Genres that apply to the movie. + pub homepage: Option, // The movie's website. + pub id: u64, // The movie's The Movie Database identifier. + pub imdb_id: Option, // The movie's IMDb identifier. + pub original_language: String, // The movie's original language. + pub original_title: String, // The movie's original title. + pub overview: Option, // The movie's overview / description. + pub popularity: f64, // The movie's popularity. + pub poster_path: Option, // The movie's poster URL. + pub production_companies: Vec, // The movie's production companies. + pub production_countries: Vec, // The movie's production countries. + pub release_date: Option, // The movie's release date. + pub revenue: u64, // The movie's total amount of revenue. + pub runtime: Option, // The movie's runtime duration, in minutes. + pub status: String, // The movie's current status as listed on The Movie Database. + pub tagline: Option, // The movie's tagline. + pub title: String, // The movie's title. + pub video: bool, // Whether or not this movie has a video available. + pub vote_average: f64, // The movie's average user score on The Movie Database. + pub vote_count: f64 // The movie's total amount of votes on The Movie Database. +} + +#[derive(Deserialize)] +pub struct Collection { + pub id: u64, // The ID of the collection. + pub name: String, // The name of the collection. + pub poster_path: String, // The poster of the collection. + pub backdrop_path: String // the backdrop of the collection. +} + +#[derive(Deserialize)] +pub struct Genre { + pub id: u64, // The genre's ID. + pub name: String // The genre's name. +} + +#[derive(Deserialize)] +pub struct ProductionCompany { + pub name: String, // The friendly name of the production company. + pub id: u64, // The ID of the production company on The Movie Database. + pub origin_country: String // The country of origin of the production company. +} + +#[derive(Deserialize)] +pub struct ProductionCountry { + pub iso_3166_1: String, // The ISO standard shortcode of the production country. + pub name: String // The friendly name of the production country. +} + +#[derive(Deserialize)] +#[rustfmt::skip] +pub struct Show { + pub backdrop_path: Option, // The show's backdrop path. + pub created_by: Vec, // The show's creators. + pub episode_run_time: Vec, // An array containing the show's episode runtimes. + pub first_air_date: NaiveDate, // The date the show first aired. + pub genres: Vec, // The genres that the show is in. + pub homepage: String, // The show's homepage. + pub id: i64, // The show's id on The Movie Database. + pub in_production: bool, // Whether or not the show is currently in production. + pub languages: Vec, // The show's available languages. + pub last_air_date: NaiveDate, // When the show last aired an episode. + pub last_episode_to_air: EpisodeToAir, // The show's last aired episode. + pub name: String, // The name of the show. + pub next_episode_to_air: Option, // The show's next scheduled episode. + pub networks: Vec, // The networks or services that air the show. + pub number_of_episodes: i64, // The total number of episodes the show has aired. + pub number_of_seasons: i64, // The total number of seasons the show has released. + pub origin_country: Vec, // The country where the show originated. + pub original_language: String, // The original language of the show. + pub original_name: String, // The show's original name. + pub overview: String, // The show's overview. + pub popularity: f64, // An integer containing the show's popularity value. + pub poster_path: Option, // The show's poster path. + #[serde(rename = "production_companies")] + pub studios: Vec, // The studios that produce and manage the show. + pub seasons: Vec, // A vector array containing information on the show's individual seasons. + pub spoken_languages: Vec, // A vector array containing information about the show's spoken languages. + pub status: String, // The status of the show; can be Returning Series, Cancelled, or Ended. + pub tagline: String, // The show's tagline. + #[serde(rename = "type")] + pub format: String, // The format of the show; can be Scripted, News, or Unscripted. + pub vote_average: f64, // The show's average user score on The Movie Database. + pub vote_count: i64, // The show's total amount of user votes on The Movie Database. + pub external_ids: ExternalId // The external IDs associated with the show, e.g. the external IMDb ID. +} + +#[derive(Deserialize)] +pub struct CreatedBy { + pub id: i64, // The ID associated with the given creator. + pub credit_id: String, // The credit ID associated with the given creator. + pub name: String, // The name of the given creator. + pub gender: Option, // The (optional) gender of the given creator. + pub profile_path: Option // The (optional) profile path of the given creator. +} + +#[derive(Deserialize)] +pub struct EpisodeToAir { + pub air_date: Option, // The episode's air date. + pub episode_number: i64, // The number of the episode. + pub id: i64, // The episode's TMDb ID. + pub name: String, // The name of the episode. + pub overview: String, // The episode's overview / synopsis. + pub production_code: String, // The episode's production code. + pub season_number: i64, // The season associated with the episode. + pub still_path: Option, // The episode's still path. + pub vote_average: f64, // The episode's average user score on The Movie Database. + pub vote_count: i64 // The total amount of votes for the episode. +} + +#[derive(Deserialize)] +pub struct NetworkOrStudio { + pub name: String, // The name of the studio. + pub id: i64, // The ID associated with the studio. + pub logo_path: Option, // The studio's logo path. + pub origin_country: Option // The country where the studio originated. +} + +#[derive(Deserialize)] +pub struct Season { + pub air_date: Option, // The premiere date of the season. + pub episode_count: i64, // The total amount of episodes in the season. + pub id: i64, // The season's TMDb identifier. + pub name: String, // The name of the season. Typically just "Season ". + pub overview: Option, // An overview / synopsis about the season. + pub poster_path: Option, // The poster path of the season. + pub season_number: i64 // The season's numerical number. +} + +#[derive(Deserialize)] +pub struct Language { + pub english_name: String, // The name of the given language, in English. + pub iso_639_1: String, // The ISO 639-1 identifier associated with the language. + pub name: String // The native name associated with the language. +} + +#[derive(Deserialize)] +pub struct ExternalId { + pub imdb_id: Option, // The show's IMDb identifier. + pub freebase_mid: Option, // The show's Freebase MID. + pub freebase_id: Option, // The show's freebase ID. + pub tvdb_id: Option, // The show's TVDb identifier. + pub tvrage_id: Option, // The show's TVRage identifier. + pub facebook_id: Option, // The ID of the show's Facebook page. + pub instagram_id: Option, // The ID of the show's Instagram profile. + pub twitter_id: Option, // The ID of the show's Twitter profile. + pub id: Option // The show's The Movie Database identifier. +} diff --git a/src/models/tmdb/movie.rs b/src/models/tmdb/movie.rs deleted file mode 100644 index 57b991e..0000000 --- a/src/models/tmdb/movie.rs +++ /dev/null @@ -1,57 +0,0 @@ -use chrono::prelude::NaiveDate; -use serde::Deserialize; - -#[derive(Deserialize)] -pub struct Movie { - pub adult: bool, // Whether or not the movie has an adult rating. - pub belongs_to_collection: Option, // The movie's collection, if applicable. - pub backdrop_path: Option, // The URL of the movie's backdrop. - pub budget: u64, // The movie's total budget. - pub genres: Vec, // Genres that apply to the movie. - pub homepage: Option, // The movie's website. - pub id: u64, // The movie's The Movie Database identifier. - pub imdb_id: Option, // The movie's IMDb identifier. - pub original_language: String, // The movie's original language. - pub original_title: String, // The movie's original title. - pub overview: Option, // The movie's overview / description. - pub popularity: f64, // The movie's popularity. - pub poster_path: Option, // The movie's poster URL. - pub production_companies: Vec, // The movie's production companies. - pub production_countries: Vec, // The movie's production countries. - pub release_date: Option, // The movie's release date. - pub revenue: u64, // The movie's total amount of revenue. - pub runtime: Option, // The movie's runtime duration, in minutes. - pub status: String, // The movie's current status as listed on The Movie Database. - pub tagline: Option, // The movie's tagline. - pub title: String, // The movie's title. - pub video: bool, // Whether or not this movie has a video available. - pub vote_average: f64, // The movie's average user score on The Movie Database. - pub vote_count: f64 // The movie's total amount of votes on The Movie Database. -} - -#[derive(Deserialize)] -pub struct Collection { - pub id: u64, // The ID of the collection. - pub name: String, // The name of the collection. - pub poster_path: String, // The poster of the collection. - pub backdrop_path: String // the backdrop of the collection. -} - -#[derive(Deserialize)] -pub struct Genre { - pub id: u64, // The genre's ID. - pub name: String // The genre's name. -} - -#[derive(Deserialize)] -pub struct ProductionCompany { - pub name: String, // The friendly name of the production company. - pub id: u64, // The ID of the production company on The Movie Database. - pub origin_country: String // The country of origin of the production company. -} - -#[derive(Deserialize)] -pub struct ProductionCountry { - pub iso_3166_1: String, // The ISO standard shortcode of the production country. - pub name: String // The friendly name of the production country. -} diff --git a/src/models/tmdb/show.rs b/src/models/tmdb/show.rs deleted file mode 100644 index 1db2474..0000000 --- a/src/models/tmdb/show.rs +++ /dev/null @@ -1,108 +0,0 @@ -use chrono::prelude::NaiveDate; -use serde::Deserialize; - -#[derive(Deserialize)] -#[rustfmt::skip] -pub struct Show { - pub backdrop_path: Option, // The show's backdrop path. - pub created_by: Vec, // The show's creators. - pub episode_run_time: Vec, // An array containing the show's episode runtimes. - pub first_air_date: NaiveDate, // The date the show first aired. - pub genres: Vec, // The genres that the show is in. - pub homepage: String, // The show's homepage. - pub id: i64, // The show's id on The Movie Database. - pub in_production: bool, // Whether or not the show is currently in production. - pub languages: Vec, // The show's available languages. - pub last_air_date: NaiveDate, // When the show last aired an episode. - pub last_episode_to_air: EpisodeToAir, // The show's last aired episode. - pub name: String, // The name of the show. - pub next_episode_to_air: Option, // The show's next scheduled episode. - pub networks: Vec, // The networks or services that air the show. - pub number_of_episodes: i64, // The total number of episodes the show has aired. - pub number_of_seasons: i64, // The total number of seasons the show has released. - pub origin_country: Vec, // The country where the show originated. - pub original_language: String, // The original language of the show. - pub original_name: String, // The show's original name. - pub overview: String, // The show's overview. - pub popularity: f64, // An integer containing the show's popularity value. - pub poster_path: Option, // The show's poster path. - #[serde(rename = "production_companies")] - pub studios: Vec, // The studios that produce and manage the show. - pub seasons: Vec, // A vector array containing information on the show's individual seasons. - pub spoken_languages: Vec, // A vector array containing information about the show's spoken languages. - pub status: String, // The status of the show; can be Returning Series, Cancelled, or Ended. - pub tagline: String, // The show's tagline. - #[serde(rename = "type")] - pub format: String, // The format of the show; can be Scripted, News, or Unscripted. - pub vote_average: f64, // The show's average user score on The Movie Database. - pub vote_count: i64, // The show's total amount of user votes on The Movie Database. - pub external_ids: ExternalId // The external IDs associated with the show, e.g. the external IMDb ID. -} - -#[derive(Deserialize)] -pub struct CreatedBy { - pub id: i64, // The ID associated with the given creator. - pub credit_id: String, // The credit ID associated with the given creator. - pub name: String, // The name of the given creator. - pub gender: Option, // The (optional) gender of the given creator. - pub profile_path: Option // The (optional) profile path of the given creator. -} - -#[derive(Deserialize)] -pub struct Genre { - pub id: i64, // The ID of the given genre. - pub name: String // The name of the given genre. -} - -#[derive(Deserialize)] -pub struct EpisodeToAir { - pub air_date: Option, // The episode's air date. - pub episode_number: i64, // The number of the episode. - pub id: i64, // The episode's TMDb ID. - pub name: String, // The name of the episode. - pub overview: String, // The episode's overview / synopsis. - pub production_code: String, // The episode's production code. - pub season_number: i64, // The season associated with the episode. - pub still_path: Option, // The episode's still path. - pub vote_average: f64, // The episode's average user score on The Movie Database. - pub vote_count: i64 // The total amount of votes for the episode. -} - -#[derive(Deserialize)] -pub struct NetworkOrStudio { - pub name: String, // The name of the studio. - pub id: i64, // The ID associated with the studio. - pub logo_path: Option, // The studio's logo path. - pub origin_country: Option // The country where the studio originated. -} - -#[derive(Deserialize)] -pub struct Season { - pub air_date: Option, // The premiere date of the season. - pub episode_count: i64, // The total amount of episodes in the season. - pub id: i64, // The season's TMDb identifier. - pub name: String, // The name of the season. Typically just "Season ". - pub overview: Option, // An overview / synopsis about the season. - pub poster_path: Option, // The poster path of the season. - pub season_number: i64 // The season's numerical number. -} - -#[derive(Deserialize)] -pub struct Language { - pub english_name: String, // The name of the given language, in English. - pub iso_639_1: String, // The ISO 639-1 identifier associated with the language. - pub name: String // The native name associated with the language. -} - -#[derive(Deserialize)] -pub struct ExternalId { - pub imdb_id: Option, // The show's IMDb identifier. - pub freebase_mid: Option, // The show's Freebase MID. - pub freebase_id: Option, // The show's freebase ID. - pub tvdb_id: Option, // The show's TVDb identifier. - pub tvrage_id: Option, // The show's TVRage identifier. - pub facebook_id: Option, // The ID of the show's Facebook page. - pub instagram_id: Option, // The ID of the show's Instagram profile. - pub twitter_id: Option, // The ID of the show's Twitter profile. - pub id: Option // The show's The Movie Database identifier. -} diff --git a/src/utils/locale.rs b/src/utils/locale.rs new file mode 100644 index 0000000..8c1adef --- /dev/null +++ b/src/utils/locale.rs @@ -0,0 +1,273 @@ +//! Locale and Language Utilities +//! +//! These are helpful utility functions that help ease the task of working +//! with locale and languages, with a focus on ISO conversions. + +/// Retrieves the friendly name of the provided language code if +/// the language is recognized. If the ISO 639-1 language code that +/// is provided is not recognized, the two-digit ISO code will be provided +/// instead. +pub fn get_language_name_from_iso(lang: &str) -> &str { + match lang { + "en" => "English", + "fr" => "French", + "ja" => "Japanese", + "ko" => "Korean", + "cn" => "Cantonese", + _ => lang + } +} + +/// Retrieves the friendly name of the provided country code if the +/// country is recognized. If the ISO 3166-1 country code provided is +/// not recognized, the function will gracefully fall back to just displaying +/// the ISO 3166-1 code. +pub fn get_country_name_from_iso(country: &str) -> &str { + match country { + "AF" => "Afghanistan", + "AX" => "Åland Islands", + "AL" => "Albania", + "DZ" => "Algeria", + "AS" => "American Samoa", + "AD" => "Andorra", + "AO" => "Angola", + "AI" => "Anguilla", + "AQ" => "Antarctica", + "AG" => "Antigua and Barbuda", + "AR" => "Argentina", + "AM" => "Aruba", + "AU" => "Australia", + "AT" => "Austria", + "AZ" => "Azerbaijan", + "BS" => "Bahamas", + "BH" => "Bahrain", + "BD" => "Bangladesh", + "BB" => "Barbados", + "BY" => "Belarus", + "BE" => "Belgium", + "BZ" => "Belize", + "BJ" => "Benin", + "BM" => "Bermuda", + "BT" => "Bhutan", + "BO" => "Bolivia", + "BA" => "Bosnia and Herzegovina", + "BW" => "Botswana", + "BV" => "Bouvet Island", + "BR" => "Brazil", + "VG" => "British Virgin Islands", + "IO" => "British Indian Ocean Territory", + "BN" => "Brunei Darussalam", + "BG" => "Bulgaria", + "BF" => "Burkina Faso", + "BI" => "Burundi", + "KH" => "Cambodia", + "CM" => "Cameroon", + "CA" => "Canada", + "CV" => "Cape Verde", + "KY" => "Cayman Islands", + "CF" => "Central African Republic", + "TD" => "Chad", + "CL" => "Chile", + "CN" => "China", + "HK" => "Hong Kong, SAR China", + "MO" => "Macao, SAR China", + "CX" => "Christmas Island", + "CC" => "Cocos (Keeling) Islands", + "CO" => "Columbia", + "KM" => "Comoros", + "CG" => "Congo (Brazzaville)", + "CD" => "Congo, (Kinshasa)", + "CK" => "Cook Islands", + "CR" => "Costa Rica", + "CI" => "Côte d'Ivoire", + "HR" => "Croatia", + "CU" => "Cuba", + "CY" => "Cyprus", + "CZ" => "Czech Republic", + "DK" => "Denmark", + "DJ" => "Djibouti", + "DM" => "Domincia", + "DO" => "Dominician Republic", + "EC" => "Ecuador", + "EG" => "Egypt", + "SV" => "El Salvador", + "GQ" => "Equatorial Guinea", + "ER" => "Eritrea", + "EE" => "Estonia", + "ET" => "Ethopia", + "FK" => "Falkland Islands (Malvinas)", + "FO" => "Faroe Islands", + "FJ" => "Fiji", + "FI" => "Finland", + "FR" => "France", + "GF" => "French Guiana", + "PF" => "French Polynesia", + "TF" => "French Southern Territories", + "GA" => "Gabon", + "GM" => "Gambia", + "GE" => "Georgia", + "DE" => "Germany", + "GH" => "Ghana", + "GI" => "Gibraltar", + "GR" => "Greece", + "GL" => "Greenland", + "GD" => "Grenada", + "GP" => "Guadeloupe", + "GU" => "Guam", + "GT" => "Guatemala", + "GG" => "Guernsey", + "GN" => "Guinea", + "GW" => "Guinea-Bissau", + "GY" => "Guyana", + "HT" => "Haiti", + "HM" => "Heard and Mcdonald Islands", + "VA" => "Holy See (Vatican City State)", + "HN" => "Honduras", + "HU" => "Hungary", + "IS" => "Iceland", + "IN" => "India", + "ID" => "Indonesia", + "IR" => "Islamic Republic of Iran", + "IQ" => "Iraq", + "IE" => "Ireland", + "IM" => "Isle of Man", + "IL" => "Israel", + "IT" => "Italy", + "JM" => "Jamaica", + "JP" => "Japan", + "JE" => "Jersey", + "JO" => "Jordan", + "KZ" => "Kazakhstan", + "KE" => "Kenya", + "KI" => "Kiribati", + "XK" => "Kosovo", + "KP" => "North Korea", + "KR" => "South Korea", + "KW" => "Kuwait", + "KG" => "Kyrgyzstan", + "LA" => "Lao PDR", + "LV" => "Latvia", + "LB" => "Lebanon", + "LS" => "Lesotho", + "LR" => "Liberia", + "LY" => "Libya", + "LI" => "Liechtenstein", + "LT" => "Lithuania", + "LU" => "Luxembourg", + "MK" => "Republic of Macedonia", + "MG" => "Madagascar", + "MW" => "Malawi", + "MY" => "Malaysia", + "MV" => "Maldives", + "ML" => "Mali", + "MT" => "Malta", + "MH" => "Marshall Islands", + "MQ" => "Martinique", + "MR" => "Mauritania", + "MU" => "Mauritius", + "YT" => "Mayotte", + "MX" => "Mexico", + "FM" => "Federated States of Micronesia", + "MD" => "Moldova", + "MC" => "Monaco", + "MN" => "Mongolia", + "ME" => "Montenegro", + "MS" => "Montserrat", + "MA" => "Morocco", + "MZ" => "Mozambique", + "MM" => "Myanmar", + "NA" => "Namibia", + "NR" => "Naru", + "NP" => "Nepal", + "NL" => "Netherlands", + "AN" => "Netherlands Antilles", + "NC" => "New Caledonia", + "NZ" => "New Zealand", + "NI" => "Nicaragua", + "NE" => "Niger", + "NG" => "Nigeria", + "NU" => "Niue", + "NF" => "Norfolk Island", + "MP" => "Northern Mariana Islands", + "NO" => "Norway", + "OM" => "Oman", + "PK" => "Pakistan", + "PW" => "Palau", + "PS" => "Palestinian Territory", + "PA" => "Panama", + "PG" => "Papua New Guinea", + "PE" => "Peru", + "PH" => "Philippines", + "PN" => "Pitcairn", + "PL" => "Poland", + "PT" => "Portugal", + "PR" => "Puerto Rico", + "QA" => "Qatar", + "RE" => "Réunion", + "RO" => "Romania", + "RU" => "Russia (Russian Federation)", + "RW" => "Rwanda", + "BL" => "Saint-Barthélemy", + "SH" => "Saint Helena", + "KN" => "Saint Kitts and Nevis", + "LC" => "Saint Lucia", + "MF" => "Saint-Martin (French part)", + "PM" => "Saint Pierre and Miquelon", + "VC" => "Saint Vincent and Grenadines", + "WS" => "Samoa", + "SM" => "San Marino", + "ST" => "Sao Tome and Principe", + "SA" => "Saudi Arabia", + "SN" => "Senegal", + "RS" => "Serbia", + "SC" => "Seychelles", + "SL" => "Sierra Leone", + "SG" => "Singapore", + "SI" => "Slovenia", + "SB" => "Solomon Islands", + "SO" => "Somalia", + "ZA" => "South Africa", + "GS" => "South Georgia and the South Sandwich Islands", + "SS" => "South Sudan", + "ES" => "Spain", + "LK" => "Sri Lanka", + "SR" => "Suriname", + "SJ" => "Svalbard and Jan Mayen Islands", + "SZ" => "Swaziland", + "SE" => "Sweden", + "CH" => "Switzerland", + "SY" => "Syran Arab Republic (Syria)", + "TW" => "Taiwan (Republic of China)", + "TJ" => "Tajikstan", + "TZ" => "United Republic of Tanzania", + "TH" => "Thailand", + "TL" => "Timor-Leste", + "TG" => "Togo", + "TK" => "Tokelau", + "TO" => "Tonga", + "TT" => "Trinidad and Tobago", + "TN" => "Tunisia", + "TR" => "Türkiye", + "TM" => "Turkmenistan", + "TC" => "Turks and Caicos Islands", + "TV" => "Tuvalu", + "UG" => "Uganda", + "UA" => "Ukraine", + "AE" => "United Arab Emirates", + "GB" => "United Kingdom", + "US" => "United States", + "UM" => "US Minor Outlying Islands", + "UY" => "Uruguay", + "UZ" => "Uzbekistan", + "VU" => "Vanuatu", + "VE" => "Venezuaela", + "VN" => "Vietnam", + "VI" => "United States Virgin Islands", + "WF" => "Wallis and Futuna Islands", + "EH" => "Western Sahara", + "YE" => "Yemen", + "ZM" => "Zambia", + "ZW" => "Zimbabwe", + _ => country + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 787d56c..bbedbd0 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,5 @@ +pub mod locale; + use crate::config::ConfigurationData; use std::{fs::File, io::Read}; @@ -7,3 +9,14 @@ pub fn read_config(file: &str) -> ConfigurationData { file.read_to_string(&mut contents).unwrap(); toml::from_str::(&contents).unwrap() } + +pub fn format_int(int: u64) -> String { + let mut string = String::new(); + for (idx, val) in int.to_string().chars().rev().enumerate() { + if idx != 0 && idx % 3 == 0 { + string.insert(0, ','); + } + string.insert(0, val); + } + string +}