Use `sqlx::query_as!` in a database-access function when SQL results should become a named Rust struct. For course queries, it maps returned columns into `Course` fields without writing an assignment for every field. The `Course` model derives `sqlx::FromRow`. section 6.2.2 section 6.4
use crate::errors::EzyTutorError;
use crate::models::course::*;
use sqlx::postgres::PgPool;
pub async fn get_courses_for_tutor_db(
pool: &PgPool,
tutor_id: i32,
) -> Result<Vec<Course>, EzyTutorError> {
// Prepare SQL statement
let course_rows: Vec<Course> = sqlx::query_as!( #1
Course,
"SELECT * FROM ezy_course_c6 where tutor_id = $1",
tutor_id
)
.fetch_all(pool) #2
.await?; #3
Ok(course_rows) #4
}
In `src/iter5/dbaccess/course.rs`, the tutor-course query names `Course`, supplies SQL and `tutor_id`, then uses `fetch_all(pool)` to return `Vec<Course>`. section 6.2.2
Choose the fetch method to match the operation: `fetch_optional` lets a single-course lookup turn a missing row into a not-found error; an INSERT with PostgreSQL `RETURNING` and `fetch_one` returns the newly created `Course`. A course update first fetches the current row so omitted optional fields can keep their existing values. section 6.2.2
use crate::errors::EzyTutorError;
use crate::models::tutor::{NewTutor, Tutor, UpdateTutor};
use sqlx::postgres::PgPool;
pub async fn get_all_tutors_db(pool: &PgPool) ->
Result<Vec<Tutor>, EzyTutorError> {
// Prepare SQL statement
let tutor_rows =
sqlx::query!("SELECT tutor_id, tutor_name, tutor_pic_url,
tutor_profile FROM ezy_tutor_c6")
.fetch_all(pool)
.await?;
// Extract result
let tutors: Vec<Tutor> = tutor_rows
.iter()
.map(|tutor_row| Tutor {
tutor_id: tutor_row.tutor_id,
tutor_name: tutor_row.tutor_name.clone(),
tutor_pic_url: tutor_row.tutor_pic_url.clone(),
tutor_profile: tutor_row.tutor_profile.clone(),
})
.collect();
match tutors.len() {
0 => Err(EzyTutorError::NotFound("No tutors found".into())),
_ => Ok(tutors),
}
}
Compare the tutor query: `sqlx::query!` fetches rows, and a `map` closure assigns each column to a `Tutor` field. Manual mapping is useful when struct fields do not match database columns, such as when a struct adds computed fields or includes a tutor’s courses. section 6.3.3
