cargo fix and fmt
This commit is contained in:
@@ -12,7 +12,7 @@
|
||||
//! ```
|
||||
|
||||
use circle_client_ws::CircleWsClientBuilder;
|
||||
use log::{info, error};
|
||||
use log::{error, info};
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
||||
@@ -29,11 +29,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Example 1: Authenticate with private key
|
||||
info!("=== Example 1: Private Key Authentication ===");
|
||||
let private_key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
|
||||
|
||||
|
||||
let mut client = CircleWsClientBuilder::new(ws_url.clone())
|
||||
.with_keypair(private_key.to_string())
|
||||
.build();
|
||||
|
||||
|
||||
match client.connect().await {
|
||||
Ok(_) => {
|
||||
info!("Successfully connected to WebSocket");
|
||||
@@ -67,27 +67,26 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
error!("Play request failed: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Keep connection alive for a moment
|
||||
sleep(Duration::from_secs(2)).await;
|
||||
|
||||
|
||||
// Disconnect
|
||||
client.disconnect().await;
|
||||
info!("Disconnected from WebSocket");
|
||||
|
||||
|
||||
// Example 3: Different private key authentication
|
||||
info!("=== Example 3: Different Private Key Authentication ===");
|
||||
let private_key2 = "0xfedcba0987654321fedcba0987654321fedcba0987654321fedcba0987654321";
|
||||
|
||||
|
||||
let mut client2 = CircleWsClientBuilder::new(ws_url.clone())
|
||||
.with_keypair(private_key2.to_string())
|
||||
.build();
|
||||
|
||||
|
||||
match client2.connect().await {
|
||||
Ok(_) => {
|
||||
info!("Connected with second private key authentication");
|
||||
|
||||
|
||||
match client2.authenticate().await {
|
||||
Ok(true) => {
|
||||
info!("Successfully authenticated with second private key");
|
||||
@@ -108,7 +107,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
error!("Second private key authentication failed: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
client2.disconnect().await;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -119,11 +118,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Example 4: Non-authenticated connection (fallback)
|
||||
info!("=== Example 4: Non-Authenticated Connection ===");
|
||||
let mut client3 = CircleWsClientBuilder::new(ws_url).build();
|
||||
|
||||
|
||||
match client3.connect().await {
|
||||
Ok(()) => {
|
||||
info!("Connected without authentication (fallback mode)");
|
||||
|
||||
|
||||
let script = "print('Hello from non-auth client!');".to_string();
|
||||
match client3.play(script).await {
|
||||
Ok(result) => {
|
||||
@@ -133,7 +132,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
error!("Non-auth request failed: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
client3.disconnect().await;
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -143,4 +142,4 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
info!("Simplified authentication example completed");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,5 @@
|
||||
//! Authentication simulation example
|
||||
//!
|
||||
//!
|
||||
//! This example simulates the authentication flow without requiring a running server.
|
||||
//! It demonstrates:
|
||||
//! 1. Key generation and management
|
||||
@@ -8,32 +8,28 @@
|
||||
//! 4. Credential management
|
||||
//! 5. Authentication state checking
|
||||
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
use log::info;
|
||||
use std::time::{SystemTime, UNIX_EPOCH};
|
||||
|
||||
// Import authentication modules
|
||||
use circle_client_ws::CircleWsClientBuilder;
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
use circle_client_ws::auth::{
|
||||
generate_private_key,
|
||||
derive_public_key,
|
||||
sign_message,
|
||||
verify_signature,
|
||||
AuthCredentials,
|
||||
NonceResponse
|
||||
derive_public_key, generate_private_key, sign_message, verify_signature, AuthCredentials,
|
||||
NonceResponse,
|
||||
};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Initialize logging
|
||||
env_logger::init();
|
||||
|
||||
|
||||
info!("🔐 Starting authentication simulation example");
|
||||
|
||||
|
||||
// Step 1: Generate cryptographic keys
|
||||
info!("🔑 Generating cryptographic keys...");
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
let (private_key, public_key) = {
|
||||
let private_key = generate_private_key()?;
|
||||
@@ -42,38 +38,39 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!("✅ Derived public key: {}...", &public_key[..20]);
|
||||
(private_key, public_key)
|
||||
};
|
||||
|
||||
|
||||
#[cfg(not(feature = "crypto"))]
|
||||
let (private_key, _public_key) = {
|
||||
let private_key = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef".to_string();
|
||||
let private_key =
|
||||
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef".to_string();
|
||||
let public_key = "04abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890".to_string();
|
||||
info!("📝 Using fallback keys (crypto feature disabled)");
|
||||
(private_key, public_key)
|
||||
};
|
||||
|
||||
|
||||
// Step 2: Simulate nonce request and response
|
||||
info!("📡 Simulating nonce request...");
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
|
||||
let simulated_nonce = format!("nonce_{}_{}", current_time, "abcdef123456");
|
||||
let expires_at = current_time + 300; // 5 minutes from now
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
let nonce_response = NonceResponse {
|
||||
nonce: simulated_nonce.clone(),
|
||||
expires_at,
|
||||
};
|
||||
|
||||
|
||||
info!("✅ Simulated nonce response:");
|
||||
info!(" Nonce: {}", simulated_nonce);
|
||||
info!(" Expires at: {}", expires_at);
|
||||
|
||||
|
||||
// Step 3: Sign the nonce
|
||||
info!("✍️ Signing nonce with private key...");
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
let signature = {
|
||||
match sign_message(&private_key, &simulated_nonce) {
|
||||
@@ -87,16 +84,16 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#[cfg(not(feature = "crypto"))]
|
||||
let _signature = {
|
||||
info!("📝 Using fallback signature (crypto feature disabled)");
|
||||
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890".to_string()
|
||||
};
|
||||
|
||||
|
||||
// Step 4: Verify the signature
|
||||
info!("🔍 Verifying signature...");
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
{
|
||||
match verify_signature(&public_key, &simulated_nonce, &signature) {
|
||||
@@ -111,12 +108,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "crypto"))]
|
||||
{
|
||||
info!("📝 Skipping signature verification (crypto feature disabled)");
|
||||
}
|
||||
|
||||
|
||||
// Step 5: Create authentication credentials
|
||||
info!("📋 Creating authentication credentials...");
|
||||
#[cfg(feature = "crypto")]
|
||||
@@ -124,9 +121,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
public_key.clone(),
|
||||
signature.clone(),
|
||||
nonce_response.nonce.clone(),
|
||||
expires_at
|
||||
expires_at,
|
||||
);
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
{
|
||||
info!("✅ Credentials created:");
|
||||
@@ -136,77 +133,86 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
info!(" Expires at: {}", credentials.expires_at);
|
||||
info!(" Is expired: {}", credentials.is_expired());
|
||||
info!(" Expires within 60s: {}", credentials.expires_within(60));
|
||||
info!(" Expires within 400s: {}", credentials.expires_within(400));
|
||||
info!(
|
||||
" Expires within 400s: {}",
|
||||
credentials.expires_within(400)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Step 6: Create client with authentication
|
||||
info!("🔌 Creating WebSocket client with authentication...");
|
||||
let _client = CircleWsClientBuilder::new("ws://localhost:8080/ws".to_string())
|
||||
.with_keypair(private_key.clone())
|
||||
.build();
|
||||
|
||||
|
||||
info!("✅ Client created");
|
||||
|
||||
|
||||
// Step 7: Demonstrate key rotation
|
||||
info!("🔄 Demonstrating key rotation...");
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
{
|
||||
let new_private_key = generate_private_key()?;
|
||||
let new_public_key = derive_public_key(&new_private_key)?;
|
||||
|
||||
|
||||
info!("✅ Generated new keys:");
|
||||
info!(" New private key: {}...", &new_private_key[..10]);
|
||||
info!(" New public key: {}...", &new_public_key[..20]);
|
||||
|
||||
|
||||
// Create new client with rotated keys
|
||||
let _new_client = CircleWsClientBuilder::new("ws://localhost:8080/ws".to_string())
|
||||
.with_keypair(new_private_key)
|
||||
.build();
|
||||
|
||||
|
||||
info!("✅ Created client with rotated keys");
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "crypto"))]
|
||||
{
|
||||
info!("📝 Skipping key rotation (crypto feature disabled)");
|
||||
}
|
||||
|
||||
|
||||
// Step 8: Demonstrate credential expiration
|
||||
info!("⏰ Demonstrating credential expiration...");
|
||||
|
||||
|
||||
// Create credentials that expire soon
|
||||
#[cfg(feature = "crypto")]
|
||||
let short_lived_credentials = AuthCredentials::new(
|
||||
public_key,
|
||||
signature,
|
||||
nonce_response.nonce,
|
||||
current_time + 5 // Expires in 5 seconds
|
||||
current_time + 5, // Expires in 5 seconds
|
||||
);
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
{
|
||||
info!("✅ Created short-lived credentials:");
|
||||
info!(" Expires at: {}", short_lived_credentials.expires_at);
|
||||
info!(" Is expired: {}", short_lived_credentials.is_expired());
|
||||
info!(" Expires within 10s: {}", short_lived_credentials.expires_within(10));
|
||||
|
||||
info!(
|
||||
" Expires within 10s: {}",
|
||||
short_lived_credentials.expires_within(10)
|
||||
);
|
||||
|
||||
// Wait a moment and check again
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
info!("⏳ After 1 second:");
|
||||
info!(" Is expired: {}", short_lived_credentials.is_expired());
|
||||
info!(" Expires within 5s: {}", short_lived_credentials.expires_within(5));
|
||||
info!(
|
||||
" Expires within 5s: {}",
|
||||
short_lived_credentials.expires_within(5)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
info!("🎉 Authentication simulation completed successfully!");
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_key_generation() {
|
||||
#[cfg(feature = "crypto")]
|
||||
@@ -214,13 +220,13 @@ mod tests {
|
||||
let private_key = generate_private_key().unwrap();
|
||||
assert!(private_key.starts_with("0x"));
|
||||
assert_eq!(private_key.len(), 66); // 0x + 64 hex chars
|
||||
|
||||
|
||||
let public_key = derive_public_key(&private_key).unwrap();
|
||||
assert!(public_key.starts_with("04"));
|
||||
assert_eq!(public_key.len(), 130); // 04 + 128 hex chars (uncompressed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_signature_flow() {
|
||||
#[cfg(feature = "crypto")]
|
||||
@@ -228,29 +234,29 @@ mod tests {
|
||||
let private_key = generate_private_key().unwrap();
|
||||
let public_key = derive_public_key(&private_key).unwrap();
|
||||
let message = "test_nonce_12345";
|
||||
|
||||
|
||||
let signature = sign_message(&private_key, message).unwrap();
|
||||
let is_valid = verify_signature(&public_key, message, &signature).unwrap();
|
||||
|
||||
|
||||
assert!(is_valid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_credentials() {
|
||||
let current_time = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs();
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
let credentials = AuthCredentials::new(
|
||||
"04abcdef...".to_string(),
|
||||
"0x123456...".to_string(),
|
||||
"nonce_123".to_string(),
|
||||
current_time + 300
|
||||
current_time + 300,
|
||||
);
|
||||
|
||||
|
||||
#[cfg(feature = "crypto")]
|
||||
{
|
||||
assert!(!credentials.is_expired());
|
||||
@@ -258,4 +264,4 @@ mod tests {
|
||||
assert!(!credentials.expires_within(100));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -16,10 +16,10 @@
|
||||
//! 4. The launcher will run until you stop it with Ctrl+C.
|
||||
|
||||
use launcher::{run_launcher, Args, CircleConfig};
|
||||
use log::{error, info};
|
||||
use std::error::Error as StdError;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::error::Error as StdError;
|
||||
use log::{error, info};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn StdError>> {
|
||||
@@ -54,7 +54,11 @@ async fn main() -> Result<(), Box<dyn StdError>> {
|
||||
let mut circle_configs: Vec<CircleConfig> = match serde_json::from_str(&config_content) {
|
||||
Ok(configs) => configs,
|
||||
Err(e) => {
|
||||
error!("Failed to parse {}: {}. Ensure it's a valid JSON array of CircleConfig.", config_path.display(), e);
|
||||
error!(
|
||||
"Failed to parse {}: {}. Ensure it's a valid JSON array of CircleConfig.",
|
||||
config_path.display(),
|
||||
e
|
||||
);
|
||||
return Err(Box::new(e) as Box<dyn StdError>);
|
||||
}
|
||||
};
|
||||
@@ -68,7 +72,10 @@ async fn main() -> Result<(), Box<dyn StdError>> {
|
||||
}
|
||||
|
||||
if circle_configs.is_empty() {
|
||||
info!("No circle configurations found in {}. Exiting.", config_path.display());
|
||||
info!(
|
||||
"No circle configurations found in {}. Exiting.",
|
||||
config_path.display()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -80,4 +87,4 @@ async fn main() -> Result<(), Box<dyn StdError>> {
|
||||
|
||||
println!("--- OurWorld Example Finished ---");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -1,51 +1,51 @@
|
||||
[
|
||||
{
|
||||
"name": "OurWorld",
|
||||
"public_key": "02acbca22369b7f10584348056ae48779e04534cd34d37b7db0f4996f4d9d5e2a5",
|
||||
"secret_key": "0c75df7425c799eb769049cf48891299761660396d772c687fa84cac5ec62570",
|
||||
"worker_queue": "rhai_tasks:02acbca22369b7f10584348056ae48779e04534cd34d37b7db0f4996f4d9d5e2a5",
|
||||
"public_key": "02b1ff38c18f66ffcfde1ff4931093484a96d378db55c1306a0760b39172d74099",
|
||||
"secret_key": "86ed603c86f8938060575f7b1c7e4e4ddf72030ad2ea1699a8e9d1fb3a610869",
|
||||
"worker_queue": "rhai_tasks:02b1ff38c18f66ffcfde1ff4931093484a96d378db55c1306a0760b39172d74099",
|
||||
"ws_url": "ws://127.0.0.1:9000"
|
||||
},
|
||||
{
|
||||
"name": "Dunia Cybercity",
|
||||
"public_key": "03d97b1a357c3ceb2f0eb78f8e2c71beda9190db5cb7e5112150105132effb35e0",
|
||||
"secret_key": "4fad664608e8de55f0e5e1712241e71dc0864be125bc8633e50601fca8040791",
|
||||
"worker_queue": "rhai_tasks:03d97b1a357c3ceb2f0eb78f8e2c71beda9190db5cb7e5112150105132effb35e0",
|
||||
"public_key": "020d8b1e3baab9991a82e9b55e117f45fda58b3f90b072dbbf10888f3195bfe6b9",
|
||||
"secret_key": "b1ac20e4c6ace638f7f9e07918997fc35b2425de78152139c8b54629ca303b81",
|
||||
"worker_queue": "rhai_tasks:020d8b1e3baab9991a82e9b55e117f45fda58b3f90b072dbbf10888f3195bfe6b9",
|
||||
"ws_url": "ws://127.0.0.1:9001"
|
||||
},
|
||||
{
|
||||
"name": "Sikana",
|
||||
"public_key": "0389595b28cfa98b45fa3c222db79892f3face65e7ef06d44e35d642967e45ed6e",
|
||||
"secret_key": "fd59ddbf0d0bada725c911dc7e3317754ac552aa1ac84cfcb899bdfe3591e1f4",
|
||||
"worker_queue": "rhai_tasks:0389595b28cfa98b45fa3c222db79892f3face65e7ef06d44e35d642967e45ed6e",
|
||||
"public_key": "0363dbff9f2b6dbaf58d3e8774db54dcccd10e23461ebf9a93cca63f8aa321d11d",
|
||||
"secret_key": "9383663dcac577c14679c3487e6ffe7ff95040f422d391219ea530b892c1b0a0",
|
||||
"worker_queue": "rhai_tasks:0363dbff9f2b6dbaf58d3e8774db54dcccd10e23461ebf9a93cca63f8aa321d11d",
|
||||
"ws_url": "ws://127.0.0.1:9002"
|
||||
},
|
||||
{
|
||||
"name": "Threefold",
|
||||
"public_key": "03270f06ee4a7d42a9f6c22c9a7d6d0138cd15d4fa659026e2e6572fc6c6a6ea18",
|
||||
"secret_key": "e204c0215bec80f74df49ea5b1592de3c6739cced339ace801bb7e158eb62231",
|
||||
"worker_queue": "rhai_tasks:03270f06ee4a7d42a9f6c22c9a7d6d0138cd15d4fa659026e2e6572fc6c6a6ea18",
|
||||
"public_key": "02c19cd347605dab98fb767b5e53c5fa5131d47a46b5f560b565fd4d79c1190994",
|
||||
"secret_key": "0c4f5172724218650ea5806f5c9f8d4d4c8197c0c775f9d022fd8a192ad59048",
|
||||
"worker_queue": "rhai_tasks:02c19cd347605dab98fb767b5e53c5fa5131d47a46b5f560b565fd4d79c1190994",
|
||||
"ws_url": "ws://127.0.0.1:9003"
|
||||
},
|
||||
{
|
||||
"name": "Mbweni",
|
||||
"public_key": "02724cf23e4ac95d0f14984f55c6955b3ca5ab2275d7ac2a2e4baf3596caf8606c",
|
||||
"secret_key": "3c013e2e5f64692f044d17233e5fabdb0577629f898359115e69c3e594d5f43e",
|
||||
"worker_queue": "rhai_tasks:02724cf23e4ac95d0f14984f55c6955b3ca5ab2275d7ac2a2e4baf3596caf8606c",
|
||||
"public_key": "0251808090b5b916e6187b63b6c97411f9d5406a9a6179408b90e3ff83042e7a9c",
|
||||
"secret_key": "c824b3334350e2b267be2d4ceb1db53e98c9f386d2855aa7130227caa580805c",
|
||||
"worker_queue": "rhai_tasks:0251808090b5b916e6187b63b6c97411f9d5406a9a6179408b90e3ff83042e7a9c",
|
||||
"ws_url": "ws://127.0.0.1:9004"
|
||||
},
|
||||
{
|
||||
"name": "Geomind",
|
||||
"public_key": "030d8ceb47d445c92b7c3f13e9e134eebcb1d83beed424425f734164544eb58eed",
|
||||
"secret_key": "dbd6dd383a6f56042710f72ce2ac68266650bbfb61432cdd139e98043b693e7c",
|
||||
"worker_queue": "rhai_tasks:030d8ceb47d445c92b7c3f13e9e134eebcb1d83beed424425f734164544eb58eed",
|
||||
"public_key": "037e2def151e7587b95519370e5d1023b9f24845e8e23a6535b0aad3cff20a859b",
|
||||
"secret_key": "9c701a02ebba983d04ecbccee5072ed2cebd67ead4677c79a72d089d3ff29295",
|
||||
"worker_queue": "rhai_tasks:037e2def151e7587b95519370e5d1023b9f24845e8e23a6535b0aad3cff20a859b",
|
||||
"ws_url": "ws://127.0.0.1:9005"
|
||||
},
|
||||
{
|
||||
"name": "Freezone",
|
||||
"public_key": "02dd21025c1d47421eccc2264c87538d41126da772a9a3f0e7226807fed89c9971",
|
||||
"secret_key": "0c0c6b02c20fcd4ccfb2afeae249979ddd623e6f6edd17af4a9a5a19bc1b15ae",
|
||||
"worker_queue": "rhai_tasks:02dd21025c1d47421eccc2264c87538d41126da772a9a3f0e7226807fed89c9971",
|
||||
"public_key": "02d4bf2713876cff2428f3f5e7e6191028374994d43a2c0f3d62c728a22d7f4aed",
|
||||
"secret_key": "602c1bdd95489c7153676488976e9a24483cb353778332ec3b7644c3f05f5af2",
|
||||
"worker_queue": "rhai_tasks:02d4bf2713876cff2428f3f5e7e6191028374994d43a2c0f3d62c728a22d7f4aed",
|
||||
"ws_url": "ws://127.0.0.1:9006"
|
||||
}
|
||||
]
|
@@ -1,6 +1,6 @@
|
||||
use std::process::{Command, Child, Stdio};
|
||||
use std::time::Duration;
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
// tokio_tungstenite and direct futures_util for ws stream are no longer needed here
|
||||
// use tokio_tungstenite::{connect_async, tungstenite::protocol::Message as WsMessage};
|
||||
@@ -32,28 +32,54 @@ impl ChildProcessGuard {
|
||||
|
||||
impl Drop for ChildProcessGuard {
|
||||
fn drop(&mut self) {
|
||||
log::info!("Cleaning up {} process (PID: {})...", self.name, self.child.id());
|
||||
log::info!(
|
||||
"Cleaning up {} process (PID: {})...",
|
||||
self.name,
|
||||
self.child.id()
|
||||
);
|
||||
match self.child.kill() {
|
||||
Ok(_) => {
|
||||
log::info!("Successfully sent kill signal to {} (PID: {}).", self.name, self.child.id());
|
||||
log::info!(
|
||||
"Successfully sent kill signal to {} (PID: {}).",
|
||||
self.name,
|
||||
self.child.id()
|
||||
);
|
||||
// Optionally wait for a short period or check status
|
||||
match self.child.wait() {
|
||||
Ok(status) => log::info!("{} (PID: {}) exited with status: {}", self.name, self.child.id(), status),
|
||||
Err(e) => log::warn!("Error waiting for {} (PID: {}): {}", self.name, self.child.id(), e),
|
||||
Ok(status) => log::info!(
|
||||
"{} (PID: {}) exited with status: {}",
|
||||
self.name,
|
||||
self.child.id(),
|
||||
status
|
||||
),
|
||||
Err(e) => log::warn!(
|
||||
"Error waiting for {} (PID: {}): {}",
|
||||
self.name,
|
||||
self.child.id(),
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => log::error!("Failed to kill {} (PID: {}): {}", self.name, self.child.id(), e),
|
||||
Err(e) => log::error!(
|
||||
"Failed to kill {} (PID: {}): {}",
|
||||
self.name,
|
||||
self.child.id(),
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_target_dir() -> Result<PathBuf, String> {
|
||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|_| "CARGO_MANIFEST_DIR not set".to_string())?;
|
||||
let workspace_root = PathBuf::from(manifest_dir).parent().ok_or("Failed to get workspace root")?.to_path_buf();
|
||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
|
||||
.map_err(|_| "CARGO_MANIFEST_DIR not set".to_string())?;
|
||||
let workspace_root = PathBuf::from(manifest_dir)
|
||||
.parent()
|
||||
.ok_or("Failed to get workspace root")?
|
||||
.to_path_buf();
|
||||
Ok(workspace_root.join("target").join("debug"))
|
||||
}
|
||||
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init();
|
||||
@@ -62,7 +88,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
log::error!("Could not determine target directory: {}", e);
|
||||
e
|
||||
})?;
|
||||
|
||||
|
||||
let rhai_worker_path = target_dir.join(RHAI_WORKER_BIN_NAME);
|
||||
let circle_server_ws_path = target_dir.join(CIRCLE_SERVER_WS_BIN_NAME);
|
||||
|
||||
@@ -79,26 +105,46 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
.stdout(Stdio::piped()) // Capture stdout
|
||||
.stderr(Stdio::piped()) // Capture stderr
|
||||
.spawn()?;
|
||||
let _rhai_worker_guard = ChildProcessGuard::new(rhai_worker_process, RHAI_WORKER_BIN_NAME.to_string());
|
||||
log::info!("{} started with PID {}", RHAI_WORKER_BIN_NAME, _rhai_worker_guard.child.id());
|
||||
let _rhai_worker_guard =
|
||||
ChildProcessGuard::new(rhai_worker_process, RHAI_WORKER_BIN_NAME.to_string());
|
||||
log::info!(
|
||||
"{} started with PID {}",
|
||||
RHAI_WORKER_BIN_NAME,
|
||||
_rhai_worker_guard.child.id()
|
||||
);
|
||||
|
||||
log::info!("Starting {} for circle '{}' on port {}...", CIRCLE_SERVER_WS_BIN_NAME, TEST_CIRCLE_NAME, TEST_SERVER_PORT);
|
||||
log::info!(
|
||||
"Starting {} for circle '{}' on port {}...",
|
||||
CIRCLE_SERVER_WS_BIN_NAME,
|
||||
TEST_CIRCLE_NAME,
|
||||
TEST_SERVER_PORT
|
||||
);
|
||||
let circle_server_process = Command::new(&circle_server_ws_path)
|
||||
.args(["--port", &TEST_SERVER_PORT.to_string(), "--circle-name", TEST_CIRCLE_NAME])
|
||||
.args([
|
||||
"--port",
|
||||
&TEST_SERVER_PORT.to_string(),
|
||||
"--circle-name",
|
||||
TEST_CIRCLE_NAME,
|
||||
])
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()?;
|
||||
let _circle_server_guard = ChildProcessGuard::new(circle_server_process, CIRCLE_SERVER_WS_BIN_NAME.to_string());
|
||||
log::info!("{} started with PID {}", CIRCLE_SERVER_WS_BIN_NAME, _circle_server_guard.child.id());
|
||||
let _circle_server_guard =
|
||||
ChildProcessGuard::new(circle_server_process, CIRCLE_SERVER_WS_BIN_NAME.to_string());
|
||||
log::info!(
|
||||
"{} started with PID {}",
|
||||
CIRCLE_SERVER_WS_BIN_NAME,
|
||||
_circle_server_guard.child.id()
|
||||
);
|
||||
|
||||
// Give servers a moment to start
|
||||
sleep(Duration::from_secs(3)).await; // Increased sleep
|
||||
|
||||
let ws_url_str = format!("ws://127.0.0.1:{}/ws", TEST_SERVER_PORT);
|
||||
|
||||
|
||||
log::info!("Creating CircleWsClient for {}...", ws_url_str);
|
||||
let mut client = CircleWsClientBuilder::new(ws_url_str.clone()).build();
|
||||
|
||||
|
||||
log::info!("Connecting CircleWsClient...");
|
||||
client.connect().await.map_err(|e| {
|
||||
log::error!("CircleWsClient connection failed: {}", e);
|
||||
@@ -108,8 +154,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
let script_to_run = "let a = 5; let b = 10; print(\"E2E Rhai: \" + (a+b)); a + b";
|
||||
|
||||
log::info!("Sending 'play' request via CircleWsClient for script: '{}'", script_to_run);
|
||||
|
||||
log::info!(
|
||||
"Sending 'play' request via CircleWsClient for script: '{}'",
|
||||
script_to_run
|
||||
);
|
||||
|
||||
match client.play(script_to_run.to_string()).await {
|
||||
Ok(play_result) => {
|
||||
log::info!("Received play result: {:?}", play_result);
|
||||
@@ -121,12 +170,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
return Err(format!("CircleWsClient play request failed: {}", e).into());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
log::info!("Disconnecting CircleWsClient...");
|
||||
client.disconnect().await;
|
||||
log::info!("CircleWsClient disconnected.");
|
||||
|
||||
|
||||
log::info!("E2E Rhai flow example completed successfully.");
|
||||
// Guards will automatically clean up child processes when they go out of scope here
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@@ -7,9 +7,9 @@
|
||||
// Ensure circle_server_ws is compiled (cargo build --bin circle_server_ws).
|
||||
|
||||
use circle_client_ws::CircleWsClientBuilder;
|
||||
use tokio::time::{sleep, Duration};
|
||||
use std::process::{Command, Child, Stdio};
|
||||
use std::path::PathBuf;
|
||||
use std::process::{Child, Command, Stdio};
|
||||
use tokio::time::{sleep, Duration};
|
||||
|
||||
const EXAMPLE_SERVER_PORT: u16 = 8089; // Using a specific port for this example
|
||||
const WS_URL: &str = "ws://127.0.0.1:8089/ws";
|
||||
@@ -32,26 +32,56 @@ impl ChildProcessGuard {
|
||||
|
||||
impl Drop for ChildProcessGuard {
|
||||
fn drop(&mut self) {
|
||||
log::info!("Cleaning up {} process (PID: {})...", self.name, self.child.id());
|
||||
log::info!(
|
||||
"Cleaning up {} process (PID: {})...",
|
||||
self.name,
|
||||
self.child.id()
|
||||
);
|
||||
match self.child.kill() {
|
||||
Ok(_) => {
|
||||
log::info!("Successfully sent kill signal to {} (PID: {}).", self.name, self.child.id());
|
||||
log::info!(
|
||||
"Successfully sent kill signal to {} (PID: {}).",
|
||||
self.name,
|
||||
self.child.id()
|
||||
);
|
||||
match self.child.wait() {
|
||||
Ok(status) => log::info!("{} (PID: {}) exited with status: {}", self.name, self.child.id(), status),
|
||||
Err(e) => log::warn!("Error waiting for {} (PID: {}): {}", self.name, self.child.id(), e),
|
||||
Ok(status) => log::info!(
|
||||
"{} (PID: {}) exited with status: {}",
|
||||
self.name,
|
||||
self.child.id(),
|
||||
status
|
||||
),
|
||||
Err(e) => log::warn!(
|
||||
"Error waiting for {} (PID: {}): {}",
|
||||
self.name,
|
||||
self.child.id(),
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(e) => log::error!("Failed to kill {} (PID: {}): {}", self.name, self.child.id(), e),
|
||||
Err(e) => log::error!(
|
||||
"Failed to kill {} (PID: {}): {}",
|
||||
self.name,
|
||||
self.child.id(),
|
||||
e
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_target_bin_path(bin_name: &str) -> Result<PathBuf, String> {
|
||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").map_err(|_| "CARGO_MANIFEST_DIR not set".to_string())?;
|
||||
let workspace_root = PathBuf::from(manifest_dir).parent().ok_or("Failed to get workspace root")?.to_path_buf();
|
||||
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR")
|
||||
.map_err(|_| "CARGO_MANIFEST_DIR not set".to_string())?;
|
||||
let workspace_root = PathBuf::from(manifest_dir)
|
||||
.parent()
|
||||
.ok_or("Failed to get workspace root")?
|
||||
.to_path_buf();
|
||||
let bin_path = workspace_root.join("target").join("debug").join(bin_name);
|
||||
if !bin_path.exists() {
|
||||
return Err(format!("Binary '{}' not found at {:?}. Ensure it's built.", bin_name, bin_path));
|
||||
return Err(format!(
|
||||
"Binary '{}' not found at {:?}. Ensure it's built.",
|
||||
bin_name, bin_path
|
||||
));
|
||||
}
|
||||
Ok(bin_path)
|
||||
}
|
||||
@@ -63,18 +93,31 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let server_bin_path = find_target_bin_path(CIRCLE_SERVER_WS_BIN_NAME)?;
|
||||
log::info!("Found server binary at: {:?}", server_bin_path);
|
||||
|
||||
log::info!("Starting {} for circle '{}' on port {}...", CIRCLE_SERVER_WS_BIN_NAME, CIRCLE_NAME_FOR_EXAMPLE, EXAMPLE_SERVER_PORT);
|
||||
log::info!(
|
||||
"Starting {} for circle '{}' on port {}...",
|
||||
CIRCLE_SERVER_WS_BIN_NAME,
|
||||
CIRCLE_NAME_FOR_EXAMPLE,
|
||||
EXAMPLE_SERVER_PORT
|
||||
);
|
||||
let server_process = Command::new(&server_bin_path)
|
||||
.args([
|
||||
"--port", &EXAMPLE_SERVER_PORT.to_string(),
|
||||
"--circle-name", CIRCLE_NAME_FOR_EXAMPLE
|
||||
"--port",
|
||||
&EXAMPLE_SERVER_PORT.to_string(),
|
||||
"--circle-name",
|
||||
CIRCLE_NAME_FOR_EXAMPLE,
|
||||
])
|
||||
.stdout(Stdio::piped()) // Pipe stdout to keep terminal clean, or Stdio::inherit() to see server logs
|
||||
.stderr(Stdio::piped()) // Pipe stderr as well
|
||||
.spawn()
|
||||
.map_err(|e| format!("Failed to start {}: {}. Ensure it is built.", CIRCLE_SERVER_WS_BIN_NAME, e))?;
|
||||
|
||||
let _server_guard = ChildProcessGuard::new(server_process, CIRCLE_SERVER_WS_BIN_NAME.to_string());
|
||||
.map_err(|e| {
|
||||
format!(
|
||||
"Failed to start {}: {}. Ensure it is built.",
|
||||
CIRCLE_SERVER_WS_BIN_NAME, e
|
||||
)
|
||||
})?;
|
||||
|
||||
let _server_guard =
|
||||
ChildProcessGuard::new(server_process, CIRCLE_SERVER_WS_BIN_NAME.to_string());
|
||||
|
||||
log::info!("Giving the server a moment to start up...");
|
||||
sleep(Duration::from_secs(3)).await; // Wait for server to initialize
|
||||
@@ -99,13 +142,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// This part should not be reached if timeout works correctly.
|
||||
print(x);
|
||||
x
|
||||
".to_string();
|
||||
"
|
||||
.to_string();
|
||||
|
||||
log::info!("Sending long-running script (expected to time out on server after ~{}s)...", SCRIPT_TIMEOUT_SECONDS);
|
||||
log::info!(
|
||||
"Sending long-running script (expected to time out on server after ~{}s)...",
|
||||
SCRIPT_TIMEOUT_SECONDS
|
||||
);
|
||||
|
||||
match client.play(long_running_script).await {
|
||||
Ok(play_result) => {
|
||||
log::warn!("Received unexpected success from play request: {:?}", play_result);
|
||||
log::warn!(
|
||||
"Received unexpected success from play request: {:?}",
|
||||
play_result
|
||||
);
|
||||
log::warn!("This might indicate the script finished faster than expected, or the timeout didn't trigger.");
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -116,7 +166,10 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
if e.to_string().contains("timed out") || e.to_string().contains("-32002") {
|
||||
log::info!("Successfully received timeout error from the server!");
|
||||
} else {
|
||||
log::warn!("Received an error, but it might not be the expected timeout error: {}", e);
|
||||
log::warn!(
|
||||
"Received an error, but it might not be the expected timeout error: {}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,4 +180,4 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
log::info!("Timeout demonstration example finished.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user