rust-ef 1.5.2

Rust Entity Framework - An EFCore-inspired ORM for Rust
Documentation
#[cfg(test)]
mod m2m_tests {
    use rust_ef::db_context::{DbContext, DbContextOptionsBuilder};
    use rust_ef::linq;
    use rust_ef::prelude::*;
    use rust_ef_sqlite::DbContextOptionsBuilderExt as _;

    #[derive(Debug, Clone, EntityType)]
    #[table("m2m_students")]
    struct Student {
        #[primary_key]
        #[auto_increment]
        student_id: i32,
        #[required]
        name: String,
        #[navigation]
        courses: HasMany<Course, Enrollment>,
    }

    #[derive(Debug, Clone, EntityType)]
    #[table("m2m_students_through")]
    struct StudentThrough {
        #[primary_key]
        #[auto_increment]
        student_id: i32,
        #[required]
        name: String,
        #[navigation]
        #[through(EnrollmentThrough)]
        courses: HasMany<Course>,
    }

    #[derive(Debug, Clone, EntityType)]
    #[table("m2m_courses")]
    struct Course {
        #[primary_key]
        #[auto_increment]
        course_id: i32,
        #[required]
        title: String,
    }

    #[derive(Debug, Clone, EntityType)]
    #[table("m2m_enrollments")]
    struct Enrollment {
        #[primary_key]
        #[auto_increment]
        enrollment_id: i32,
        #[foreign_key(Student)]
        student_id: i32,
        #[foreign_key(Course)]
        course_id: i32,
    }

    #[derive(Debug, Clone, EntityType)]
    #[table("m2m_enrollments_through")]
    struct EnrollmentThrough {
        #[primary_key]
        #[auto_increment]
        enrollment_id: i32,
        #[foreign_key(StudentThrough)]
        student_id: i32,
        #[foreign_key(Course)]
        course_id: i32,
    }

    async fn seed_m2m(ctx: &mut DbContext, use_through_attr: bool) {
        if use_through_attr {
            ctx.set::<StudentThrough>();
            ctx.set::<EnrollmentThrough>();
        } else {
            ctx.set::<Student>();
            ctx.set::<Enrollment>();
        }
        ctx.set::<Course>();
        ctx.ensure_created().await.unwrap();

        if use_through_attr {
            ctx.set::<StudentThrough>().add(StudentThrough {
                student_id: 0,
                name: "Alice".into(),
                courses: HasMany::new(),
            });
        } else {
            ctx.set::<Student>().add(Student {
                student_id: 0,
                name: "Alice".into(),
                courses: HasMany::new(),
            });
        }
        ctx.save_changes().await.unwrap();

        let student_id = if use_through_attr {
            ctx.set::<StudentThrough>().query().to_list().await.unwrap()[0].student_id
        } else {
            ctx.set::<Student>().query().to_list().await.unwrap()[0].student_id
        };

        ctx.set::<Course>().add(Course {
            course_id: 0,
            title: "Rust 101".into(),
        });
        ctx.set::<Course>().add(Course {
            course_id: 0,
            title: "ORM Design".into(),
        });
        ctx.save_changes().await.unwrap();
        let courses = ctx.set::<Course>().query().to_list().await.unwrap();

        if use_through_attr {
            ctx.set::<EnrollmentThrough>().add(EnrollmentThrough {
                enrollment_id: 0,
                student_id,
                course_id: courses[0].course_id,
            });
            ctx.set::<EnrollmentThrough>().add(EnrollmentThrough {
                enrollment_id: 0,
                student_id,
                course_id: courses[1].course_id,
            });
        } else {
            ctx.set::<Enrollment>().add(Enrollment {
                enrollment_id: 0,
                student_id,
                course_id: courses[0].course_id,
            });
            ctx.set::<Enrollment>().add(Enrollment {
                enrollment_id: 0,
                student_id,
                course_id: courses[1].course_id,
            });
        }
        ctx.save_changes().await.unwrap();
    }

    fn make_ctx() -> DbContext {
        let mut builder = DbContextOptionsBuilder::new();
        builder.use_sqlite_in_memory();
        let options = builder.build();
        DbContext::from_options(&options).unwrap()
    }

    #[tokio::test]
    async fn test_many_to_many_has_many_join_generic() {
        let mut ctx = make_ctx();
        seed_m2m(&mut ctx, false).await;

        let students = linq!(ctx.set::<Student>(); include b.courses)
            .to_list()
            .await
            .unwrap();

        assert_eq!(students.len(), 1);
        assert_eq!(students[0].courses.len(), 2);
    }

    #[tokio::test]
    async fn test_many_to_many_through_attribute() {
        let mut ctx = make_ctx();
        seed_m2m(&mut ctx, true).await;

        let students = linq!(ctx.set::<StudentThrough>(); include b.courses)
            .to_list()
            .await
            .unwrap();

        assert_eq!(students.len(), 1);
        assert_eq!(students[0].courses.len(), 2);
        let titles: Vec<&str> = students[0]
            .courses
            .items()
            .iter()
            .map(|c| c.title.as_str())
            .collect();
        assert!(titles.contains(&"Rust 101"));
        assert!(titles.contains(&"ORM Design"));
    }
}