Skip to content
This repository was archived by the owner on Jun 30, 2025. It is now read-only.

Commit f20a0c9

Browse files
tunamaguroclaude
andcommitted
feat: struct-based APIとデータベースクレート型生成の修正
- 全データベースクレート対応の型生成修正 - struct-based APIによるゼロコスト抽象化実装 - copy_types最適化統合と後方互換性保持 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 273294e commit f20a0c9

File tree

15 files changed

+2270
-349
lines changed

15 files changed

+2270
-349
lines changed

examples/authors/src/queries.rs

Lines changed: 116 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,49 @@ pub struct GetAuthorRow {
1010
pub name: String,
1111
pub bio: Option<String>,
1212
}
13+
impl GetAuthorRow {
14+
pub(crate) fn from_row(row: &tokio_postgres::Row) -> Result<Self, tokio_postgres::Error> {
15+
Ok(GetAuthorRow {
16+
id: row.try_get(0)?,
17+
name: row.try_get(1)?,
18+
bio: row.try_get(2)?,
19+
})
20+
}
21+
}
1322
pub async fn get_author(
1423
client: &impl tokio_postgres::GenericClient,
1524
id: i64,
1625
) -> Result<Option<GetAuthorRow>, tokio_postgres::Error> {
17-
let row = client.query_opt(GET_AUTHOR, &[&id]).await?;
18-
let v = match row {
19-
Some(v) => GetAuthorRow {
20-
id: v.try_get(0)?,
21-
name: v.try_get(1)?,
22-
bio: v.try_get(2)?,
23-
},
24-
None => return Ok(None),
25-
};
26-
Ok(Some(v))
26+
let query_struct = GetAuthor { id: id };
27+
query_struct.query_opt(client).await
28+
}
29+
#[derive(Debug)]
30+
pub struct GetAuthor {
31+
pub id: i64,
32+
}
33+
impl GetAuthor {
34+
pub const QUERY: &'static str = r#"-- name: GetAuthor :one
35+
SELECT id, name, bio FROM authors
36+
WHERE id = $1 LIMIT 1"#;
37+
}
38+
impl GetAuthor {
39+
pub async fn query_one(
40+
&self,
41+
client: &impl tokio_postgres::GenericClient,
42+
) -> Result<GetAuthorRow, tokio_postgres::Error> {
43+
let row = client.query_one(Self::QUERY, &[&self.id]).await?;
44+
GetAuthorRow::from_row(&row)
45+
}
46+
pub async fn query_opt(
47+
&self,
48+
client: &impl tokio_postgres::GenericClient,
49+
) -> Result<Option<GetAuthorRow>, tokio_postgres::Error> {
50+
let row = client.query_opt(Self::QUERY, &[&self.id]).await?;
51+
match row {
52+
Some(ref row) => Ok(Some(GetAuthorRow::from_row(row)?)),
53+
None => Ok(None),
54+
}
55+
}
2756
}
2857
pub const LIST_AUTHORS: &str = r#"-- name: ListAuthors :many
2958
SELECT id, name, bio FROM authors
@@ -34,20 +63,23 @@ pub struct ListAuthorsRow {
3463
pub name: String,
3564
pub bio: Option<String>,
3665
}
66+
impl ListAuthorsRow {
67+
pub(crate) fn from_row(row: &tokio_postgres::Row) -> Result<Self, tokio_postgres::Error> {
68+
Ok(ListAuthorsRow {
69+
id: row.try_get(0)?,
70+
name: row.try_get(1)?,
71+
bio: row.try_get(2)?,
72+
})
73+
}
74+
}
3775
pub async fn list_authors(
3876
client: &impl tokio_postgres::GenericClient,
3977
) -> Result<
4078
impl Iterator<Item = Result<ListAuthorsRow, tokio_postgres::Error>>,
4179
tokio_postgres::Error,
4280
> {
4381
let rows = client.query(LIST_AUTHORS, &[]).await?;
44-
Ok(rows.into_iter().map(|r| {
45-
Ok(ListAuthorsRow {
46-
id: r.try_get(0)?,
47-
name: r.try_get(1)?,
48-
bio: r.try_get(2)?,
49-
})
50-
}))
82+
Ok(rows.into_iter().map(|r| ListAuthorsRow::from_row(&r)))
5183
}
5284
pub const CREATE_AUTHOR: &str = r#"-- name: CreateAuthor :one
5385
INSERT INTO authors (
@@ -62,21 +94,62 @@ pub struct CreateAuthorRow {
6294
pub name: String,
6395
pub bio: Option<String>,
6496
}
97+
impl CreateAuthorRow {
98+
pub(crate) fn from_row(row: &tokio_postgres::Row) -> Result<Self, tokio_postgres::Error> {
99+
Ok(CreateAuthorRow {
100+
id: row.try_get(0)?,
101+
name: row.try_get(1)?,
102+
bio: row.try_get(2)?,
103+
})
104+
}
105+
}
65106
pub async fn create_author(
66107
client: &impl tokio_postgres::GenericClient,
67108
name: &str,
68109
bio: Option<&str>,
69110
) -> Result<Option<CreateAuthorRow>, tokio_postgres::Error> {
70-
let row = client.query_opt(CREATE_AUTHOR, &[&name, &bio]).await?;
71-
let v = match row {
72-
Some(v) => CreateAuthorRow {
73-
id: v.try_get(0)?,
74-
name: v.try_get(1)?,
75-
bio: v.try_get(2)?,
76-
},
77-
None => return Ok(None),
111+
let query_struct = CreateAuthor {
112+
name: std::borrow::Cow::Borrowed(name),
113+
bio: bio.map(std::borrow::Cow::Borrowed),
78114
};
79-
Ok(Some(v))
115+
query_struct.query_opt(client).await
116+
}
117+
#[derive(Debug)]
118+
pub struct CreateAuthor<'a> {
119+
pub name: std::borrow::Cow<'a, str>,
120+
pub bio: Option<std::borrow::Cow<'a, str>>,
121+
}
122+
impl<'a> CreateAuthor<'a> {
123+
pub const QUERY: &'static str = r#"-- name: CreateAuthor :one
124+
INSERT INTO authors (
125+
name, bio
126+
) VALUES (
127+
$1, $2
128+
)
129+
RETURNING id, name, bio"#;
130+
}
131+
impl<'a> CreateAuthor<'a> {
132+
pub async fn query_one(
133+
&self,
134+
client: &impl tokio_postgres::GenericClient,
135+
) -> Result<CreateAuthorRow, tokio_postgres::Error> {
136+
let row = client
137+
.query_one(Self::QUERY, &[&self.name.as_ref(), &self.bio.as_deref()])
138+
.await?;
139+
CreateAuthorRow::from_row(&row)
140+
}
141+
pub async fn query_opt(
142+
&self,
143+
client: &impl tokio_postgres::GenericClient,
144+
) -> Result<Option<CreateAuthorRow>, tokio_postgres::Error> {
145+
let row = client
146+
.query_opt(Self::QUERY, &[&self.name.as_ref(), &self.bio.as_deref()])
147+
.await?;
148+
match row {
149+
Some(ref row) => Ok(Some(CreateAuthorRow::from_row(row)?)),
150+
None => Ok(None),
151+
}
152+
}
80153
}
81154
pub const DELETE_AUTHOR: &str = r#"-- name: DeleteAuthor :exec
82155
DELETE FROM authors
@@ -87,3 +160,20 @@ pub async fn delete_author(
87160
) -> Result<u64, tokio_postgres::Error> {
88161
client.execute(DELETE_AUTHOR, &[&id]).await
89162
}
163+
#[derive(Debug)]
164+
pub struct DeleteAuthor {
165+
pub id: i64,
166+
}
167+
impl DeleteAuthor {
168+
pub const QUERY: &'static str = r#"-- name: DeleteAuthor :exec
169+
DELETE FROM authors
170+
WHERE id = $1"#;
171+
}
172+
impl DeleteAuthor {
173+
pub async fn execute(
174+
&self,
175+
client: &impl tokio_postgres::GenericClient,
176+
) -> Result<u64, tokio_postgres::Error> {
177+
client.execute(Self::QUERY, &[&self.id]).await
178+
}
179+
}

0 commit comments

Comments
 (0)