Skip to content

Commit df7aa97

Browse files
authored
Merge pull request #17 from MrRevillod/refactor/di-provider-impl
refactor: 🔥 change the way to declare `providers` and improve the di examples
2 parents a25d9f5 + 809d548 commit df7aa97

File tree

20 files changed

+871
-141
lines changed

20 files changed

+871
-141
lines changed

Cargo.lock

Lines changed: 722 additions & 43 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/config-file/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@ edition = "2021"
66
[dependencies]
77
sword = { workspace = true }
88
serde = { workspace = true }
9-
serde_json = { workspace = true }

examples/dependency-injection/.env

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
POSTGRES_USER=user
2+
POSTGRES_PASSWORD=password
3+
POSTGRES_DB=postgres_db
4+
POSTGRES_DATABASE_URL="postgres://user:password@localhost:5432/postgres_db"

examples/dependency-injection/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ path = "src/main.rs"
1010
[dependencies]
1111
sword = { workspace = true }
1212
serde = { workspace = true }
13-
serde_json = { workspace = true }
14-
tokio = { workspace = true }
13+
sqlx = { version = "0.8.6", features = ["postgres", "runtime-tokio"] }
14+
dotenv = "0.15.0"
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
# Dependency injection example with SQLx
3+
4+
To run this example, make sure you have Docker installed and running on your machine.
5+
6+
## Setup
7+
8+
1. Clone the Sword repository if you haven't already:
9+
10+
```bash
11+
git clone https://github.com/sword-web/sword.git
12+
cd sword/examples/dependency-injection
13+
```
14+
15+
2. Run the PostgreSQL database using Docker Compose:
16+
17+
```bash
18+
docker-compose up -d
19+
```
20+
21+
3. Run the Sword application:
22+
23+
```bash
24+
cargo run
25+
```
26+
27+
## Endpoints
28+
29+
### List tasks
30+
31+
```bash
32+
curl http://localhost:8080/tasks
33+
```
34+
35+
### Create a new task (with default values)
36+
37+
```bash
38+
curl -X POST http://localhost:8080/tasks
39+
```
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
version: '3.8'
2+
3+
services:
4+
postgres:
5+
image: postgres:15
6+
env_file:
7+
- "./.env"
8+
container_name: sword_postgres_example
9+
restart: unless-stopped
10+
environment:
11+
POSTGRES_USER: ${POSTGRES_USER}
12+
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
13+
POSTGRES_DB: ${POSTGRES_DB}
14+
ports:
15+
- "5432:5432"
16+
volumes:
17+
- postgres_data:/var/lib/postgresql/data
18+
19+
volumes:
20+
postgres_data:

examples/dependency-injection/config/config.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ port = 8080
44
body_limit = "10MB"
55
request_timeout_seconds = 15
66
graceful_shutdown = false
7-
name = "MySwordApp"
7+
name = "Dependency Injection Example"
88

99
[db-config]
10-
collection_name = "tasks"
10+
uri = "${POSTGRES_DATABASE_URL}"
11+
migrations_path = "config/migrations"
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
-- Add migration script here
2+
3+
CREATE TABLE tasks(
4+
id INT PRIMARY KEY,
5+
title TEXT NOT NULL
6+
)
Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,42 @@
1-
use std::{collections::HashMap, sync::Arc};
1+
use std::{path::Path, sync::Arc};
22

33
use serde::Deserialize;
4-
use serde_json::Value;
4+
use sqlx::{migrate::Migrator, PgPool};
55
use sword::prelude::*;
6-
use tokio::sync::RwLock;
7-
8-
pub type Store = Arc<RwLock<HashMap<String, Vec<Value>>>>;
96

107
#[derive(Clone, Deserialize)]
118
#[config(key = "db-config")]
129
pub struct DatabaseConfig {
13-
collection_name: String,
10+
uri: String,
11+
migrations_path: String,
1412
}
1513

16-
#[injectable(kind = "provider")]
14+
#[injectable(provider)]
1715
pub struct Database {
18-
db: Store,
16+
pool: Arc<PgPool>,
1917
}
2018

2119
impl Database {
2220
pub async fn new(db_conf: DatabaseConfig) -> Self {
23-
let db = Arc::new(RwLock::new(HashMap::new()));
24-
25-
db.write().await.insert(db_conf.collection_name, Vec::new());
21+
let pool = PgPool::connect(&db_conf.uri)
22+
.await
23+
.expect("Failed to create Postgres connection pool");
2624

27-
Self { db }
28-
}
25+
let migrator = Migrator::new(Path::new(&db_conf.migrations_path))
26+
.await
27+
.unwrap();
2928

30-
pub async fn insert(&self, table: &'static str, record: Value) {
31-
let mut db = self.db.write().await;
29+
migrator
30+
.run(&pool)
31+
.await
32+
.expect("Failed to run database migrations");
3233

33-
if let Some(table_data) = db.get_mut(table) {
34-
table_data.push(record);
34+
Self {
35+
pool: Arc::new(pool),
3536
}
3637
}
3738

38-
pub async fn get_all(&self, table: &'static str) -> Option<Vec<Value>> {
39-
let db = self.db.read().await;
40-
41-
db.get(table).cloned()
39+
pub fn get_pool(&self) -> &PgPool {
40+
&self.pool
4241
}
4342
}

examples/dependency-injection/src/main.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,23 @@
11
mod database;
22
mod middleware;
33
mod repository;
4-
mod service;
54

65
use std::sync::Arc;
76

7+
use dotenv::dotenv;
88
pub use middleware::MyMiddleware;
99
pub use repository::TaskRepository;
1010

11-
use serde_json::json;
1211
use sword::{core::DependencyContainer, prelude::*};
1312

1413
use crate::{
1514
database::{Database, DatabaseConfig},
16-
service::TasksService,
15+
repository::Task,
1716
};
1817

19-
#[controller("/tasks", version = "v1")]
18+
#[controller("/tasks")]
2019
struct TasksController {
21-
tasks: Arc<TasksService>,
20+
tasks: Arc<TaskRepository>,
2221
}
2322

2423
#[routes]
@@ -33,12 +32,13 @@ impl TasksController {
3332

3433
#[post("/")]
3534
async fn create_task(&self) -> HttpResponse {
36-
let total_task = self.tasks.find_all().await.len();
35+
let tasks = self.tasks.find_all().await;
36+
let total_count = tasks.len() as i32 + 1;
3737

38-
let task = json!({
39-
"id": total_task + 1,
40-
"title": format!("Task {}", total_task + 1),
41-
});
38+
let task = Task {
39+
id: total_count,
40+
title: format!("Task {total_count}"),
41+
};
4242

4343
self.tasks.create(task.clone()).await;
4444

@@ -48,6 +48,8 @@ impl TasksController {
4848

4949
#[sword::main]
5050
async fn main() {
51+
dotenv().ok();
52+
5153
let app = Application::builder();
5254
let db_config = app.config::<DatabaseConfig>().unwrap();
5355

@@ -56,7 +58,6 @@ async fn main() {
5658
let container = DependencyContainer::builder()
5759
.register_provider(db)
5860
.register_component::<TaskRepository>()
59-
.register_component::<TasksService>()
6061
.build();
6162

6263
let app = app

0 commit comments

Comments
 (0)