8000 Add factory declaration on export attribute by nicolascotton · Pull Request #41 · nicolascotton/nject · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add factory declaration on export attribute #41

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations 8000
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ use nject::{injectable, provider};

mod sub {
use nject::{injectable, module};
use std::rc::Rc;

trait Greeter {
fn greet(&self) -> &str;
Expand All @@ -253,10 +254,13 @@ mod sub {
#[injectable]
struct InternalType(#[inject(123)] i32); // Not visible outside of module.

struct Ref<T>(Rc<T>);

#[injectable]
pub struct Facade<'a> {
hidden: &'a InternalType,
hidden_dyn: &'a dyn Greeter,
hidden_ref: Ref<InternalType>,
}

#[injectable]
Expand All @@ -267,6 +271,9 @@ mod sub {
hidden: InternalType,
#[export(dyn Greeter)]
hidden_dyn: GreeterOne,
#[inject(|x: InternalType| Rc::new(x))]
#[export(Ref<InternalType>, |x| Ref(x.clone()))]
hidden_rc: Rc<InternalType>,
}
}

Expand Down Expand Up @@ -428,7 +435,7 @@ struct Provider;

fn main() {
let factory = Provider.provide::<Factory>();
let dep = factory.create_dep();
let _dep = factory.create_dep();
}
```

Expand Down
22 changes: 13 additions & 9 deletions examples/actix/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use actix_web::{
App, Error,
};
use nject::{injectable, provider};
use user::UserModule;
use user::{ConnectionOptions, UserModule};

type Prov = Data<&'static Provider>;
type Prov = Data<Provider>;

#[injectable]
#[provider]
Expand All @@ -18,16 +18,22 @@ pub struct Provider {
}

impl Provider {
pub fn new() -> Self {
pub fn new(db_url: &str) -> Self {
#[provider]
struct InitProvider;
struct InitProvider<'a> {
#[provide]
conn: ConnectionOptions<'a>,
}

InitProvider.provide()
let init = InitProvider {
conn: ConnectionOptions { url: db_url },
};
init.provide()
}
}

pub fn setup_app(
provider: &'static Provider,
provider: Prov,
) -> App<
impl ServiceFactory<
ServiceRequest,
Expand All @@ -37,7 +43,5 @@ pub fn setup_app(
InitError = (),
> + 'static,
> {
App::new()
.app_data(Data::new(provider))
.service(user::create_scope())
App::new().app_data(provider).service(user::create_scope())
}
11 changes: 6 additions & 5 deletions examples/actix/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
use actix_example::{setup_app, Provider};
use actix_web::HttpServer;
use actix_web::{web::Data, HttpServer};
use sqlx::{query, Connection, SqliteConnection};

const DB_URL: &str = "file::memory:?cache=shared";

#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Initialize database tables
let mut conn = SqliteConnection::connect("file::memory:?cache=shared")
let mut conn = SqliteConnection::connect(DB_URL)
.await
.expect("Unable to connect to database");
query("CREATE TABLE IF NOT EXISTS user (name TEXT NOT NULL)")
.execute(&mut conn)
.await
.expect("Unable to initialize database");

// Using an Arc to share the provider across multiple threads.
let provider: &'static Provider = Box::leak(Box::new(Provider::new()));
let provider = Data::new(Provider::new(DB_URL));
let addr = ("127.0.0.1", 8080);
println!("listening on {}:{}", &addr.0, &addr.1);
HttpServer::new(move || setup_app(provider))
HttpServer::new(move || setup_app(provider.clone()))
.bind(addr)?
.run()
.await
Expand Down
24 changes: 21 additions & 3 deletions examples/actix/src/user/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,30 @@ mod models;
mod repository;
mod routes;
mod service;
use nject::{injectable, module};
use nject::{inject, injectable, module};
pub use routes::create_scope;
use std::ops::Deref;

// The options are specied by a provider
pub struct ConnectionOptions<'a> {
pub url: &'a str,
}

#[derive(Clone)]
struct Pool(sqlx::pool::Pool<sqlx::Sqlite>);

impl Deref for Pool {
type Target = sqlx::pool::Pool<sqlx::Sqlite>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

#[injectable]
#[module]
pub struct UserModule {
#[export]
service: service::UserService,
#[inject(|o: &'prov ConnectionOptions<'prov>| Pool(sqlx::SqlitePool::connect_lazy(o.url).expect("Invalid database URL")))]
#[export(Pool, |x| x.clone())]
pool: Pool,
}
13 changes: 6 additions & 7 deletions examples/actix/src/user/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,26 @@ use super::{
models::{CreateUser, User},
};
use nject::injectable;
use sqlx::{query, query_as, SqlitePool};
use sqlx::{query, query_as};

#[injectable]
pub struct UserRepository {
#[inject(SqlitePool::connect_lazy("file::memory:?cache=shared").expect("Invalid database URL"))]
pool: SqlitePool,
pool: super::Pool,
}

impl UserRepository {
pub async fn get(&self, id: i64) -> Result<User, Error> {
let (id, name) = query_as("SELECT rowid, name FROM user WHERE rowid = ?")
.bind(id)
.fetch_one(&self.pool)
.fetch_one(&*self.pool)
.await?;
Ok(User { id, name })
}
pub async fn update(&self, user: &User) -> Result<(), Error> {
let result = query("UPDATE user SET name = ? WHERE rowid = ?")
.bind(&user.name)
.bind(user.id)
.execute(&self.pool)
.execute(&*self.pool)
.await?;
if result.rows_affected() == 0 {
return Err(Error::NotFound);
Expand All @@ -33,15 +32,15 @@ impl UserRepository {
pub async fn delete(&self, id: i64) -> Result<(), Error> {
query("DELETE FROM user WHERE rowid = ?")
.bind(id)
.execute(&self.pool)
.execute(&*self.pool)
.await?;
Ok(())
}

pub async fn create(&self, user: &CreateUser) -> Result<User, Error> {
let (id, name) = query_as("INSERT INTO user (name) VALUES (?) RETURNING rowid, name")
.bind(&user.name)
.fetch_one(&self.pool)
.fetch_one(&*self.pool)
.await?;
Ok(User { id, name })
}
Expand Down
8 changes: 4 additions & 4 deletions examples/actix/src/user/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub fn create_scope() -> Scope {

#[get("/{user_id}")]
async fn get_user(provider: Prov, user_id: Path<i64>) -> Result<impl Responder> {
let service = provider.provide::<&UserService>();
let service = provider.provide::<UserService>();
let result = service.get(*user_id).await;
match result {
Ok(user) => Ok(Either::Left(web::Json(user))),
Expand All @@ -32,7 +32,7 @@ async fn get_user(provider: Prov, user_id: Path<i64>) -> Result<impl Responder>

#[post("/")]
async fn create_user(provider: Prov, user: Json<CreateUser>) -> Result<impl Responder> {
let service = provider.provide::<&UserService>();
let service = provider.provide::<UserService>();
let result = service.create(&user).await;
match result {
Ok(user) => Ok(Either::Left(web::Json(user))),
Expand All @@ -45,7 +45,7 @@ async fn create_user(provider: Prov, user: Json<CreateUser>) -> Result<impl Resp

#[put("/")]
async fn update_user(provider: Prov, user: Json<User>) -> Result<impl Responder> {
let service = provider.provide::<&UserService>();
let service = provider.provide::<UserService>();
let result = service.update(&user).await;
match result {
Ok(_) => Ok(Either::Left(HttpResponse::Ok())),
Expand All @@ -58,7 +58,7 @@ async fn update_user(provider: Prov, user: Json<User>) -> Result<impl Responder>

#[delete("/{user_id}")]
async fn delete_user(provider: Prov, user_id: Path<i64>) -> Result<impl Responder> {
let service = provider.provide::<&UserService>();
let service = provider.provide::<UserService>();
let result = service.delete(*user_id).await;
match result {
Ok(_) => Ok(Either::Left(HttpResponse::Ok())),
Expand Down
6 changes: 1 addition & 5 deletions examples/axum/src/repository/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,6 @@ impl Repository for MemoryRepository {

fn get(&self, user_id: usize) -> Option<User> {
let users = self.users.lock().unwrap();
if let Some(user) = users.get(user_id) {
Some(user.to_owned())
} else {
None
}
users.get(user_id).map(|user| user.to_owned())
}
}
2 changes: 1 addition & 1 deletion nject-macro/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "nject-macro"
version = "0.4.1"
version = "0.4.2"
edition = "2021"
description = "Zero cost dependency injection macros"
rust-version = "1.60"
Expand Down
9 changes: 8 additions & 1 deletion nject-macro/README.md
E377
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ use nject::{injectable, provider};

mod sub {
use nject::{injectable, module};
use std::rc::Rc;

trait Greeter {
fn greet(&self) -> &str;
Expand All @@ -253,10 +254,13 @@ mod sub {
#[injectable]
struct InternalType(#[inject(123)] i32); // Not visible outside of module.

struct Ref<T>(Rc<T>);

#[injectable]
pub struct Facade<'a> {
hidden: &'a InternalType,
hidden_dyn: &'a dyn Greeter,
hidden_ref: Ref<InternalType>,
}

#[injectable]
Expand All @@ -267,6 +271,9 @@ mod sub {
hidden: InternalType,
#[export(dyn Greeter)]
hidden_dyn: GreeterOne,
#[inject(|x: InternalType| Rc::new(x))]
#[export(Ref<InternalType>, |x| Ref(x.clone()))]
hidden_rc: Rc<InternalType>,
}
}

Expand Down Expand Up @@ -428,7 +435,7 @@ struct Provider;

fn main() {
let factory = Provider.provide::<Factory>();
let dep = factory.create_dep();
let _dep = factory.create_dep();
}
```

Expand Down
52 changes: 43 additions & 9 deletions nject-macro/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use std::path::PathBuf;
use std::{ops::Deref, str::FromStr};
use syn::Token;
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Expand Down Expand Up @@ -47,8 +48,7 @@ impl DeriveInput {
pub fn field_idents(&self) -> Vec<&Ident> {
self.fields()
.iter()
.map(|f| f.ident.as_ref())
.filter_map(|i| i)
.filter_map(|f| f.ident.as_ref())
.collect::<Vec<_>>()
}
pub fn generic_params(&self) -> Vec<&GenericParam> {
Expand Down Expand Up @@ -110,6 +110,43 @@ impl Parse for FactoryExpr {
}
}

pub enum FieldFactoryExpr {
None,
Type(Type),
TypeExpr(Type, Ident, Box<Expr>),
}
impl Parse for FieldFactoryExpr {
fn parse(input: ParseStream) -> syn::Result<Self> {
if input.is_empty() {
return Ok(Self::None);
}
let parsed_type = input.parse()?;
if !input.peek(Token![,]) {
return Ok(Self::Type(parsed_type));
}

input.parse::<Token![,]>()?;
let expr = input.parse::<ExprClosure>()?;
if expr.inputs.is_empty() {
return Err(syn::Error::new(expr.span(), "Missing factory input."));
}
if expr.inputs.len() > 1 {
return Err(syn::Error::new(expr.span(), "More than one input found"));
}

let input = &expr.inputs[0];
if let Pat::Ident(pat_ident) = input {
Ok(Self::TypeExpr(
parsed_type,
pat_ident.ident.to_owned(),
expr.body,
))
} else {
Err(syn::Error::new(input.span(), "Input must be an identity."))
}
}
}

pub fn extract_path_from_type(ty: &Type) -> &Path {
match ty {
Type::Path(p) => &p.path,
Expand All @@ -127,9 +164,7 @@ pub fn cache_path() -> PathBuf {
/// Retry the `action` nth `times` with 100ms between each time.
pub fn retry<T, E>(times: usize, action: impl Fn() -> Result<T, E>) -> Result<T, E> {
let result = action();
if result.is_ok() {
result
} else if times <= 0 {
if result.is_ok() || times < 1 {
result
} else {
std::thread::sleep(std::time::Duration::from_millis(100));
Expand All @@ -151,15 +186,14 @@ pub fn substitute_in_type(ty: &mut Type, from: &str, to: &str) {
Type::Reference(ref mut r) => substitute_in_type(&mut r.elem, from, to),
Type::TraitObject(ref mut t) => {
for bound in &mut t.bounds {
match bound {
syn::TypeParamBound::Trait(t) => substitute_in_path(&mut t.path, from, to),
_ => (),
if let syn::TypeParamBound::Trait(t) = bound {
substitute_in_path(&mut t.path, from, to)
}
}
}
_ => panic!(
"Unsupported type. Must be a Path, Reference or Trait: {}",
ty.to_token_stream().to_string()
ty.to_token_stream()
),
};
}
Expand Down
Loading
0