reovim_kernel/api/module/id.rs
1//! Module identifier type.
2
3use std::{borrow::Cow, fmt};
4
5/// Unique identifier for a loadable module.
6///
7/// Convention: Use kebab-case names like "lang-rust", "feat-completion".
8///
9/// # Static vs Dynamic IDs
10///
11/// Module IDs can be either:
12/// - **Static** (`&'static str`): For compile-time known modules, use `ModuleId::new()`
13/// - **Dynamic** (`String`): For runtime-generated modules, use `ModuleId::from_string()`
14///
15/// Static IDs are preferred for performance (no allocation), but dynamic IDs
16/// allow for user-defined or plugin-loaded modules with arbitrary names.
17///
18/// # Example
19///
20/// ```
21/// use reovim_kernel::api::v1::ModuleId;
22///
23/// // Static ID (compile-time known)
24/// let static_id = ModuleId::new("lang-rust");
25///
26/// // Dynamic ID (runtime generated)
27/// let name = format!("user-plugin-{}", 42);
28/// let dynamic_id = ModuleId::from_string(name);
29///
30/// // Both work the same way
31/// assert_eq!(static_id.as_str(), "lang-rust");
32/// assert_eq!(dynamic_id.as_str(), "user-plugin-42");
33/// ```
34#[derive(Debug, Clone, PartialEq, Eq, Hash)]
35pub struct ModuleId(Cow<'static, str>);
36
37impl ModuleId {
38 /// Create a new module identifier from a static string.
39 ///
40 /// This is the preferred way to create module IDs for statically-known modules.
41 /// It's a const fn and involves no allocation.
42 #[must_use]
43 pub const fn new(id: &'static str) -> Self {
44 Self(Cow::Borrowed(id))
45 }
46
47 /// Create a module identifier from an owned String.
48 ///
49 /// Use this for dynamically-generated module IDs (e.g., user plugins,
50 /// runtime-loaded modules with user-provided names).
51 #[must_use]
52 #[allow(clippy::missing_const_for_fn)] // String operations aren't const-stable
53 pub fn from_string(id: String) -> Self {
54 Self(Cow::Owned(id))
55 }
56
57 /// Get the identifier string.
58 #[must_use]
59 pub fn as_str(&self) -> &str {
60 &self.0
61 }
62
63 /// Check if this is a static (borrowed) ID.
64 #[must_use]
65 pub const fn is_static(&self) -> bool {
66 matches!(self.0, Cow::Borrowed(_))
67 }
68
69 /// Check if this is a dynamic (owned) ID.
70 #[must_use]
71 pub const fn is_dynamic(&self) -> bool {
72 matches!(self.0, Cow::Owned(_))
73 }
74}
75
76impl fmt::Display for ModuleId {
77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 write!(f, "{}", self.0)
79 }
80}
81
82impl From<&'static str> for ModuleId {
83 fn from(s: &'static str) -> Self {
84 Self::new(s)
85 }
86}
87
88impl From<String> for ModuleId {
89 fn from(s: String) -> Self {
90 Self::from_string(s)
91 }
92}