initial import of main veilid core

This commit is contained in:
John Smith
2021-11-22 11:28:30 -05:00
parent c4cd54e020
commit 9e94a6a96f
218 changed files with 34880 additions and 1 deletions

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,12 @@
mod block_store;
mod network;
mod protected_store;
mod system;
mod table_store;
pub mod utils;
pub use block_store::*;
pub use network::*;
pub use protected_store::*;
pub use system::*;
pub use table_store::*;

View File

@@ -0,0 +1,179 @@
mod protocol;
use crate::intf::*;
use crate::network_manager::*;
use crate::routing_table::*;
use crate::*;
use protocol::ws::WebsocketProtocolHandler;
pub use protocol::*;
/////////////////////////////////////////////////////////////////
struct NetworkInner {
network_manager: NetworkManager,
stop_network: Eventual,
network_needs_restart: bool,
//join_handle: TryJoin?
}
#[derive(Clone)]
pub struct Network {
inner: Arc<Mutex<NetworkInner>>,
}
impl Network {
fn new_inner(network_manager: NetworkManager) -> NetworkInner {
NetworkInner {
network_manager: network_manager,
stop_network: Eventual::new(),
network_needs_restart: false,
//join_handle: None,
}
}
pub fn new(network_manager: NetworkManager) -> Self {
Self {
inner: Arc::new(Mutex::new(Self::new_inner(network_manager))),
}
}
/////////////////////////////////////////////////////////////////
async fn send_data_to_existing_connection(
&self,
descriptor: &ConnectionDescriptor,
data: Vec<u8>,
) -> Result<Option<Vec<u8>>, String> {
match descriptor.protocol_type() {
ProtocolType::UDP => return Err("no support for udp protocol".to_owned()),
ProtocolType::TCP => return Err("no support for tcp protocol".to_owned()),
ProtocolType::WS | ProtocolType::WSS => {
// find an existing connection in the connection table if one exists
let network_manager = self.inner.lock().network_manager.clone();
if let Some(entry) = network_manager
.connection_table()
.get_connection(&descriptor)
{
// connection exists, send over it
entry
.conn
.send(data)
.await
.map_err(|_| "failed to send ws message".to_owned())?;
// Data was consumed
return Ok(None);
}
}
}
// connection or local socket didn't exist, we'll need to use dialinfo to create one
// Pass the data back out so we don't own it any more
Ok(Some(data))
}
pub async fn send_data_unbound_to_dial_info(
&self,
dial_info: &DialInfo,
data: Vec<u8>,
) -> Result<(), String> {
let network_manager = self.inner.lock().network_manager.clone();
match &dial_info {
DialInfo::UDP(_) => return Err("no support for UDP protocol".to_owned()),
DialInfo::TCP(_) => return Err("no support for TCP protocol".to_owned()),
DialInfo::WS(_) => Err("WS protocol does not support unbound messages".to_owned()),
DialInfo::WSS(_) => Err("WSS protocol does not support unbound messages".to_owned()),
}
}
pub async fn send_data_to_dial_info(
&self,
dial_info: &DialInfo,
data: Vec<u8>,
) -> Result<(), String> {
let network_manager = self.inner.lock().network_manager.clone();
let conn = match &dial_info {
DialInfo::UDP(_) => return Err("no support for UDP protocol".to_owned()),
DialInfo::TCP(_) => return Err("no support for TCP protocol".to_owned()),
DialInfo::WS(_) => WebsocketProtocolHandler::connect(network_manager, dial_info)
.await
.map_err(|_| "failed to connect to WS dial info".to_owned())?,
DialInfo::WSS(_) => WebsocketProtocolHandler::connect(network_manager, dial_info)
.await
.map_err(|_| "failed to connect to WSS dial info".to_owned())?,
};
conn.send(data)
.await
.map_err(|_| "failed to send data to dial info".to_owned())
}
pub async fn send_data(&self, node_ref: NodeRef, data: Vec<u8>) -> Result<(), String> {
let dial_info = node_ref.dial_info();
let descriptor = node_ref.last_connection();
// First try to send data to the last socket we've seen this peer on
let di_data = if let Some(descriptor) = descriptor {
match self
.clone()
.send_data_to_existing_connection(&descriptor, data)
.await?
{
None => {
return Ok(());
}
Some(d) => d,
}
} else {
data
};
// If that fails, try to make a connection or reach out to the peer via its dial info
if let Some(di) = dial_info {
self.clone().send_data_to_dial_info(&di, di_data).await
} else {
Err("couldn't send data, no dial info or peer address".to_owned())
}
}
/////////////////////////////////////////////////////////////////
pub async fn startup(&self) -> Result<(), String> {
//let network_manager = self.inner.lock().network_manager.clone();
//let config_shared = network_manager.core().config();
//let config = config_shared.get();
Ok(())
}
pub fn needs_restart(&self) -> bool {
self.inner.lock().network_needs_restart
}
pub async fn shutdown(&self) {
trace!("stopping network");
// Reset state
let network_manager = self.inner.lock().network_manager.clone();
let routing_table = network_manager.routing_table();
// Drop all dial info
routing_table.clear_local_dial_info();
routing_table.clear_public_dial_info();
// Cancels all async background tasks by dropping join handles
*self.inner.lock() = Self::new_inner(network_manager);
trace!("network stopped");
}
//////////////////////////////////////////
pub fn get_network_class(&self) -> NetworkClass {
// xxx eventually detect tor browser?
return NetworkClass::WebApp;
}
//////////////////////////////////////////
pub async fn tick(&self) -> Result<(), String> {
Ok(())
}
}

