use crate::chain::Chain;
use crate::core::GunCore;
use crate::dam::Mesh;
use crate::error::GunResult;
use crate::storage::{LocalStorage, SledStorage, Storage};
use crate::types::MessagePredicate;
use crate::webrtc::{WebRTCManager, WebRTCOptions};
use crate::websocket::{WebSocketClient, WebSocketServer};
use chia_bls::{PublicKey, SecretKey};
use std::sync::Arc;
use tokio::task::JoinHandle;
pub struct Gun {
core: Arc<GunCore>,
mesh: Option<Arc<Mesh>>,
ws_server: Option<JoinHandle<()>>, #[allow(dead_code)] webrtc_manager: Option<Arc<WebRTCManager>>, secret_key: SecretKey, public_key: PublicKey, }
impl Gun {
pub fn new(secret_key: SecretKey, public_key: PublicKey) -> Self {
Self {
core: Arc::new(GunCore::new()),
mesh: None,
ws_server: None,
webrtc_manager: None,
secret_key,
public_key,
}
}
pub async fn with_options(secret_key: SecretKey, public_key: PublicKey, options: GunOptions) -> GunResult<Self> {
let core = if options.localStorage || options.storage_path.is_some() {
let storage: Arc<dyn Storage> = if let Some(ref storage_path) = options.storage_path {
if options.radisk {
Arc::new(SledStorage::new(storage_path)?)
} else {
Arc::new(LocalStorage::new(storage_path)?)
}
} else {
let default_path = "./gun_data";
Arc::new(LocalStorage::new(default_path)?)
};
Arc::new(GunCore::with_storage(storage))
} else {
Arc::new(GunCore::new())
};
let mesh = if !options.peers.is_empty() || options.super_peer {
Some(Arc::new(Mesh::new(core.clone(), secret_key.clone(), public_key.clone(), options.message_predicate.clone())))
} else {
None
};
let mut ws_server = None;
if let (Some(ref mesh_ref), Some(port)) = (&mesh, options.port) {
let server = WebSocketServer::new(core.clone(), mesh_ref.clone(), port);
let server_clone = server;
let handle = tokio::spawn(async move {
if let Err(e) = server_clone.start().await {
eprintln!("WebSocket server error: {}", e);
}
});
ws_server = Some(handle);
}
if let Some(ref mesh_ref) = mesh {
let client = WebSocketClient::new(core.clone(), mesh_ref.clone());
for peer_url in &options.peers {
match client.connect(peer_url).await {
Ok(_) => {
println!("Successfully connected to peer: {}", peer_url);
}
Err(e) => {
eprintln!("Failed to connect to peer {}: {}", peer_url, e);
}
}
}
}
let webrtc_manager = if options.webrtc.enabled {
if let Some(ref mesh_ref) = mesh {
let manager = Arc::new(WebRTCManager::new(
core.clone(),
mesh_ref.clone(),
options.webrtc.clone(),
));
Some(manager)
} else {
None
}
} else {
None
};
if let Some(ref mesh_ref) = mesh {
let mesh_clone = mesh_ref.clone();
let mesh_for_sync = mesh_clone.clone();
let core_for_sync = core.clone();
core.events.on("network_sync", Box::new(move |event: &crate::events::Event| {
if let Some(soul) = event.data.get("soul").and_then(|v| v.as_str()) {
if let Some(data) = event.data.get("data") {
let core_clone = core_for_sync.clone();
let mesh_send = mesh_for_sync.clone();
let soul_str = soul.to_string();
let data_clone = data.clone();
tokio::spawn(async move {
if let Some(node) = core_clone.graph.get(&soul_str) {
let mut node_obj = serde_json::Map::new();
if let Some(meta_obj) = node.meta.get("_").and_then(|v| v.as_object()) {
let mut meta = serde_json::Map::new();
for (k, v) in meta_obj {
meta.insert(k.clone(), v.clone());
}
meta.insert("#".to_string(), serde_json::Value::String(soul_str.clone()));
node_obj.insert("_".to_string(), serde_json::Value::Object(meta));
} else {
let mut meta = serde_json::Map::new();
meta.insert("#".to_string(), serde_json::Value::String(soul_str.clone()));
if let Some(states) = node.meta.get(">") {
meta.insert(">".to_string(), states.clone());
}
node_obj.insert("_".to_string(), serde_json::Value::Object(meta));
}
if let Some(data_obj) = data_clone.as_object() {
for (key, value) in data_obj {
node_obj.insert(key.clone(), value.clone());
}
}
let mut put_obj = serde_json::Map::new();
put_obj.insert(soul_str.clone(), serde_json::Value::Object(node_obj));
let msg = serde_json::json!({
"put": serde_json::Value::Object(put_obj)
});
eprintln!("DEBUG: Sending put message to peers (Gun.js format): {}", serde_json::to_string(&msg).unwrap_or_default());
if let Err(e) = mesh_send.say(&msg, None).await {
eprintln!("Error sending network_sync to peers: {}", e);
}
}
});
}
}
}));
let mesh_for_get = mesh_clone.clone();
core.events.on("get_request", Box::new(move |event: &crate::events::Event| {
if let Some(get_data) = event.data.get("get") {
let msg = serde_json::json!({
"get": get_data
});
let mesh_send = mesh_for_get.clone();
let msg_send = msg.clone();
tokio::spawn(async move {
if let Err(e) = mesh_send.say(&msg_send, None).await {
eprintln!("Error sending get_request to peers: {}", e);
}
});
}
}));
}
Ok(Self {
core,
mesh,
ws_server,
webrtc_manager,
secret_key,
public_key,
})
}
pub fn get(&self, key: &str) -> Arc<Chain> {
Arc::new(Chain::with_soul(self.core.clone(), key.to_string(), None))
}
pub fn root(&self) -> Arc<Chain> {
Arc::new(Chain::new(self.core.clone()))
}
pub fn state(&self) -> f64 {
self.core.state.next()
}
#[allow(dead_code)]
pub(crate) fn core(&self) -> &Arc<GunCore> {
&self.core
}
#[allow(dead_code)]
pub(crate) fn mesh(&self) -> Option<&Arc<Mesh>> {
self.mesh.as_ref()
}
pub async fn connected_peer_count(&self) -> usize {
if let Some(ref mesh) = self.mesh {
mesh.connected_peer_count().await
} else {
0
}
}
pub async fn is_connected(&self) -> bool {
if let Some(ref mesh) = self.mesh {
mesh.has_connected_peers().await
} else {
false
}
}
pub async fn wait_for_connection(&self, timeout_ms: u64) -> bool {
if let Some(ref mesh) = self.mesh {
mesh.wait_for_connection(timeout_ms).await
} else {
false
}
}
pub async fn shutdown(&mut self) -> GunResult<()> {
if let Some(handle) = self.ws_server.take() {
handle.abort();
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
}
Ok(())
}
}
#[derive(Clone)]
#[allow(non_snake_case)] pub struct GunOptions {
pub peers: Vec<String>,
pub storage_path: Option<String>,
pub radisk: bool,
#[allow(non_snake_case)] pub localStorage: bool,
pub super_peer: bool,
pub port: Option<u16>,
pub webrtc: WebRTCOptions,
pub message_predicate: Option<MessagePredicate>,
}
impl Default for GunOptions {
fn default() -> Self {
Self {
peers: vec![],
storage_path: None,
radisk: true,
localStorage: true,
super_peer: false,
port: None,
webrtc: WebRTCOptions::default(),
message_predicate: None,
}
}
}
impl GunOptions {
pub fn with_relay(relay_url: &str) -> Self {
Self {
peers: vec![relay_url.to_string()],
..Default::default()
}
}
pub fn with_peers(peers: Vec<String>) -> Self {
Self {
peers,
..Default::default()
}
}
pub fn relay_server(port: u16) -> Self {
Self {
port: Some(port),
super_peer: true,
..Default::default()
}
}
}