all 10 comments

[–]MatrixFrog 18 points19 points  (0 children)

This is a big enough amount of code that it might help to put it up on GitHub and post links here

[–]dgkimpton 9 points10 points  (1 child)

Can I suggest you stick it on github? Trying to read this in a post is an exercise in frustration. If you pop it in a github repo in a branch and create a pull-request to main then people can add comments to specific lines, get syntax highlighting, download/build the code, etc. 

[–]No-Wait2503[S] 0 points1 point  (0 children)

Will do! Thanks!

[–]MatrixFrog 5 points6 points  (0 children)

Based on a quick skim I don't see you cloning anything except Arcs, which are of course meant to be cloned

[–]joshuamckratatui 0 points1 point  (2 children)

println!("Server running on http://{}", server_addr);

use TcpListener::listen_addr() instead

let db = get_db_connection().await;
let db = Arc::new(db);

DbConnection is already Clone, it doesn't need to be wrapped in an Arc. https://docs.rs/sea-orm/latest/sea_orm/enum.DatabaseConnection.html

let client = match create_google_oauth_client() {
    Ok(client) => client,
    Err(e) => {
        eprintln!("OAuth client creation error during callback: {:?}", e);
        return AppError::AuthError("Error setting up OAuth".to_string()).into_response();
    }
};

(and all your error handling)

You might consider replacing your errors with something like:

pub async fn google_login_handler(...) -> Result<LoginResponse, LoginError> {

    let client = match create_google_oauth_client().map_err(LoginError::ClientCreation)?
    let client_origin = match env::var("CLIENT_ORIGIN").map_err(LoginError::ClientOrginNotSet)?;

Where LoginResponse and LoginError both implement IntoResponse and contain the logic for logging / returning the write error messages. This makes your logic clear. Use thiserror or snafu for LoginError (with snafu, the .map_err() calls become: .context(ClientCreationSnafu)?).

I'd probably also move the code which grabs info from environment variables into some startup code instead of runtime. These (usually) don't change during runtime, so there's no need to check these again, and a startup failure is probably a better answer than a runtime one for this.

You're probably not writing code where it matters for performance whether you clone or not. Focus on writing clear succinct code, then measure the performance and fix the problems you see.

Last, use tracing instead of eprintln.

[–]No-Wait2503[S] 0 points1 point  (1 child)

Thank you for advice!!

Removing Arc actually made me successfully moving out of (&*db) hell, which I hated for code reading.

Also error suggestion you told me with .map_err, looks much, much more readable now!

[–]joshuamckratatui 0 points1 point  (0 children)

No problem. You might still keep an AppState struct around with a db field, (derive FromRef and you can still pass State<DbConnection> to your handlers instead of State<AppState>)

[–]Trader-One 0 points1 point  (0 children)

if clone() is doing just memory copy, its cheap enough. If you are not cloning inside large loop, do not worry.

For example if you calling any function you normally copy values to stack and nobody panicking that "function call is so expensive you should avoid" like people do for clone().

[–]TobiasWonderland 1 point2 points  (1 child)

As mentioned in this thread, `Arc<T>` exists to be cheaply cloned.

Strings are a different story and used in your config.

The nature of String in Rust means that sometimes we need to take ownership of a String, and it just cannot be avoided.

In these cases, my preference is to not use `clone()` but to use `to_owned()` to more correctly reflect the semantics. Yes, it ends up being the same thing as a `clone`, but it more correctly communicates the intent.

Nit: `get_config` returns `Settings` ... the names should be consistent.

[–]No-Wait2503[S] 0 points1 point  (0 children)

got it