View File

@@ -0,0 +1,48 @@
pub mod wrtc;
pub mod ws;
use crate::veilid_api::ProtocolType;
use crate::xx::*;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DummyNetworkConnection {}
impl DummyNetworkConnection {
pub fn protocol_type(&self) -> ProtocolType {
ProtocolType::UDP
}
pub fn send(&self, _message: Vec<u8>) -> SystemPinBoxFuture<Result<(), ()>> {
Box::pin(async { Ok(()) })
}
pub fn recv(&self) -> SystemPinBoxFuture<Result<Vec<u8>, ()>> {
Box::pin(async { Ok(Vec::new()) })
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum NetworkConnection {
Dummy(DummyNetworkConnection),
WS(ws::WebsocketNetworkConnection),
//WebRTC(wrtc::WebRTCNetworkConnection),
}
impl NetworkConnection {
pub fn protocol_type(&self) -> ProtocolType {
match self {
Self::Dummy(d) => d.protocol_type(),
Self::WS(w) => w.protocol_type(),
}
}
pub fn send(&self, message: Vec<u8>) -> SystemPinBoxFuture<Result<(), ()>> {
match self {
Self::Dummy(d) => d.send(message),
Self::WS(w) => w.send(message),
}
}
pub fn recv(&self) -> SystemPinBoxFuture<Result<Vec<u8>, ()>> {
match self {
Self::Dummy(d) => d.recv(),
Self::WS(w) => w.recv(),
}
}
}

View File

@@ -0,0 +1,116 @@
use crate::intf::*;
use crate::network_manager::{NetworkManager, MAX_MESSAGE_SIZE};
use crate::*;
use alloc::fmt;
use futures_util::stream::StreamExt;
use web_sys::WebSocket;
use ws_stream_wasm::*;
struct WebsocketNetworkConnectionInner {
_ws_meta: WsMeta,
ws_stream: WsStream,
ws: WebSocket,
}
#[derive(Clone)]
pub struct WebsocketNetworkConnection {
tls: bool,
inner: Arc<Mutex<WebsocketNetworkConnectionInner>>,
}
impl fmt::Debug for WebsocketNetworkConnection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", core::any::type_name::<Self>())
}
}
impl PartialEq for WebsocketNetworkConnection {
fn eq(&self, other: &Self) -> bool {
self.tls == other.tls && Arc::as_ptr(&self.inner) == Arc::as_ptr(&other.inner)
}
}
impl Eq for WebsocketNetworkConnection {}
impl WebsocketNetworkConnection {
pub fn new(tls: bool, ws_meta: WsMeta, ws_stream: WsStream) -> Self {
let ws = ws_stream.wrapped().clone();
Self {
tls: tls,
inner: Arc::new(Mutex::new(WebsocketNetworkConnectionInner {
_ws_meta: ws_meta,
ws_stream: ws_stream,
ws: ws,
})),
}
}
}
impl WebsocketNetworkConnection {
pub fn protocol_type(&self) -> ProtocolType {
if self.tls {
ProtocolType::WSS
} else {
ProtocolType::WS
}
}
pub fn send(&self, message: Vec<u8>) -> SystemPinBoxFuture<Result<(), ()>> {
let inner = self.inner.clone();
Box::pin(async move {
if message.len() > MAX_MESSAGE_SIZE {
return Err(());
}
inner.lock().ws.send_with_u8_array(&message).map_err(drop)
})
}
pub fn recv(&self) -> SystemPinBoxFuture<Result<Vec<u8>, ()>> {
let inner = self.inner.clone();
Box::pin(async move {
let out = match inner.lock().ws_stream.next().await {
Some(WsMessage::Binary(v)) => v,
_ => {
trace!("websocket recv failed");
return Err(());
}
};
if out.len() > MAX_MESSAGE_SIZE {
Err(())
} else {
Ok(out)
}
})
}
}
///////////////////////////////////////////////////////////
///
pub struct WebsocketProtocolHandler {}
impl WebsocketProtocolHandler {
pub async fn connect(
network_manager: NetworkManager,
dial_info: &DialInfo,
) -> Result<NetworkConnection, ()> {
let url = dial_info.to_url_string(None);
let (tls, host, port, protocol_type) = match dial_info {
DialInfo::WS(ws) => (false, ws.fqdn.clone(), ws.port, ProtocolType::WS),
DialInfo::WSS(wss) => (true, wss.fqdn.clone(), wss.port, ProtocolType::WSS),
_ => return Err(()),
};
let peer_addr = PeerAddress::new(Address::from_str(&host)?, port, protocol_type);
let (ws, wsio) = match WsMeta::connect(url, None).await {
Ok(conn) => conn,
Err(_) => return Err(()),
};
let conn = NetworkConnection::WS(WebsocketNetworkConnection::new(tls, ws, wsio));
network_manager
.on_new_connection(ConnectionDescriptor::new_no_local(peer_addr), conn.clone())
.await?;
Ok(conn)
}
}

