1use async_trait::async_trait;
4use sova_db::DbHandle;
5use sova_core::Result;
6use std::collections::HashMap;
7use std::future::Future;
8use std::sync::Arc;
9
10#[async_trait]
11pub trait OwnerResolver: Send + Sync {
12 async fn owner_user_id(&self, db: &DbHandle, resource_id: i64) -> Result<Option<i64>>;
13}
14
15#[derive(Default)]
16pub struct OwnerRegistry {
17 resolvers: HashMap<&'static str, Arc<dyn OwnerResolver>>,
18}
19
20impl OwnerRegistry {
21 pub fn register(&mut self, kind: &'static str, resolver: Arc<dyn OwnerResolver>) {
22 self.resolvers.insert(kind, resolver);
23 }
24
25 pub async fn is_owner(
26 &self,
27 db: &DbHandle,
28 resource: Option<crate::types::ResourceRef>,
29 user_id: i64,
30 ) -> Result<bool> {
31 let Some(resource) = resource else {
32 return Ok(false);
33 };
34 let Some(resolver) = self.resolvers.get(resource.kind) else {
35 return Ok(false);
36 };
37 Ok(resolver
38 .owner_user_id(db, resource.id)
39 .await?
40 .is_some_and(|id| id == user_id))
41 }
42}
43
44struct CallbackOwner<F>(F);
45
46#[async_trait]
47impl<F, Fut> OwnerResolver for CallbackOwner<F>
48where
49 F: Fn(DbHandle, i64) -> Fut + Send + Sync,
50 Fut: Future<Output = Result<Option<i64>>> + Send,
51{
52 async fn owner_user_id(&self, db: &DbHandle, resource_id: i64) -> Result<Option<i64>> {
53 (self.0)(db.clone(), resource_id).await
54 }
55}
56
57pub struct FnOwnerResolver<F>(F);
58
59impl<F> FnOwnerResolver<F> {
60 pub fn new(f: F) -> Self {
61 Self(f)
62 }
63}
64
65impl<F, Fut> OwnerResolver for FnOwnerResolver<F>
66where
67 F: Fn(DbHandle, i64) -> Fut + Send + Sync,
68 Fut: Future<Output = Result<Option<i64>>> + Send,
69{
70 fn owner_user_id<'life0, 'life1, 'async_trait>(
71 &'life0 self,
72 db: &'life1 DbHandle,
73 resource_id: i64,
74 ) -> core::pin::Pin<
75 Box<dyn core::future::Future<Output = Result<Option<i64>>> + Send + 'async_trait>,
76 >
77 where
78 'life0: 'async_trait,
79 'life1: 'async_trait,
80 {
81 Box::pin(async move { (self.0)(db.clone(), resource_id).await })
82 }
83}
84
85pub fn callback_owner<F, Fut>(f: F) -> Arc<dyn OwnerResolver>
86where
87 F: Fn(DbHandle, i64) -> Fut + Send + Sync + 'static,
88 Fut: Future<Output = Result<Option<i64>>> + Send + 'static,
89{
90 Arc::new(CallbackOwner(f))
91}