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}