View File

@@ -0,0 +1,185 @@
use super::utils;
use crate::xx::*;
use js_sys::*;
use wasm_bindgen_futures::*;
use web_sys::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(catch, js_name = setPassword, js_namespace = ["global", "wasmhost", "keytar"])]
fn keytar_setPassword(service: &str, account: &str, password: &str)
-> Result<Promise, JsValue>;
#[wasm_bindgen(catch, js_name = getPassword, js_namespace = ["global", "wasmhost", "keytar"])]
fn keytar_getPassword(service: &str, account: &str) -> Result<Promise, JsValue>;
#[wasm_bindgen(catch, js_name = deletePassword, js_namespace = ["global", "wasmhost", "keytar"])]
fn keytar_deletePassword(service: &str, account: &str) -> Result<Promise, JsValue>;
}
fn keyring_name(namespace: &str) -> String {
if namespace.len() == 0 {
"veilid".to_owned()
} else {
format!("veilid_{}", namespace)
}
}
fn browser_key_name(namespace: &str, key: &str) -> String {
if namespace.len() == 0 {
format!("__veilid_secret_{}", key)
} else {
format!("__veilid_{}_secret_{}", namespace, key)
}
}
pub async fn save_user_secret_string(
namespace: &str,
key: &str,
value: &str,
) -> Result<bool, String> {
if utils::is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(keyring_name(namespace).as_str(), key)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(v) => v.is_truthy(),
Err(_) => false,
};
match JsFuture::from(
keytar_setPassword(keyring_name(namespace).as_str(), key, value)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(_) => {}
Err(_) => return Err("Failed to set password".to_owned()),
}
Ok(prev)
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = browser_key_name(namespace, key);
let prev = match ls
.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())?
{
Some(_) => true,
None => false,
};
ls.set_item(&vkey, value)
.map_err(|_| "exception_thrown".to_owned())?;
Ok(prev)
} else {
Err("unimplemented".to_owned())
}
}
pub async fn load_user_secret_string(namespace: &str, key: &str) -> Result<Option<String>, String> {
if utils::is_nodejs() {
let prev = match JsFuture::from(
keytar_getPassword(keyring_name(namespace).as_str(), key)
.map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(p) => p,
Err(_) => JsValue::UNDEFINED,
};
if prev.is_undefined() || prev.is_null() {
return Ok(None);
}
Ok(prev.as_string())
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = browser_key_name(namespace, key);
ls.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())
} else {
Err("unimplemented".to_owned())
}
}
pub async fn remove_user_secret_string(namespace: &str, key: &str) -> Result<bool, String> {
if utils::is_nodejs() {
match JsFuture::from(
keytar_deletePassword("veilid", key).map_err(|_| "exception thrown".to_owned())?,
)
.await
{
Ok(v) => Ok(v.is_truthy()),
Err(_) => Err("Failed to delete".to_owned()),
}
} else if utils::is_browser() {
let win = match window() {
Some(w) => w,
None => {
return Err("failed to get window".to_owned());
}
};
let ls = match win
.local_storage()
.map_err(|_| "exception getting local storage".to_owned())?
{
Some(l) => l,
None => {
return Err("failed to get local storage".to_owned());
}
};
let vkey = browser_key_name(namespace, key);
match ls
.get_item(&vkey)
.map_err(|_| "exception_thrown".to_owned())?
{
Some(_) => {
ls.delete(&vkey)
.map_err(|_| "exception_thrown".to_owned())?;
Ok(true)
}
None => Ok(false),
}
} else {
Err("unimplemented".to_owned())
}
}

