Skip to main content

systemprompt_users/repository/user/
directory.rs

1//! Subject lookups for sharing: active users by name prefix and the roles
2//! active users hold.
3//!
4//! Copyright (c) systemprompt.io — Business Source License 1.1.
5//! See <https://systemprompt.io> for licensing details.
6
7use crate::error::Result;
8use crate::models::{User, UserRole, UserRow, UserStatus};
9use crate::repository::UserRepository;
10
11const MAX_DIRECTORY_PAGE: i64 = 50;
12
13fn like_prefix(query: &str) -> String {
14    let escaped = query
15        .replace('\\', "\\\\")
16        .replace('%', "\\%")
17        .replace('_', "\\_");
18    format!("{escaped}%")
19}
20
21impl UserRepository {
22    pub async fn search_active_by_prefix(&self, query: &str, limit: i64) -> Result<Vec<User>> {
23        let pattern = like_prefix(query.trim());
24        let active = UserStatus::Active.as_str();
25        let anonymous = UserRole::Anonymous.as_str();
26        sqlx::query_as!(
27            UserRow,
28            r#"SELECT id, name, email, full_name, display_name, status, email_verified,
29                      roles, avatar_url, is_bot, is_scanner, created_at, updated_at, account_kind
30               FROM users
31               WHERE status = $1 AND NOT ($2 = ANY(roles)) AND NOT is_bot
32                 AND (display_name ILIKE $3 OR name ILIKE $3 OR email ILIKE $3
33                      OR full_name ILIKE $3)
34               ORDER BY COALESCE(display_name, full_name, name), id
35               LIMIT $4"#,
36            active,
37            anonymous,
38            pattern,
39            limit.clamp(1, MAX_DIRECTORY_PAGE),
40        )
41        .fetch_all(&*self.pool)
42        .await?
43        .into_iter()
44        .map(User::try_from)
45        .collect()
46    }
47
48    pub async fn list_active_roles(&self) -> Result<Vec<String>> {
49        let active = UserStatus::Active.as_str();
50        let anonymous = UserRole::Anonymous.as_str();
51        let roles = sqlx::query_scalar!(
52            r#"SELECT DISTINCT role AS "role!" FROM users, UNNEST(roles) AS role
53               WHERE status = $1 AND role <> $2 ORDER BY 1"#,
54            active,
55            anonymous,
56        )
57        .fetch_all(&*self.pool)
58        .await?;
59        Ok(roles)
60    }
61}