From 5201f352c8948f1b47887656286bc32b6460bdae Mon Sep 17 00:00:00 2001 From: JmPotato Date: Sun, 25 Aug 2024 16:39:42 +0800 Subject: [PATCH] Reduce the use of .unwrap() Signed-off-by: JmPotato --- src/app.rs | 24 +++++++++--------------- src/config.rs | 35 ++++++++++++++++++++++++----------- src/error.rs | 13 +++++++++++-- src/handlers.rs | 22 ++++++++++++++++------ src/models/articles.rs | 20 ++++++++++---------- src/models/pages.rs | 4 ++-- 6 files changed, 72 insertions(+), 46 deletions(-) diff --git a/src/app.rs b/src/app.rs index 017cf3c..4e83cb0 100644 --- a/src/app.rs +++ b/src/app.rs @@ -51,13 +51,10 @@ impl AppState { info!("connecting to the database"); // connect to the database. - let db = match sqlx::MySqlPool::connect(&config.mysql_connection_url()).await { - Ok(db) => db, - Err(e) => return Err(Error::Sqlx(e)), - }; + let db = sqlx::MySqlPool::connect(&config.mysql_connection_url()?).await?; info!("initializing the database"); // create the tables if they don't exist. - create_tables(&db).await.unwrap(); + create_tables(&db).await?; // init the admin user. let admin_username = config.admin_username(); User::insert( @@ -65,20 +62,19 @@ impl AppState { &admin_username, &password_auth::generate_hash(&admin_username), ) - .await - .unwrap(); + .await?; info!("initializing the environment"); let mut env = Environment::new(); // iterate the templates directory and add all the templates. - for entry in std::fs::read_dir(TEMPLATES_DIR).unwrap() { + for entry in std::fs::read_dir(TEMPLATES_DIR)? { let path = entry.unwrap().path(); if !path.is_file() { continue; } let file_name = path.file_name().unwrap().to_string_lossy().into_owned(); - let template_content = std::fs::read_to_string(path).unwrap(); - env.add_template_owned(file_name, template_content).unwrap(); + let template_content = std::fs::read_to_string(path)?; + env.add_template_owned(file_name, template_content)?; } // load the global variables into the environment. env.add_global("config", Value::from_object(config.clone())); @@ -205,11 +201,9 @@ impl App { ) .with_state(Arc::new(self.state.clone())); - let listener = tokio::net::TcpListener::bind(self.state.config.server_url()) - .await - .unwrap(); - info!("listening on {}", listener.local_addr().unwrap()); - axum::serve(listener, app).await.unwrap(); + let listener = tokio::net::TcpListener::bind(self.state.config.server_url()).await?; + info!("listening on {}", listener.local_addr()?); + axum::serve(listener, app).await?; Ok(()) } diff --git a/src/config.rs b/src/config.rs index fe481da..a4cfde3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -151,7 +151,7 @@ pub struct Config { impl Config { pub fn new(path: &str) -> Result { - let config_content = std::fs::read_to_string(path).unwrap(); + let config_content = std::fs::read_to_string(path)?; let mut config: Self = toml::from_str(&config_content).map_err(Error::Toml)?; // get some environment variables. config.load_env_vars()?; @@ -201,18 +201,31 @@ impl Config { // get the MySQL connection URL according to the config, it will use `connection_url` if it is set, // otherwise it will use `username`, `password`, `host`, `port` and `database` to build one. - pub fn mysql_connection_url(&self) -> String { - if let Some(connection_url) = self.mysql.connection_url.clone() { - connection_url + pub fn mysql_connection_url(&self) -> Result { + if let Some(connection_url) = &self.mysql.connection_url { + Ok(connection_url.clone()) } else { - format!( + let (username, password, host, port, database) = ( + self.mysql + .username + .as_ref() + .ok_or(Error::InvalidMySQLConfig)?, + self.mysql + .password + .as_ref() + .ok_or(Error::InvalidMySQLConfig)?, + self.mysql.host.as_ref().ok_or(Error::InvalidMySQLConfig)?, + self.mysql.port.ok_or(Error::InvalidMySQLConfig)?, + self.mysql + .database + .as_ref() + .ok_or(Error::InvalidMySQLConfig)?, + ); + + Ok(format!( "mysql://{}:{}@{}:{}/{}", - self.mysql.username.as_ref().unwrap(), - self.mysql.password.as_ref().unwrap(), - self.mysql.host.as_ref().unwrap(), - self.mysql.port.unwrap(), - self.mysql.database.as_ref().unwrap() - ) + username, password, host, port, database + )) } } diff --git a/src/error.rs b/src/error.rs index 1efabaf..424b064 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,12 +5,21 @@ pub enum Error { #[error(transparent)] Toml(#[from] toml::de::Error), - #[error("config validation failed: {0}")] - ConfigValidation(String), + #[error(transparent)] + Io(#[from] std::io::Error), + + #[error(transparent)] + MiniJinja(#[from] minijinja::Error), #[error(transparent)] Sqlx(#[from] sqlx::Error), #[error(transparent)] TaskJoin(#[from] task::JoinError), + + #[error("config validation failed: {0}")] + ConfigValidation(String), + + #[error("invalid MySQL config, please specify the connection URL or the username, password, host, port and database")] + InvalidMySQLConfig, } diff --git a/src/handlers.rs b/src/handlers.rs index 24eafc1..07b9662 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -504,13 +504,18 @@ pub async fn handler_delete_article( Path(editor_path): Path, ) -> impl IntoResponse { let redirect = Redirect::to("/admin"); - match Article::delete(&state.db, editor_path.id.unwrap()).await { - Ok(_) => redirect.into_response(), + let id = match editor_path.id { + Some(id) => id, + None => return redirect.into_response(), + }; + match Article::delete(&state.db, id).await { + Ok(_) => redirect, Err(err) => { error!("failed deleting article: {:?}", err); - redirect.into_response() + redirect } } + .into_response() } pub async fn handler_delete_page( @@ -518,13 +523,18 @@ pub async fn handler_delete_page( Path(editor_path): Path, ) -> impl IntoResponse { let redirect = Redirect::to("/admin"); - match Page::delete(&state.db, editor_path.id.unwrap()).await { - Ok(_) => redirect.into_response(), + let id = match editor_path.id { + Some(id) => id, + None => return redirect.into_response(), + }; + match Page::delete(&state.db, id).await { + Ok(_) => redirect, Err(err) => { error!("failed deleting page: {:?}", err); - redirect.into_response() + redirect } } + .into_response() } pub async fn handler_ping() -> impl IntoResponse { diff --git a/src/models/articles.rs b/src/models/articles.rs index 36e3cc5..c7248d6 100644 --- a/src/models/articles.rs +++ b/src/models/articles.rs @@ -18,7 +18,7 @@ impl Article { sqlx::query_as("SELECT * FROM articles ORDER BY id DESC") .fetch_all(db) .await - .unwrap() + .unwrap_or_default() } pub async fn get_on_page(db: &sqlx::MySqlPool, page: u32, article_per_page: u32) -> Vec { @@ -27,14 +27,14 @@ impl Article { .bind((page - 1) * article_per_page) .fetch_all(db) .await - .unwrap() + .unwrap_or_default() } pub async fn get_total_count(db: &sqlx::MySqlPool) -> i32 { sqlx::query_scalar("SELECT COUNT(*) FROM articles") .fetch_one(db) .await - .unwrap() + .unwrap_or_default() } pub async fn get_by_id(db: &sqlx::MySqlPool, id: i32) -> Option { @@ -47,23 +47,23 @@ impl Article { pub async fn get_by_tag(db: &sqlx::MySqlPool, tag: &str) -> Vec { sqlx::query_as( - "SELECT a.id, a.title, a.content, a.tags, a.created_at, a.updated_at - FROM articles AS a - INNER JOIN tags AS t ON a.id = t.article_id - WHERE t.name = ? + "SELECT a.id, a.title, a.content, a.tags, a.created_at, a.updated_at + FROM articles AS a + INNER JOIN tags AS t ON a.id = t.article_id + WHERE t.name = ? ORDER BY a.id DESC", ) .bind(tag) .fetch_all(db) .await - .unwrap() + .unwrap_or_default() } pub async fn get_latest_updated(db: &sqlx::MySqlPool) -> Option> { sqlx::query_scalar("SELECT MAX(updated_at) FROM articles") .fetch_one(db) .await - .unwrap() + .ok() } pub async fn insert( @@ -185,6 +185,6 @@ impl Tags { sqlx::query_as("SELECT name, COUNT(name) AS num FROM tags GROUP BY name ORDER BY num DESC") .fetch_all(db) .await - .unwrap() + .unwrap_or_default() } } diff --git a/src/models/pages.rs b/src/models/pages.rs index db1ff16..b7bca86 100644 --- a/src/models/pages.rs +++ b/src/models/pages.rs @@ -16,14 +16,14 @@ impl Page { sqlx::query_as("SELECT * FROM pages ORDER BY id DESC") .fetch_all(db) .await - .unwrap() + .unwrap_or_default() } pub async fn get_all_titles(db: &sqlx::MySqlPool) -> Vec { sqlx::query_scalar("SELECT title FROM pages ORDER BY title ASC") .fetch_all(db) .await - .unwrap() + .unwrap_or_default() } pub async fn get_by_id(db: &sqlx::MySqlPool, id: i32) -> Option {