Skip to main content

teamcraft/services/
mod.rs

1//! Business logic. Every mutation checks permissions and lifecycle rules,
2//! runs in a database transaction and writes an audit log entry.
3//!
4//! Both the web UI and the REST API call into these functions, so the rules
5//! are enforced identically for both.
6
7pub mod access;
8pub mod access_log;
9pub mod access_review;
10pub mod annotations;
11pub mod as_built;
12pub mod assemblies;
13pub mod assistant;
14pub mod assistant_threads;
15pub mod attributes;
16pub mod audit;
17pub mod bom;
18pub mod bom_compare;
19pub mod bom_references;
20pub mod branding;
21pub mod bulk;
22pub mod changes;
23pub mod checkouts;
24pub mod deactivation;
25pub mod desktop;
26pub mod duplicates;
27pub mod explorer;
28pub mod files;
29pub mod folders;
30pub mod impacts;
31pub mod imports;
32pub mod insights;
33pub mod interchange;
34pub mod items;
35pub mod manager_gaps;
36pub mod mass_geometry;
37pub mod mass_properties;
38pub mod membership_conditions;
39pub mod notifications;
40pub mod offboarding;
41pub mod ownership;
42pub mod package;
43pub mod personal;
44pub mod project_policy;
45pub mod projects;
46pub mod quotas;
47pub mod readiness;
48pub mod references;
49pub mod request_digests;
50pub mod requests;
51pub mod result_proposals;
52pub mod result_templates;
53pub mod retention;
54pub mod reviewers;
55pub mod revisions;
56pub mod save_as;
57pub mod season;
58pub mod similar;
59pub mod simulations;
60pub mod stats;
61pub mod structure;
62pub mod uploads;
63pub mod users;
64pub mod webhooks;
65pub mod workset;
66
67use sqlx::PgConnection;
68
69use crate::error::AppResult;
70
71/// Atomically increments and returns the named counter (starting at 1).
72pub(crate) async fn next_counter(conn: &mut PgConnection, name: &str) -> AppResult<i64> {
73    let value = sqlx::query_scalar::<_, i64>(
74        "INSERT INTO counters (name, value) VALUES ($1, 1)
75         ON CONFLICT (name) DO UPDATE SET value = counters.value + 1
76         RETURNING value",
77    )
78    .bind(name)
79    .fetch_one(&mut *conn)
80    .await?;
81    Ok(value)
82}
83
84/// Pagination helper used by list endpoints.
85#[derive(Clone, Copy, Debug)]
86pub struct Page {
87    pub limit: i64,
88    pub offset: i64,
89}
90
91impl Page {
92    pub const DEFAULT_LIMIT: i64 = 50;
93    pub const MAX_LIMIT: i64 = 500;
94
95    pub fn new(limit: Option<i64>, offset: Option<i64>) -> Self {
96        Page {
97            limit: limit
98                .unwrap_or(Self::DEFAULT_LIMIT)
99                .clamp(1, Self::MAX_LIMIT),
100            offset: offset.unwrap_or(0).max(0),
101        }
102    }
103
104    /// 1-based page number used by the web UI.
105    pub fn from_page_number(page: Option<i64>, per_page: i64) -> Self {
106        let page = page.unwrap_or(1).max(1);
107        Page {
108            limit: per_page,
109            offset: (page - 1) * per_page,
110        }
111    }
112}