Skip to main content

kellnr_db/
provider.rs

1use std::collections::HashMap;
2use std::path::Path;
3
4use chrono::{DateTime, Utc};
5use crate_meta::CrateMeta;
6use kellnr_common::account_name::{GroupName, UserName};
7use kellnr_common::canonical_name::CanonicalName;
8use kellnr_common::crate_data::CrateData;
9use kellnr_common::crate_dependents::CrateDependents;
10use kellnr_common::crate_overview::CrateOverview;
11use kellnr_common::cratesio_prefetch_msg::CratesioPrefetchMsg;
12use kellnr_common::index_metadata::IndexMetadata;
13use kellnr_common::original_name::OriginalName;
14use kellnr_common::plain_token::PlainToken;
15use kellnr_common::prefetch::Prefetch;
16use kellnr_common::publish_metadata::PublishMetadata;
17use kellnr_common::version::Version;
18use kellnr_common::webhook::{Webhook, WebhookEvent, WebhookQueue};
19use sea_orm::prelude::async_trait::async_trait;
20use serde::{Deserialize, Serialize};
21
22use crate::error::DbError;
23use crate::{
24    AuthToken, AuthTokenId, CrateId, DocQueueEntry, Group, ToolchainTargetStatus, User, UserFlags,
25    crate_meta,
26};
27
28pub type DbResult<T> = Result<T, DbError>;
29
30#[derive(Debug, PartialEq, Eq)]
31pub enum PrefetchState {
32    /// The client's cached index is current. Carries the validators stored in
33    /// the database, which are the only ones that may be sent upstream.
34    UpToDate {
35        etag: String,
36        last_modified: String,
37    },
38    NeedsUpdate(Prefetch),
39    NotFound,
40}
41
42/// Checksum, license and publish time of a cached crates.io crate version.
43#[derive(Debug, Clone, PartialEq, Eq)]
44pub struct CratesioVersionMeta {
45    pub cksum: String,
46    /// `None` if the license was not fetched yet, `UNKNOWN_LICENSE` if the
47    /// version declares none.
48    pub license: Option<String>,
49    /// `pubtime` of the index entry, `None` if it has none.
50    pub pubtime: Option<DateTime<Utc>>,
51}
52
53/// A version of a cached crates.io crate, with the name as written in its
54/// index entry, which is also the name of its cached `.crate` file.
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct CachedCratesioVersion {
57    pub name: OriginalName,
58    pub version: String,
59}
60
61/// Which download-restricted crates a caller may see in crate listings.
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub enum CrateVisibility {
64    /// All crates, used for admins.
65    All,
66    /// Unrestricted crates and the restricted crates the user owns or may
67    /// access as crate user or crate group member.
68    User(UserName),
69    /// Unrestricted crates only, used for anonymous callers.
70    Unrestricted,
71}
72
73/// Order of the crate overview list.
74#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
75#[serde(rename_all = "lowercase")]
76pub enum CrateSort {
77    /// Alphabetical by name.
78    #[default]
79    Name,
80    /// Most recently updated first.
81    Updated,
82    /// Most downloaded first.
83    Downloads,
84}
85
86/// Restricts the crate overview list to crates with a keyword and/or a
87/// category. Both match case-insensitively and ignore crates.io proxy crates,
88/// which have neither.
89#[derive(Debug, Clone, Default, PartialEq, Eq)]
90pub struct CrateTagFilter {
91    pub keyword: Option<String>,
92    pub category: Option<String>,
93}
94
95impl CrateTagFilter {
96    pub fn is_empty(&self) -> bool {
97        self.keyword.is_none() && self.category.is_none()
98    }
99}
100
101/// Authenticated user and privileges resolved from a valid session cookie.
102#[derive(Debug, Clone, PartialEq, Eq)]
103pub struct SessionInfo {
104    pub name: UserName,
105    pub is_admin: bool,
106    pub is_read_only: bool,
107}
108
109/// Data stored during `OAuth2` authentication flow for CSRF/PKCE verification
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct OAuth2StateData {
112    pub state: String,
113    pub pkce_verifier: String,
114    pub nonce: String,
115}
116
117/// Information about an individual toolchain component (e.g., rustc, cargo)
118#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
119pub struct ToolchainComponentInfo {
120    /// Component name (e.g., "rustc", "cargo", "rust-std-x86_64-unknown-linux-gnu")
121    pub name: String,
122    /// Path to the stored component archive
123    pub storage_path: String,
124    /// SHA256 hash of the component archive
125    pub hash: String,
126    /// Component archive size in bytes
127    pub size: i64,
128}
129
130impl From<kellnr_entity::toolchain_component::Model> for ToolchainComponentInfo {
131    fn from(c: kellnr_entity::toolchain_component::Model) -> Self {
132        Self {
133            name: c.name,
134            storage_path: c.storage_path,
135            hash: c.hash,
136            size: c.size,
137        }
138    }
139}
140
141/// Toolchain target information (e.g., a specific archive for a target triple)
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
143pub struct ToolchainTargetInfo {
144    /// Target ID
145    pub id: i64,
146    /// Target triple (e.g., "x86_64-unknown-linux-gnu")
147    pub target: String,
148    /// Path to the stored archive
149    pub storage_path: String,
150    /// SHA256 hash of the archive
151    pub hash: String,
152    /// Archive size in bytes
153    pub size: i64,
154    /// Processing status
155    pub status: ToolchainTargetStatus,
156    /// Individual components extracted from the combined archive
157    pub components: Vec<ToolchainComponentInfo>,
158}
159
160/// Toolchain with all its targets
161#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
162pub struct ToolchainWithTargets {
163    /// Toolchain ID
164    pub id: i64,
165    /// Toolchain name (e.g., "rust")
166    pub name: String,
167    /// Toolchain version (e.g., "1.75.0")
168    pub version: String,
169    /// Release date
170    pub date: String,
171    /// Channel (e.g., "stable", "beta", "nightly")
172    pub channel: Option<String>,
173    /// Creation timestamp
174    pub created: String,
175    /// Available targets for this toolchain
176    pub targets: Vec<ToolchainTargetInfo>,
177}
178
179/// Channel information (e.g., "stable" -> "1.75.0")
180#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
181pub struct ChannelInfo {
182    /// Channel name (e.g., "stable", "beta", "nightly")
183    pub name: String,
184    /// Toolchain version pointed to by this channel
185    pub version: String,
186    /// Release date
187    pub date: String,
188}
189
190#[cfg_attr(feature = "mock", mockall::automock)]
191#[async_trait]
192pub trait DbProvider: Send + Sync {
193    async fn get_last_updated_crate(
194        &self,
195        visibility: &CrateVisibility,
196    ) -> DbResult<Option<(OriginalName, Version)>>;
197    async fn authenticate_user(&self, name: &UserName, pwd: &str) -> DbResult<User>;
198    async fn increase_download_counter(
199        &self,
200        crate_name: &CanonicalName,
201        crate_version: &Version,
202    ) -> DbResult<()>;
203    async fn increase_cached_download_counter(
204        &self,
205        crate_name: &CanonicalName,
206        crate_version: &Version,
207    ) -> DbResult<()>;
208    async fn increase_download_counter_by(
209        &self,
210        crate_name: &CanonicalName,
211        crate_version: &Version,
212        count: u64,
213    ) -> DbResult<()>;
214    async fn increase_cached_download_counter_by(
215        &self,
216        crate_name: &CanonicalName,
217        crate_version: &Version,
218        count: u64,
219    ) -> DbResult<()>;
220    async fn validate_session(&self, session_token: &PlainToken) -> DbResult<SessionInfo>;
221    /// Delete all sessions that have outlived the configured session age.
222    /// Returns the number of rows removed.
223    async fn delete_expired_sessions(&self) -> DbResult<u64>;
224    async fn add_session_token(&self, name: &UserName, session_token: &PlainToken) -> DbResult<()>;
225    /// Delete all sessions of a user. Returns the number of rows removed.
226    async fn delete_user_sessions(&self, user_name: &UserName) -> DbResult<u64>;
227    /// Delete all sessions of a user except `keep_session_token`. Returns the
228    /// number of rows removed.
229    async fn delete_other_user_sessions(
230        &self,
231        user_name: &UserName,
232        keep_session_token: &PlainToken,
233    ) -> DbResult<u64>;
234    async fn add_crate_user(&self, crate_name: &CanonicalName, user: &UserName) -> DbResult<()>;
235    /// Adds `owner` to the owners of the crate. Adding an existing owner
236    /// succeeds without a change.
237    async fn add_owner(&self, crate_name: &CanonicalName, owner: &UserName) -> DbResult<()>;
238    async fn is_download_restricted(&self, crate_name: &CanonicalName) -> DbResult<bool>;
239    async fn change_download_restricted(
240        &self,
241        crate_name: &CanonicalName,
242        restricted: bool,
243    ) -> DbResult<()>;
244    async fn is_crate_user(&self, crate_name: &CanonicalName, user: &UserName) -> DbResult<bool>;
245    async fn is_owner(&self, crate_name: &CanonicalName, user: &UserName) -> DbResult<bool>;
246    async fn get_crate_id(&self, crate_name: &CanonicalName) -> DbResult<Option<CrateId>>;
247    async fn get_crate_owners(&self, crate_name: &CanonicalName) -> DbResult<Vec<User>>;
248    async fn get_crate_users(&self, crate_name: &CanonicalName) -> DbResult<Vec<User>>;
249    async fn get_crate_versions(&self, crate_name: &CanonicalName) -> DbResult<Vec<Version>>;
250    async fn delete_session_token(&self, session_token: &PlainToken) -> DbResult<()>;
251    async fn delete_user(&self, user_name: &UserName) -> DbResult<()>;
252    async fn change_pwd(&self, user_name: &UserName, new_pwd: &str) -> DbResult<()>;
253    async fn change_read_only_state(&self, user_name: &UserName, state: bool) -> DbResult<()>;
254    async fn change_admin_state(&self, user_name: &UserName, state: bool) -> DbResult<()>;
255    async fn crate_version_exists(&self, crate_id: CrateId, version: &Version) -> DbResult<bool>;
256    async fn get_max_version_from_id(&self, crate_id: CrateId) -> DbResult<Version>;
257    async fn get_max_version_from_name(&self, crate_name: &CanonicalName) -> DbResult<Version>;
258    async fn add_auth_token(&self, name: &str, token: &PlainToken, user: &UserName)
259    -> DbResult<()>;
260    async fn get_user_from_token(&self, token: &PlainToken) -> DbResult<User>;
261    /// Name of the auth token, `None` if there is no such token.
262    async fn get_auth_token_name(&self, token: &PlainToken) -> DbResult<Option<String>>;
263    async fn get_user(&self, name: &UserName) -> DbResult<User>;
264    async fn get_auth_tokens(&self, user_name: &UserName) -> DbResult<Vec<AuthToken>>;
265    /// Deletes the token `id` of the user, [`DbError::TokenNotFound`] if the
266    /// user has no such token.
267    async fn delete_auth_token(&self, user_name: &UserName, id: AuthTokenId) -> DbResult<()>;
268    /// Removes all `owners` from the crate in one transaction. Nothing is
269    /// removed if one of them is not an owner ([`DbError::OwnerNotFound`]) or
270    /// if no owner would remain and `allow_ownerless` is false
271    /// ([`DbError::LastOwner`]).
272    async fn delete_owners(
273        &self,
274        crate_name: &CanonicalName,
275        owners: &[UserName],
276        allow_ownerless: bool,
277    ) -> DbResult<()>;
278    async fn delete_crate_user(&self, crate_name: &CanonicalName, user: &UserName) -> DbResult<()>;
279    async fn add_user(&self, name: &UserName, pwd: &str, flags: UserFlags) -> DbResult<()>;
280    async fn get_users(&self) -> DbResult<Vec<User>>;
281    async fn add_group(&self, name: &GroupName) -> DbResult<()>;
282    async fn get_groups(&self) -> DbResult<Vec<Group>>;
283    async fn delete_group(&self, name: &GroupName) -> DbResult<()>;
284    async fn add_group_user(&self, group_name: &GroupName, user: &UserName) -> DbResult<()>;
285    async fn delete_group_user(&self, group_name: &GroupName, user: &UserName) -> DbResult<()>;
286    async fn get_group_users(&self, group_name: &GroupName) -> DbResult<Vec<User>>;
287    async fn is_group_user(&self, group_name: &GroupName, user: &UserName) -> DbResult<bool>;
288    async fn add_crate_group(&self, crate_name: &CanonicalName, group: &GroupName) -> DbResult<()>;
289    async fn delete_crate_group(
290        &self,
291        crate_name: &CanonicalName,
292        group: &GroupName,
293    ) -> DbResult<()>;
294    async fn get_crate_groups(&self, crate_name: &CanonicalName) -> DbResult<Vec<Group>>;
295    async fn is_crate_group(&self, crate_name: &CanonicalName, group: &GroupName)
296    -> DbResult<bool>;
297    async fn is_crate_group_user(
298        &self,
299        crate_name: &CanonicalName,
300        user: &UserName,
301    ) -> DbResult<bool>;
302    async fn get_total_unique_crates(&self) -> DbResult<u64>;
303    async fn get_total_crate_versions(&self) -> DbResult<u64>;
304    async fn get_total_downloads(&self) -> DbResult<u64>;
305    async fn get_top_crates_downloads(
306        &self,
307        top: u64,
308        visibility: &CrateVisibility,
309    ) -> DbResult<Vec<(String, u64)>>;
310    async fn get_total_unique_cached_crates(&self) -> DbResult<u64>;
311    async fn get_total_cached_crate_versions(&self) -> DbResult<u64>;
312    async fn get_total_cached_downloads(&self) -> DbResult<u64>;
313    async fn add_doc_queue(
314        &self,
315        krate: &OriginalName,
316        version: &Version,
317        path: &Path,
318    ) -> DbResult<()>;
319    async fn delete_doc_queue(&self, id: i64) -> DbResult<()>;
320    async fn get_doc_queue(&self) -> DbResult<Vec<DocQueueEntry>>;
321    async fn delete_crate(&self, krate: &CanonicalName, version: &Version) -> DbResult<()>;
322    async fn get_crate_meta_list(&self, crate_name: &CanonicalName) -> DbResult<Vec<CrateMeta>>;
323    async fn search_in_crate_name_and_description(
324        &self,
325        contains: &str,
326        limit: u64,
327        offset: u64,
328        cache: bool,
329        visibility: &CrateVisibility,
330    ) -> DbResult<Vec<CrateOverview>>;
331    async fn count_by_crate_name_and_description(
332        &self,
333        contains: &str,
334        cache: bool,
335        visibility: &CrateVisibility,
336    ) -> DbResult<u64>;
337    async fn get_crate_overview_list(
338        &self,
339        limit: u64,
340        offset: u64,
341        cache: bool,
342        visibility: &CrateVisibility,
343        sort: CrateSort,
344        tags: &CrateTagFilter,
345    ) -> DbResult<Vec<CrateOverview>>;
346    async fn count_crate_overview_list(
347        &self,
348        cache: bool,
349        visibility: &CrateVisibility,
350        tags: &CrateTagFilter,
351    ) -> DbResult<u64>;
352    async fn get_crate_data(&self, crate_name: &CanonicalName) -> DbResult<CrateData>;
353    /// Crates whose latest non-yanked version depends on the crate, with the
354    /// latest version of the crate. `restricted` is always 0, the caller
355    /// decides which dependents to hide.
356    async fn get_crate_dependents(&self, crate_name: &CanonicalName) -> DbResult<CrateDependents>;
357    async fn add_empty_crate(&self, name: &str, created: &DateTime<Utc>) -> DbResult<CrateId>;
358    /// Adds a published crate version. `owner` is the publishing user, who
359    /// becomes the owner of a new crate. `token_name` is the name of the auth
360    /// token used to publish, `None` if unknown.
361    async fn add_crate(
362        &self,
363        pub_metadata: &PublishMetadata,
364        cksum: &str,
365        size: i64,
366        created: &DateTime<Utc>,
367        owner: &UserName,
368        token_name: Option<String>,
369    ) -> DbResult<CrateId>;
370    async fn update_docs_link(
371        &self,
372        crate_name: &CanonicalName,
373        version: &Version,
374        docs_link: &str,
375    ) -> DbResult<()>;
376    async fn get_prefetch_data(&self, crate_name: &OriginalName) -> DbResult<Prefetch>;
377    async fn is_cratesio_cache_up_to_date(
378        &self,
379        crate_name: &OriginalName,
380        if_none_match: Option<String>,
381        if_modified_since: Option<String>,
382    ) -> DbResult<PrefetchState>;
383    async fn add_cratesio_prefetch_data(
384        &self,
385        crate_name: &OriginalName,
386        etag: &str,
387        last_modified: &str,
388        description: Option<String>,
389        indices: &[IndexMetadata],
390        licenses: &HashMap<String, String>,
391    ) -> DbResult<Prefetch>;
392    /// Removes the cached versions of a crates.io crate that are not in
393    /// `upstream_versions`, as they were deleted upstream. Returns the removed
394    /// versions. The cached `.crate` files are not touched.
395    async fn remove_deleted_cratesio_versions(
396        &self,
397        crate_name: &OriginalName,
398        upstream_versions: &[String],
399    ) -> DbResult<Vec<CachedCratesioVersion>>;
400    /// Removes a crate that was deleted from crates.io from the cache, if it
401    /// is cached under the separators of `crate_name`. Returns the removed
402    /// versions. The cached `.crate` files are not touched.
403    async fn delete_cratesio_crate(
404        &self,
405        crate_name: &OriginalName,
406    ) -> DbResult<Vec<CachedCratesioVersion>>;
407    async fn get_cratesio_index_update_list(&self) -> DbResult<Vec<CratesioPrefetchMsg>>;
408    /// Checksum, license and publish time of a crates.io crate version from
409    /// the cached index, if the version is known.
410    async fn get_cratesio_version_meta(
411        &self,
412        crate_name: &CanonicalName,
413        version: &Version,
414    ) -> DbResult<Option<CratesioVersionMeta>>;
415    /// License of a Kellnr crate version, `None` if it declares none or the
416    /// version does not exist.
417    async fn get_crate_license(
418        &self,
419        crate_name: &CanonicalName,
420        version: &Version,
421    ) -> DbResult<Option<String>>;
422    async fn unyank_crate(&self, crate_name: &CanonicalName, version: &Version) -> DbResult<()>;
423    async fn yank_crate(&self, crate_name: &CanonicalName, version: &Version) -> DbResult<()>;
424    async fn register_webhook(&self, webhook: Webhook) -> DbResult<String>;
425    async fn delete_webhook(&self, id: &str) -> DbResult<()>;
426    async fn get_webhook(&self, id: &str) -> DbResult<Webhook>;
427    async fn get_all_webhooks(&self) -> DbResult<Vec<Webhook>>;
428    /// Creates a new webhook queue entry for each register webhook
429    /// matching the given event. `Next_attempt` is set to current time,
430    ///  in order to trigger immediate dispatch.
431    async fn add_webhook_queue(
432        &self,
433        event: WebhookEvent,
434        payload: serde_json::Value,
435    ) -> DbResult<()>;
436    /// Extracts webhook queue entries with `next_attempt` at or earlier than provided timestamp.
437    async fn get_pending_webhook_queue_entries(
438        &self,
439        timestamp: DateTime<Utc>,
440    ) -> DbResult<Vec<WebhookQueue>>;
441    async fn update_webhook_queue(
442        &self,
443        id: &str,
444        last_attempt: DateTime<Utc>,
445        next_attempt: DateTime<Utc>,
446    ) -> DbResult<()>;
447    async fn delete_webhook_queue(&self, id: &str) -> DbResult<()>;
448
449    // `OAuth2` identity methods
450    /// Look up a user by their `OAuth2` identity (issuer + subject)
451    async fn get_user_by_oauth2_identity(
452        &self,
453        issuer: &str,
454        subject: &str,
455    ) -> DbResult<Option<User>>;
456
457    /// Whether any admin user has an `OAuth2` identity linked.
458    ///
459    /// Used at startup to tell a usable SSO-only deployment apart from one that
460    /// has locked itself out: the default local admin always exists, so the
461    /// presence of an admin alone says nothing.
462    async fn has_oauth2_admin(&self) -> DbResult<bool>;
463
464    /// Create a new user from `OAuth2` authentication and link their identity
465    async fn create_oauth2_user(
466        &self,
467        username: &UserName,
468        issuer: &str,
469        subject: &str,
470        email: Option<String>,
471        flags: UserFlags,
472    ) -> DbResult<User>;
473
474    // `OAuth2` state methods (CSRF/PKCE during auth flow)
475    /// Store `OAuth2` state for CSRF/PKCE verification during auth flow
476    async fn store_oauth2_state(
477        &self,
478        state: &str,
479        pkce_verifier: &str,
480        nonce: &str,
481    ) -> DbResult<()>;
482
483    /// Retrieve and delete `OAuth2` state (single use)
484    async fn get_and_delete_oauth2_state(&self, state: &str) -> DbResult<Option<OAuth2StateData>>;
485
486    /// Clean up expired `OAuth2` states (older than 10 minutes)
487    async fn cleanup_expired_oauth2_states(&self) -> DbResult<u64>;
488
489    /// Check if username is available (for `OAuth2` auto-provisioning)
490    async fn is_username_available(&self, username: &UserName) -> DbResult<bool>;
491
492    // Toolchain distribution methods
493    /// Add a new toolchain release
494    async fn add_toolchain(
495        &self,
496        name: &str,
497        version: &str,
498        date: &str,
499        channel: Option<String>,
500    ) -> DbResult<i64>;
501
502    /// Add a target archive to an existing toolchain
503    async fn add_toolchain_target(
504        &self,
505        toolchain_id: i64,
506        target: &str,
507        storage_path: &str,
508        hash: &str,
509        size: i64,
510    ) -> DbResult<i64>;
511
512    /// Add a component to a toolchain target
513    async fn add_toolchain_component(
514        &self,
515        target_id: i64,
516        name: &str,
517        storage_path: &str,
518        hash: &str,
519        size: i64,
520    ) -> DbResult<()>;
521
522    /// Whether a component of any toolchain target is stored at `storage_path`
523    async fn is_toolchain_component_stored_at(&self, storage_path: &str) -> DbResult<bool>;
524
525    /// Update the processing status of a toolchain target
526    async fn set_target_status(
527        &self,
528        target_id: i64,
529        status: ToolchainTargetStatus,
530    ) -> DbResult<()>;
531
532    /// Mark all toolchain targets that are still processing as failed.
533    /// Returns the number of marked targets.
534    async fn fail_processing_toolchain_targets(&self) -> DbResult<u64>;
535
536    /// Get a toolchain by its channel (e.g., "stable", "nightly")
537    async fn get_toolchain_by_channel(
538        &self,
539        channel: &str,
540    ) -> DbResult<Option<ToolchainWithTargets>>;
541
542    /// Get a toolchain by name and version
543    async fn get_toolchain_by_version(
544        &self,
545        name: &str,
546        version: &str,
547    ) -> DbResult<Option<ToolchainWithTargets>>;
548
549    /// List all toolchains
550    async fn list_toolchains(&self) -> DbResult<Vec<ToolchainWithTargets>>;
551
552    /// Delete a toolchain and all its targets
553    async fn delete_toolchain(&self, name: &str, version: &str) -> DbResult<()>;
554
555    /// Delete a specific toolchain target
556    async fn delete_toolchain_target(
557        &self,
558        name: &str,
559        version: &str,
560        target: &str,
561    ) -> DbResult<()>;
562
563    /// Set a channel to point to a specific toolchain version
564    async fn set_channel(&self, channel: &str, name: &str, version: &str) -> DbResult<()>;
565
566    /// Get all channels with their current versions
567    async fn get_channels(&self) -> DbResult<Vec<ChannelInfo>>;
568
569    /// The name a crate was first published with, or `None` if the crate
570    /// does not exist. Crate files are stored under this name.
571    async fn get_crate_original_name(
572        &self,
573        crate_name: &CanonicalName,
574    ) -> DbResult<Option<OriginalName>>;
575
576    /// The original name of the crate if the crate has the version.
577    async fn get_original_name(
578        &self,
579        crate_name: &CanonicalName,
580        version: &Version,
581    ) -> DbResult<Option<OriginalName>>;
582}
583
584#[cfg(feature = "mock")]
585pub mod mock {
586    pub use super::MockDbProvider as MockDb;
587}