gun/core.rs
1use crate::dup::Dup;
2use crate::events::EventEmitter;
3use crate::graph::Graph;
4use crate::state::State;
5use crate::storage::Storage;
6use std::sync::Arc;
7
8/// Core Gun instance structure
9///
10/// This is the central engine that powers all Gun operations. It manages:
11/// - **Graph**: In-memory storage of all nodes
12/// - **State**: Timestamp generation for conflict resolution
13/// - **Events**: Event system for reactive updates
14/// - **Storage**: Optional persistent storage backend
15/// - **Dedup**: Message deduplication for network operations
16///
17/// Based on Gun.js `root.js` and `core.js`. This is an internal structure
18/// that is wrapped by the public [`Gun`](crate::Gun) type.
19///
20/// # Example
21///
22/// ```rust,no_run
23/// use gun::core::GunCore;
24///
25/// let core = GunCore::new();
26/// let soul = core.uuid(None);
27/// println!("Generated soul: {}", soul);
28/// ```
29pub struct GunCore {
30 pub graph: Arc<Graph>,
31 pub state: Arc<State>,
32 pub events: Arc<EventEmitter>,
33 pub storage: Option<Arc<dyn Storage>>,
34 pub id_counter: Arc<std::sync::atomic::AtomicU64>,
35 pub dup: Arc<tokio::sync::RwLock<Dup>>, // Message deduplication for DAM
36}
37
38impl GunCore {
39 /// Create a new GunCore instance without persistent storage
40 ///
41 /// This creates an in-memory only instance. Use [`with_storage`](Self::with_storage)
42 /// to enable persistent storage.
43 ///
44 /// # Returns
45 /// A new `GunCore` instance with no persistent storage.
46 pub fn new() -> Self {
47 Self {
48 graph: Arc::new(Graph::new()),
49 state: Arc::new(State::new()),
50 events: Arc::new(EventEmitter::new()),
51 storage: None,
52 id_counter: Arc::new(std::sync::atomic::AtomicU64::new(0)),
53 dup: Arc::new(tokio::sync::RwLock::new(Dup::new_default())),
54 }
55 }
56
57 /// Create a new GunCore instance with persistent storage
58 ///
59 /// This enables data persistence across application restarts. The storage
60 /// backend can be any implementation of the [`Storage`](crate::storage::Storage) trait,
61 /// such as [`LocalStorage`](crate::storage::LocalStorage) or [`SledStorage`](crate::storage::SledStorage).
62 ///
63 /// # Arguments
64 /// * `storage` - Storage backend implementing the `Storage` trait
65 ///
66 /// # Returns
67 /// A new `GunCore` instance with persistent storage enabled.
68 ///
69 /// # Example
70 ///
71 /// ```rust,no_run
72 /// use gun::core::GunCore;
73 /// use gun::storage::LocalStorage;
74 /// use std::sync::Arc;
75 ///
76 /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
77 /// let storage = Arc::new(LocalStorage::new("./gun_data")?);
78 /// let core = GunCore::with_storage(storage);
79 /// # Ok(())
80 /// # }
81 /// ```
82 pub fn with_storage(storage: Arc<dyn Storage>) -> Self {
83 Self {
84 graph: Arc::new(Graph::new()),
85 state: Arc::new(State::new()),
86 events: Arc::new(EventEmitter::new()),
87 storage: Some(storage),
88 id_counter: Arc::new(std::sync::atomic::AtomicU64::new(0)),
89 dup: Arc::new(tokio::sync::RwLock::new(Dup::new_default())),
90 }
91 }
92
93 /// Generate a new soul (UUID) for a node
94 ///
95 /// Souls are unique identifiers for nodes in the Gun graph. They combine:
96 /// - A state-based component derived from the current timestamp
97 /// - A random component for uniqueness
98 ///
99 /// Based on Gun.js uuid generation: `Gun.state().toString(36).replace('.','') + String.random(12)`
100 ///
101 /// # Arguments
102 /// * `length` - Optional length of the random component (default: 12)
103 ///
104 /// # Returns
105 /// A unique soul string suitable for use as a node identifier.
106 ///
107 /// # Example
108 ///
109 /// ```rust,no_run
110 /// use gun::core::GunCore;
111 ///
112 /// let core = GunCore::new();
113 /// let soul = core.uuid(None); // Default 12-character random suffix
114 /// let short_soul = core.uuid(Some(6)); // 6-character random suffix
115 /// ```
116 pub fn uuid(&self, length: Option<usize>) -> String {
117 let len = length.unwrap_or(12);
118 let state = self.state.next();
119
120 // Convert state to base36 string (similar to JavaScript toString(36))
121 let state_str = format!("{:x}", state as u64).replace(".", "");
122
123 // Generate random alphanumeric string
124 use rand::Rng;
125 let mut rng = rand::thread_rng();
126 let chars: Vec<char> = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
127 .chars()
128 .collect();
129 let random_part: String = (0..len)
130 .map(|_| {
131 let idx = rng.gen_range(0..chars.len());
132 chars[idx]
133 })
134 .collect();
135
136 format!("{}{}", state_str, random_part)
137 }
138
139 /// Generate a simple random ID (for message IDs, etc.)
140 ///
141 /// This generates a pure random alphanumeric string without the state component.
142 /// Useful for message IDs and other temporary identifiers.
143 ///
144 /// # Arguments
145 /// * `length` - Length of the random ID to generate
146 ///
147 /// # Returns
148 /// A random alphanumeric string of the specified length.
149 ///
150 /// # Example
151 ///
152 /// ```rust,no_run
153 /// use gun::core::GunCore;
154 ///
155 /// let core = GunCore::new();
156 /// let message_id = core.random_id(16);
157 /// ```
158 pub fn random_id(&self, length: usize) -> String {
159 use rand::Rng;
160 let mut rng = rand::thread_rng();
161 let chars: Vec<char> = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
162 .chars()
163 .collect();
164 (0..length)
165 .map(|_| {
166 let idx = rng.gen_range(0..chars.len());
167 chars[idx]
168 })
169 .collect()
170 }
171
172 /// Get the next unique chain ID
173 ///
174 /// Chain IDs are used internally to track chain instances for listener management.
175 /// This increments atomically and returns a unique identifier for each chain.
176 ///
177 /// # Returns
178 /// A unique chain ID (monotonically increasing counter).
179 pub fn next_chain_id(&self) -> u64 {
180 self.id_counter
181 .fetch_add(1, std::sync::atomic::Ordering::SeqCst)
182 }
183}
184
185impl Default for GunCore {
186 fn default() -> Self {
187 Self::new()
188 }
189}