View File

@@ -0,0 +1,154 @@
use super::utils;
use crate::xx::*;
use crate::*;
pub use async_executors::JoinHandle;
use async_executors::{Bindgen, LocalSpawnHandleExt /*, SpawnHandleExt*/};
use core::fmt;
use futures_util::future::{select, Either};
use js_sys::*;
use wasm_bindgen_futures::*;
use web_sys::*;
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(catch, structural, js_namespace = global, js_name = setTimeout)]
fn nodejs_global_set_timeout_with_callback_and_timeout_and_arguments_0(
handler: &::js_sys::Function,
timeout: u32,
) -> Result<JsValue, JsValue>;
}
pub fn get_timestamp() -> u64 {
if utils::is_browser() {
return (Date::now() * 1000.0f64) as u64;
} else if utils::is_nodejs() {
return (Date::now() * 1000.0f64) as u64;
} else {
panic!("WASM requires browser or nodejs environment");
}
}
pub fn random_bytes(dest: &mut [u8]) -> Result<(), String> {
let len = dest.len();
let u32len = len / 4;
let remlen = len % 4;
for n in 0..u32len {
let r = (Math::random() * (u32::max_value() as f64)) as u32;
dest[n * 4 + 0] = (r & 0xFF) as u8;
dest[n * 4 + 1] = ((r >> 8) & 0xFF) as u8;
dest[n * 4 + 2] = ((r >> 16) & 0xFF) as u8;
dest[n * 4 + 3] = ((r >> 24) & 0xFF) as u8;
}
if remlen > 0 {
let r = (Math::random() * (u32::max_value() as f64)) as u32;
for n in 0..remlen {
dest[u32len * 4 + n] = ((r >> (n * 8)) & 0xFF) as u8;
}
}
Ok(())
}
pub fn get_random_u32() -> u32 {
(Math::random() * (u32::max_value() as f64)) as u32
}
pub fn get_random_u64() -> u64 {
let v1: u32 = get_random_u32();
let v2: u32 = get_random_u32();
((v1 as u64) << 32) | ((v2 as u32) as u64)
}
pub async fn sleep(millis: u32) {
if utils::is_browser() {
let wait_millis = if millis > u32::MAX {
i32::MAX
} else {
millis as i32
};
let promise = Promise::new(&mut |yes, _| {
let win = window().unwrap();
win.set_timeout_with_callback_and_timeout_and_arguments_0(&yes, wait_millis)
.unwrap();
});
JsFuture::from(promise).await.unwrap();
} else if utils::is_nodejs() {
let promise = Promise::new(&mut |yes, _| {
nodejs_global_set_timeout_with_callback_and_timeout_and_arguments_0(&yes, millis)
.unwrap();
});
JsFuture::from(promise).await.unwrap();
} else {
panic!("WASM requires browser or nodejs environment");
}
}
pub fn spawn<Out>(future: impl Future<Output = Out> + 'static) -> JoinHandle<Out>
where
Out: Send + 'static,
{
Bindgen
.spawn_handle_local(future)
.expect("wasm-bindgen-futures spawn should never error out")
}
pub fn spawn_local<Out>(future: impl Future<Output = Out> + 'static) -> JoinHandle<Out>
where
Out: 'static,
{
Bindgen
.spawn_handle_local(future)
.expect("wasm-bindgen-futures spawn_local should never error out")
}
pub fn interval<F, FUT>(freq_ms: u32, callback: F) -> SystemPinBoxFuture<()>
where
F: Fn() -> FUT + 'static,
FUT: Future<Output = ()>,
{
let e = Eventual::new();
let ie = e.clone();
let jh = spawn_local(Box::pin(async move {
while timeout(freq_ms, ie.instance_clone(())).await.is_err() {
callback().await;
}
}));
Box::pin(async move {
e.resolve().await;
jh.await;
})
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TimeoutError {
_private: (),
}
//impl Error for TimeoutError {}
impl fmt::Display for TimeoutError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"future has timed out".fmt(f)
}
}
pub async fn timeout<F, T>(dur_ms: u32, f: F) -> Result<T, TimeoutError>
where
F: Future<Output = T>,
{
match select(Box::pin(intf::sleep(dur_ms)), Box::pin(f)).await {
Either::Left((_x, _b)) => Err(TimeoutError { _private: () }),
Either::Right((y, _a)) => Ok(y),
}
}
// xxx: for now until wasm threads are more stable, and/or we bother with web workers
pub fn get_concurrency() -> u32 {
1
}

View File

@@ -0,0 +1,118 @@
use crate::intf::table_db::*;
use crate::intf::*;
use crate::*;
use keyvaluedb_web::*;
struct TableStoreInner {
config: VeilidConfig,
opened: BTreeMap<String, Weak<Mutex<TableDBInner>>>,
}
#[derive(Clone)]
pub struct TableStore {
inner: Arc<Mutex<TableStoreInner>>,
}
impl TableStore {
fn new_inner(config: VeilidConfig) -> TableStoreInner {
TableStoreInner {
config: config,
opened: BTreeMap::new(),
}
}
pub fn new(config: VeilidConfig) -> Self {
Self {
inner: Arc::new(Mutex::new(Self::new_inner(config))),
}
}
pub async fn init(&self) -> Result<(), String> {
Ok(())
}
pub async fn terminate(&self) {
assert!(
self.inner.lock().opened.len() == 0,
"all open databases should have been closed"
);
}
pub fn on_table_db_drop(&self, table: String) {
let mut inner = self.inner.lock();
match inner.opened.remove(&table) {
Some(_) => (),
None => {
assert!(false, "should have removed an item");
}
}
}
fn get_table_name(inner: &TableStoreInner, table: &str) -> Result<String, String> {
if !table
.chars()
.all(|c| char::is_alphanumeric(c) || c == '_' || c == '-')
{
return Err(format!("table name '{}' is invalid", table));
}
let c = inner.config.get();
let namespace = c.namespace.clone();
Ok(if namespace.len() == 0 {
format!("{}", table)
} else {
format!("_ns_{}_{}", namespace, table)
})
}
pub async fn open(&self, name: &str, column_count: u32) -> Result<TableDB, String> {
let mut inner = self.inner.lock();
let table_name = Self::get_table_name(&*inner, name)?;
if let Some(table_db_weak_inner) = inner.opened.get(&table_name) {
match TableDB::try_new_from_weak_inner(table_db_weak_inner.clone()) {
Some(tdb) => {
return Ok(tdb);
}
None => {
inner.opened.remove(&table_name);
}
};
}
let db = Database::open(table_name.clone(), column_count)
.await
.map_err(|e| format!("failed to open tabledb at: {} ({})", table_name, e))?;
let table_db = TableDB::new(table_name.clone(), self.clone(), db);
inner.opened.insert(table_name, table_db.weak_inner());
Ok(table_db)
}
pub async fn delete(&self, name: &str) -> Result<bool, String> {
trace!("TableStore::delete {}", name);
let inner = self.inner.lock();
let table_name = Self::get_table_name(&*inner, name)?;
if inner.opened.contains_key(&table_name) {
trace!(
"TableStore::delete {}: Not deleting, still open.",
table_name
);
return Err("Not deleting table that is still opened".to_owned());
}
if utils::is_nodejs() {
Err("unimplemented".to_owned())
} else if utils::is_browser() {
let out = match Database::delete(table_name.clone()).await {
Ok(_) => true,
Err(_) => false,
};
//.map_err(|e| format!("failed to delete tabledb at: {} ({})", table_name, e))?;
trace!("TableStore::deleted {}", table_name);
Ok(out)
} else {
Err("unimplemented".to_owned())
}
}
}

View File

@@ -0,0 +1,140 @@
use crate::xx::*;
use alloc::collections::VecDeque;
#[derive(Debug)]
pub struct Channel<T> {
items: VecDeque<T>,
cap: usize,
eventual: Eventual,
}
#[derive(Debug)]
pub struct Sender<T> {
imp: Arc<Mutex<Channel<T>>>,
}
impl<T> Clone for Sender<T> {
fn clone(&self) -> Self {
Self {
imp: self.imp.clone(),
}
}
}
#[derive(Debug)]
pub struct Receiver<T> {
imp: Arc<Mutex<Channel<T>>>,
}
impl<T> Clone for Receiver<T> {
fn clone(&self) -> Self {
Self {
imp: self.imp.clone(),
}
}
}
pub fn channel<T>(cap: usize) -> (Sender<T>, Receiver<T>) {
let imp = Channel {
items: VecDeque::with_capacity(cap),
cap: cap,
eventual: Eventual::new(),
};
let imparc = Arc::new(Mutex::new(imp));
(
Sender {
imp: imparc.clone(),
},
Receiver {
imp: imparc.clone(),
},
)
}
#[derive(Debug, PartialEq, Eq)]
pub enum TrySendError<T> {
Full(T),
Disconnected(T),
}
impl<T> Sender<T> {
// NOTE: This needs a timeout or you could block a very long time
// pub async fn send(&self, msg: T) -> Result<(), SendError<T>> {
// xxx
// }
pub async fn try_send(&self, msg: T) -> Result<(), TrySendError<T>> {
let eventual = {
let mut inner = self.imp.lock();
if inner.items.len() == inner.cap {
return Err(TrySendError::Full(msg));
}
let empty = inner.items.is_empty();
inner.items.push_back(msg);
if empty {
Some(inner.eventual.clone())
} else {
None
}
};
if let Some(e) = eventual {
e.resolve().await;
}
Ok(())
}
pub fn capacity(&self) -> usize {
self.imp.lock().cap
}
pub fn is_empty(&self) -> bool {
self.imp.lock().items.is_empty()
}
pub fn is_full(&self) -> bool {
let inner = self.imp.lock();
inner.items.len() == inner.cap
}
pub fn len(&self) -> usize {
self.imp.lock().items.len()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct RecvError;
#[derive(Debug, PartialEq, Eq)]
pub enum TryRecvError {
Empty,
Disconnected,
}
impl<T> Receiver<T> {
pub async fn recv(&self) -> Result<T, RecvError> {
let eventual = {
let inner = self.imp.lock();
inner.eventual.clone()
};
while self.is_empty() {
eventual.instance_clone(true).await;
}
Ok(self.imp.lock().items.pop_front().unwrap())
}
pub async fn try_recv(&self) -> Result<T, TryRecvError> {
if self.is_empty() {
return Err(TryRecvError::Empty);
}
Ok(self.imp.lock().items.pop_front().unwrap())
}
pub fn capacity(&self) -> usize {
self.imp.lock().cap
}
pub fn is_empty(&self) -> bool {
self.imp.lock().items.is_empty()
}
pub fn is_full(&self) -> bool {
let inner = self.imp.lock();
inner.items.len() == inner.cap
}
pub fn len(&self) -> usize {
self.imp.lock().items.len()
}
}

View File

@@ -0,0 +1,91 @@
#![cfg(target_arch = "wasm32")]
pub mod channel;
use crate::xx::*;
use core::sync::atomic::{AtomicI8, Ordering};
use js_sys::{global, Reflect};
cfg_if! {
if #[cfg(feature = "wee_alloc")] {
// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
extern crate wee_alloc;
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
}
}
#[wasm_bindgen]
extern "C" {
// Use `js_namespace` here to bind `console.log(..)` instead of just
// `log(..)`
#[wasm_bindgen(js_namespace = console, js_name = log)]
pub fn console_log(s: &str);
#[wasm_bindgen]
pub fn alert(s: &str);
}
pub fn is_nodejs() -> bool {
static CACHE: AtomicI8 = AtomicI8::new(-1);
let cache = CACHE.load(Ordering::Relaxed);
if cache != -1 {
return cache != 0;
}
let res = js_sys::eval("process.release.name === 'node'")
.map(|res| res.is_truthy())
.unwrap_or_default();
CACHE.store(res as i8, Ordering::Relaxed);
res
}
pub fn is_browser() -> bool {
static CACHE: AtomicI8 = AtomicI8::new(-1);
let cache = CACHE.load(Ordering::Relaxed);
if cache != -1 {
return cache != 0;
}
let res = Reflect::has(&global().as_ref(), &"window".into()).unwrap_or_default();
CACHE.store(res as i8, Ordering::Relaxed);
res
}
pub fn is_browser_https() -> bool {
static CACHE: AtomicI8 = AtomicI8::new(-1);
let cache = CACHE.load(Ordering::Relaxed);
if cache != -1 {
return cache != 0;
}
let res = js_sys::eval("window.location.protocol === 'https'")
.map(|res| res.is_truthy())
.unwrap_or_default();
CACHE.store(res as i8, Ordering::Relaxed);
res
}
pub fn node_require(module: &str) -> JsValue {
if !is_nodejs() {
return JsValue::UNDEFINED;
}
let mut home = env!("CARGO_MANIFEST_DIR");
if home.len() == 0 {
home = ".";
}
match js_sys::eval(format!("require(\"{}/{}\")", home, module).as_str()) {
Ok(v) => v,
Err(e) => {
panic!("node_require failed: {:?}", e);
}
}
}