#![allow(dead_code, unused, clippy::type_complexity)]
#[derive(Debug, Clone)]
struct User {
id: i32,
email: String,
name: Option<String>,
posts: Option<Vec<Post>>,
profile: Option<Box<Profile>>,
}
#[derive(Debug, Clone)]
struct Post {
id: i32,
title: String,
content: Option<String>,
author_id: i32,
author: Option<Box<User>>,
tags: Option<Vec<Tag>>,
comments: Option<Vec<Comment>>,
}
#[derive(Debug, Clone)]
struct Profile {
id: i32,
bio: Option<String>,
user_id: i32,
user: Option<Box<User>>,
}
#[derive(Debug, Clone)]
struct Tag {
id: i32,
name: String,
posts: Option<Vec<Post>>,
}
#[derive(Debug, Clone)]
struct Comment {
id: i32,
content: String,
post_id: i32,
parent_id: Option<i32>,
post: Option<Box<Post>>,
parent: Option<Box<Comment>>,
replies: Option<Vec<Comment>>,
}
struct MockClient;
impl MockClient {
fn user(&self) -> UserQuery {
UserQuery
}
fn post(&self) -> PostQuery {
PostQuery
}
}
struct UserQuery;
impl UserQuery {
fn find_unique(self) -> UserFindUnique {
UserFindUnique { includes: vec![] }
}
fn find_many(self) -> UserFindMany {
UserFindMany { includes: vec![] }
}
fn create(self, _data: CreateUserData) -> UserCreate {
UserCreate
}
}
struct UserFindUnique {
includes: Vec<String>,
}
impl UserFindUnique {
#[allow(non_snake_case)]
fn r#where(self, _filter: &str) -> Self {
self
}
fn include(mut self, relation: &str) -> Self {
self.includes.push(relation.to_string());
self
}
async fn exec(self) -> Result<Option<User>, Box<dyn std::error::Error>> {
let mut user = User {
id: 1,
email: "alice@example.com".to_string(),
name: Some("Alice".to_string()),
posts: None,
profile: None,
};
if self.includes.contains(&"posts".to_string()) {
user.posts = Some(vec![
Post {
id: 1,
title: "First Post".to_string(),
content: Some("Content...".to_string()),
author_id: 1,
author: None,
tags: None,
comments: None,
},
Post {
id: 2,
title: "Second Post".to_string(),
content: Some("More content...".to_string()),
author_id: 1,
author: None,
tags: None,
comments: None,
},
]);
}
if self.includes.contains(&"profile".to_string()) {
user.profile = Some(Box::new(Profile {
id: 1,
bio: Some("Software developer".to_string()),
user_id: 1,
user: None,
}));
}
Ok(Some(user))
}
}
struct UserFindMany {
includes: Vec<String>,
}
impl UserFindMany {
fn include(mut self, relation: &str) -> Self {
self.includes.push(relation.to_string());
self
}
fn take(self, _count: usize) -> Self {
self
}
async fn exec(self) -> Result<Vec<User>, Box<dyn std::error::Error>> {
Ok(vec![User {
id: 1,
email: "alice@example.com".to_string(),
name: Some("Alice".to_string()),
posts: if self.includes.contains(&"posts".to_string()) {
Some(vec![])
} else {
None
},
profile: None,
}])
}
}
struct CreateUserData {
email: String,
name: Option<String>,
posts: Option<NestedPostCreate>,
profile: Option<NestedProfileCreate>,
}
struct NestedPostCreate {
data: Vec<CreatePostData>,
}
struct CreatePostData {
title: String,
content: Option<String>,
}
struct NestedProfileCreate {
data: CreateProfileData,
}
struct CreateProfileData {
bio: Option<String>,
}
struct UserCreate;
impl UserCreate {
fn include(self, _relation: &str) -> Self {
self
}
async fn exec(self) -> Result<User, Box<dyn std::error::Error>> {
Ok(User {
id: 3,
email: "new@example.com".to_string(),
name: Some("New User".to_string()),
posts: Some(vec![Post {
id: 10,
title: "My First Post".to_string(),
content: Some("Hello world!".to_string()),
author_id: 3,
author: None,
tags: None,
comments: None,
}]),
profile: Some(Box::new(Profile {
id: 3,
bio: Some("Just joined!".to_string()),
user_id: 3,
user: None,
})),
})
}
}
struct PostQuery;
impl PostQuery {
fn find_unique(self) -> PostFindUnique {
PostFindUnique { includes: vec![] }
}
}
struct PostFindUnique {
includes: Vec<String>,
}
impl PostFindUnique {
#[allow(non_snake_case)]
fn r#where(self, _filter: &str) -> Self {
self
}
fn include(mut self, relation: &str) -> Self {
self.includes.push(relation.to_string());
self
}
async fn exec(self) -> Result<Option<Post>, Box<dyn std::error::Error>> {
let mut post = Post {
id: 1,
title: "First Post".to_string(),
content: Some("Content...".to_string()),
author_id: 1,
author: None,
tags: None,
comments: None,
};
if self.includes.contains(&"author".to_string()) {
post.author = Some(Box::new(User {
id: 1,
email: "alice@example.com".to_string(),
name: Some("Alice".to_string()),
posts: None,
profile: None,
}));
}
if self.includes.contains(&"tags".to_string()) {
post.tags = Some(vec![
Tag {
id: 1,
name: "rust".to_string(),
posts: None,
},
Tag {
id: 2,
name: "tutorial".to_string(),
posts: None,
},
]);
}
if self.includes.contains(&"comments".to_string()) {
post.comments = Some(vec![Comment {
id: 1,
content: "Great post!".to_string(),
post_id: 1,
parent_id: None,
post: None,
parent: None,
replies: Some(vec![Comment {
id: 2,
content: "Thanks!".to_string(),
post_id: 1,
parent_id: Some(1),
post: None,
parent: None,
replies: None,
}]),
}]);
}
Ok(Some(post))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Prax Relations Examples ===\n");
let client = MockClient;
println!("--- One-to-Many: User with Posts ---");
let user = client
.user()
.find_unique()
.r#where("id = 1")
.include("posts")
.exec()
.await?
.expect("User not found");
println!(
"User: {} ({})",
user.email,
user.name.as_deref().unwrap_or("")
);
if let Some(posts) = &user.posts {
println!("Posts ({}):", posts.len());
for post in posts {
println!(" - {} (id: {})", post.title, post.id);
}
}
println!();
println!("--- One-to-One: User with Profile ---");
let user = client
.user()
.find_unique()
.r#where("id = 1")
.include("profile")
.exec()
.await?
.expect("User not found");
println!("User: {}", user.email);
if let Some(profile) = &user.profile {
println!("Profile bio: {:?}", profile.bio);
}
println!();
println!("--- Multiple Relations ---");
let user = client
.user()
.find_unique()
.r#where("id = 1")
.include("posts")
.include("profile")
.exec()
.await?
.expect("User not found");
println!("User: {}", user.email);
println!("Has posts: {}", user.posts.is_some());
println!("Has profile: {}", user.profile.is_some());
println!();
println!("--- Many-to-Many: Post with Tags ---");
let post = client
.post()
.find_unique()
.r#where("id = 1")
.include("tags")
.exec()
.await?
.expect("Post not found");
println!("Post: {}", post.title);
if let Some(tags) = &post.tags {
println!(
"Tags: {:?}",
tags.iter().map(|t| &t.name).collect::<Vec<_>>()
);
}
println!();
println!("--- Reverse Relation: Post with Author ---");
let post = client
.post()
.find_unique()
.r#where("id = 1")
.include("author")
.exec()
.await?
.expect("Post not found");
println!("Post: {}", post.title);
if let Some(author) = &post.author {
println!(
"Author: {} ({})",
author.email,
author.name.as_deref().unwrap_or("")
);
}
println!();
println!("--- Nested Relations: Post with Comments ---");
let post = client
.post()
.find_unique()
.r#where("id = 1")
.include("comments")
.exec()
.await?
.expect("Post not found");
println!("Post: {}", post.title);
if let Some(comments) = &post.comments {
for comment in comments {
println!(" Comment: {}", comment.content);
if let Some(replies) = &comment.replies {
for reply in replies {
println!(" Reply: {}", reply.content);
}
}
}
}
println!();
println!("--- Nested Writes: Create User with Relations ---");
let new_user = client
.user()
.create(CreateUserData {
email: "new@example.com".to_string(),
name: Some("New User".to_string()),
posts: Some(NestedPostCreate {
data: vec![CreatePostData {
title: "My First Post".to_string(),
content: Some("Hello world!".to_string()),
}],
}),
profile: Some(NestedProfileCreate {
data: CreateProfileData {
bio: Some("Just joined!".to_string()),
},
}),
})
.include("posts")
.include("profile")
.exec()
.await?;
println!("Created user: {} (id: {})", new_user.email, new_user.id);
if let Some(posts) = &new_user.posts {
println!("With {} post(s)", posts.len());
}
if let Some(profile) = &new_user.profile {
println!("With profile: {:?}", profile.bio);
}
println!();
println!("--- Relation Schema Example ---");
let schema_example = r#"
// One-to-Many: User has many Posts
model User {
id Int @id @auto
email String @unique
posts Post[] // One-to-many relation
}
model Post {
id Int @id @auto
title String
authorId Int
author User @relation(fields: [authorId], references: [id])
}
// One-to-One: User has one Profile
model Profile {
id Int @id @auto
bio String?
userId Int @unique // Unique makes it one-to-one
user User @relation(fields: [userId], references: [id])
}
// Many-to-Many: Post has many Tags, Tag has many Posts
model Tag {
id Int @id @auto
name String @unique
posts Post[] @relation("PostTags")
}
// Self-referential: Comment replies
model Comment {
id Int @id @auto
content String
parentId Int?
parent Comment? @relation("CommentReplies", fields: [parentId], references: [id])
replies Comment[] @relation("CommentReplies")
}
"#;
println!("Schema for relations:");
println!("{}", schema_example);
println!("=== All examples completed successfully! ===");
Ok(())
}