refactor
This commit is contained in:
@@ -15,9 +15,12 @@ mod mutable_future;
|
||||
mod single_shot_eventual;
|
||||
mod split_url;
|
||||
mod tick_task;
|
||||
mod timeout_or;
|
||||
mod tools;
|
||||
|
||||
pub use cfg_if::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use eyre::{bail, ensure, eyre, Report as EyreReport, Result as EyreResult, WrapErr};
|
||||
pub use futures_util::future::{select, Either};
|
||||
pub use futures_util::select;
|
||||
pub use futures_util::stream::FuturesUnordered;
|
||||
@@ -28,6 +31,7 @@ pub use parking_lot::*;
|
||||
pub use split_url::*;
|
||||
pub use static_assertions::*;
|
||||
pub use stop_token::*;
|
||||
pub use thiserror::Error as ThisError;
|
||||
pub use tracing::*;
|
||||
|
||||
pub type PinBox<T> = Pin<Box<T>>;
|
||||
@@ -124,4 +128,5 @@ pub use mutable_future::*;
|
||||
// pub use single_future::*;
|
||||
pub use single_shot_eventual::*;
|
||||
pub use tick_task::*;
|
||||
pub use timeout_or::*;
|
||||
pub use tools::*;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
// URLs must convert to UTF8
|
||||
// Only IP address and DNS hostname host fields are supported
|
||||
|
||||
use super::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use super::*;
|
||||
use alloc::borrow::ToOwned;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
@@ -62,12 +62,14 @@ fn is_valid_scheme<H: AsRef<str>>(host: H) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn hex_decode(h: u8) -> Result<u8, String> {
|
||||
fn hex_decode(h: u8) -> Result<u8, SplitUrlError> {
|
||||
match h {
|
||||
b'0'..=b'9' => Ok(h - b'0'),
|
||||
b'A'..=b'F' => Ok(h - b'A' + 10),
|
||||
b'a'..=b'f' => Ok(h - b'a' + 10),
|
||||
_ => Err("Unexpected character in percent encoding".to_owned()),
|
||||
_ => Err(SplitUrlError::new(
|
||||
"Unexpected character in percent encoding",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,10 +90,10 @@ fn hex_encode(c: u8) -> (char, char) {
|
||||
)
|
||||
}
|
||||
|
||||
fn url_decode<S: AsRef<str>>(s: S) -> Result<String, String> {
|
||||
fn url_decode<S: AsRef<str>>(s: S) -> Result<String, SplitUrlError> {
|
||||
let url = s.as_ref().to_owned();
|
||||
if !url.is_ascii() {
|
||||
return Err("URL is not in ASCII encoding".to_owned());
|
||||
return Err(SplitUrlError::new("URL is not in ASCII encoding"));
|
||||
}
|
||||
let url_bytes = url.as_bytes();
|
||||
let mut dec_bytes: Vec<u8> = Vec::with_capacity(url_bytes.len());
|
||||
@@ -102,14 +104,15 @@ fn url_decode<S: AsRef<str>>(s: S) -> Result<String, String> {
|
||||
i += 1;
|
||||
if b == b'%' {
|
||||
if (i + 1) >= end {
|
||||
return Err("Invalid URL encoding".to_owned());
|
||||
return Err(SplitUrlError::new("Invalid URL encoding"));
|
||||
}
|
||||
b = hex_decode(url_bytes[i])? << 4 | hex_decode(url_bytes[i + 1])?;
|
||||
i += 2;
|
||||
}
|
||||
dec_bytes.push(b);
|
||||
}
|
||||
String::from_utf8(dec_bytes).map_err(|e| format!("Decoded URL is not valid UTF-8: {}", e))
|
||||
String::from_utf8(dec_bytes)
|
||||
.map_err(|e| SplitUrlError::new(format!("Decoded URL is not valid UTF-8: {}", e)))
|
||||
}
|
||||
|
||||
fn url_encode<S: AsRef<str>>(s: S, must_encode: impl Fn(u8) -> bool) -> String {
|
||||
@@ -128,14 +131,25 @@ fn url_encode<S: AsRef<str>>(s: S, must_encode: impl Fn(u8) -> bool) -> String {
|
||||
out
|
||||
}
|
||||
|
||||
fn convert_port<N>(port_str: N) -> Result<u16, String>
|
||||
fn convert_port<N>(port_str: N) -> Result<u16, SplitUrlError>
|
||||
where
|
||||
N: AsRef<str>,
|
||||
{
|
||||
port_str
|
||||
.as_ref()
|
||||
.parse::<u16>()
|
||||
.map_err(|e| format!("Invalid port: {}", e))
|
||||
.map_err(|e| SplitUrlError::new(format!("Invalid port: {}", e)))
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
#[derive(ThisError, Debug, Clone, Eq, PartialEq)]
|
||||
#[error("SplitUrlError: {0}")]
|
||||
pub struct SplitUrlError(String);
|
||||
|
||||
impl SplitUrlError {
|
||||
pub fn new<T: ToString>(message: T) -> Self {
|
||||
SplitUrlError(message.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -163,7 +177,7 @@ impl SplitUrlPath {
|
||||
}
|
||||
|
||||
impl FromStr for SplitUrlPath {
|
||||
type Err = String;
|
||||
type Err = SplitUrlError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(if let Some((p, q)) = s.split_once('?') {
|
||||
if let Some((p, f)) = p.split_once('#') {
|
||||
@@ -213,17 +227,17 @@ pub enum SplitUrlHost {
|
||||
}
|
||||
|
||||
impl SplitUrlHost {
|
||||
pub fn new<S: AsRef<str>>(s: S) -> Result<Self, String> {
|
||||
pub fn new<S: AsRef<str>>(s: S) -> Result<Self, SplitUrlError> {
|
||||
Self::from_str(s.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for SplitUrlHost {
|
||||
type Err = String;
|
||||
type Err = SplitUrlError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.is_empty() {
|
||||
return Err("Host is empty".to_owned());
|
||||
return Err(SplitUrlError::new("Host is empty"));
|
||||
}
|
||||
if let Ok(v4) = Ipv4Addr::from_str(s) {
|
||||
return Ok(SplitUrlHost::IpAddr(IpAddr::V4(v4)));
|
||||
@@ -232,13 +246,13 @@ impl FromStr for SplitUrlHost {
|
||||
if let Ok(v6) = Ipv6Addr::from_str(&s[1..s.len() - 1]) {
|
||||
return Ok(SplitUrlHost::IpAddr(IpAddr::V6(v6)));
|
||||
}
|
||||
return Err("Invalid ipv6 address".to_owned());
|
||||
return Err(SplitUrlError::new("Invalid ipv6 address"));
|
||||
}
|
||||
for ch in s.chars() {
|
||||
if !matches!(ch,
|
||||
'A'..='Z' | 'a'..='z' | '0'..='9' | '-' | '.' )
|
||||
{
|
||||
return Err("Invalid hostname".to_owned());
|
||||
return Err(SplitUrlError::new("Invalid hostname"));
|
||||
}
|
||||
}
|
||||
Ok(SplitUrlHost::Hostname(s.to_owned()))
|
||||
@@ -311,11 +325,11 @@ fn split_host_with_port(s: &str) -> Option<(&str, &str)> {
|
||||
}
|
||||
|
||||
impl FromStr for SplitUrl {
|
||||
type Err = String;
|
||||
type Err = SplitUrlError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if let Some((scheme, mut rest)) = s.split_once("://") {
|
||||
if !is_valid_scheme(scheme) {
|
||||
return Err("Invalid scheme specified".to_owned());
|
||||
return Err(SplitUrlError::new("Invalid scheme specified"));
|
||||
}
|
||||
let userinfo = {
|
||||
if let Some((userinfo_str, after)) = rest.split_once('@') {
|
||||
@@ -350,7 +364,7 @@ impl FromStr for SplitUrl {
|
||||
Ok(SplitUrl::new(scheme, userinfo, host, None, None))
|
||||
}
|
||||
} else {
|
||||
Err("No scheme specified".to_owned())
|
||||
Err(SplitUrlError::new("No scheme specified"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,26 +5,33 @@ use once_cell::sync::OnceCell;
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
type TickTaskRoutine =
|
||||
dyn Fn(StopToken, u64, u64) -> PinBoxFuture<Result<(), String>> + 'static;
|
||||
type TickTaskRoutine<E> =
|
||||
dyn Fn(StopToken, u64, u64) -> PinBoxFuture<Result<(), E>> + 'static;
|
||||
} else {
|
||||
type TickTaskRoutine =
|
||||
dyn Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), String>> + Send + Sync + 'static;
|
||||
type TickTaskRoutine<E> =
|
||||
dyn Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), E>> + Send + Sync + 'static;
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs a single-future background processing task, attempting to run it once every 'tick period' microseconds.
|
||||
/// If the prior tick is still running, it will allow it to finish, and do another tick when the timer comes around again.
|
||||
/// One should attempt to make tasks short-lived things that run in less than the tick period if you want things to happen with regular periodicity.
|
||||
pub struct TickTask {
|
||||
pub struct TickTask<
|
||||
#[cfg(target_arch = "wasm32")] E: 'static,
|
||||
#[cfg(not(target_arch = "wasm32"))] E: Send + 'static,
|
||||
> {
|
||||
last_timestamp_us: AtomicU64,
|
||||
tick_period_us: u64,
|
||||
routine: OnceCell<Box<TickTaskRoutine>>,
|
||||
routine: OnceCell<Box<TickTaskRoutine<E>>>,
|
||||
stop_source: AsyncMutex<Option<StopSource>>,
|
||||
single_future: MustJoinSingleFuture<Result<(), String>>,
|
||||
single_future: MustJoinSingleFuture<Result<(), E>>,
|
||||
}
|
||||
|
||||
impl TickTask {
|
||||
impl<
|
||||
#[cfg(target_arch = "wasm32")] E: 'static,
|
||||
#[cfg(not(target_arch = "wasm32"))] E: Send + 'static,
|
||||
> TickTask<E>
|
||||
{
|
||||
pub fn new_us(tick_period_us: u64) -> Self {
|
||||
Self {
|
||||
last_timestamp_us: AtomicU64::new(0),
|
||||
@@ -53,25 +60,14 @@ impl TickTask {
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
pub fn set_routine(
|
||||
&self,
|
||||
routine: impl Fn(StopToken, u64, u64) -> PinBoxFuture<Result<(), String>> + 'static,
|
||||
) {
|
||||
self.routine.set(Box::new(routine)).map_err(drop).unwrap();
|
||||
}
|
||||
} else {
|
||||
pub fn set_routine(
|
||||
&self,
|
||||
routine: impl Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), String>> + Send + Sync + 'static,
|
||||
) {
|
||||
self.routine.set(Box::new(routine)).map_err(drop).unwrap();
|
||||
}
|
||||
}
|
||||
pub fn set_routine(
|
||||
&self,
|
||||
routine: impl Fn(StopToken, u64, u64) -> SendPinBoxFuture<Result<(), E>> + Send + Sync + 'static,
|
||||
) {
|
||||
self.routine.set(Box::new(routine)).map_err(drop).unwrap();
|
||||
}
|
||||
|
||||
pub async fn stop(&self) -> Result<(), String> {
|
||||
pub async fn stop(&self) -> Result<(), E> {
|
||||
// drop the stop source if we have one
|
||||
let opt_stop_source = &mut *self.stop_source.lock().await;
|
||||
if opt_stop_source.is_none() {
|
||||
@@ -89,7 +85,7 @@ impl TickTask {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn tick(&self) -> Result<(), String> {
|
||||
pub async fn tick(&self) -> Result<(), E> {
|
||||
let now = intf::get_timestamp();
|
||||
let last_timestamp_us = self.last_timestamp_us.load(Ordering::Acquire);
|
||||
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
use super::*;
|
||||
use cfg_if::*;
|
||||
use core::fmt::{Debug, Display};
|
||||
use core::result::Result;
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
|
||||
#[derive(ThisError, Debug, Clone, Copy, Eq, PartialEq)]
|
||||
#[error("Timeout")]
|
||||
pub struct TimeoutError();
|
||||
|
||||
impl TimeoutError {
|
||||
pub fn to_io(self) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::TimedOut, self)
|
||||
}
|
||||
}
|
||||
|
||||
cfg_if! {
|
||||
if #[cfg(feature="rt-async-std")] {
|
||||
impl From<async_std::future::TimeoutError> for TimeoutError {
|
||||
fn from(_: async_std::future::TimeoutError) -> Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
} else if #[cfg(feature="rt-tokio")] {
|
||||
impl From<tokio::time::error::Elapsed> for TimeoutError {
|
||||
fn from(_: tokio::time::error::Elapsed) -> Self {
|
||||
Self()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
pub enum TimeoutOr<T> {
|
||||
Timeout,
|
||||
Value(T),
|
||||
}
|
||||
|
||||
impl<T> TimeoutOr<T> {
|
||||
pub fn timeout() -> Self {
|
||||
Self::Timeout
|
||||
}
|
||||
pub fn value(value: T) -> Self {
|
||||
Self::Value(value)
|
||||
}
|
||||
|
||||
pub fn is_timeout(&self) -> bool {
|
||||
matches!(self, Self::Timeout)
|
||||
}
|
||||
|
||||
pub fn is_value(&self) -> bool {
|
||||
matches!(self, Self::Value(_))
|
||||
}
|
||||
|
||||
pub fn ok(self) -> Result<T, TimeoutError> {
|
||||
match self {
|
||||
Self::Timeout => Err(TimeoutError {}),
|
||||
Self::Value(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<TimeoutOr<T>> for Option<T> {
|
||||
fn from(t: TimeoutOr<T>) -> Self {
|
||||
match t {
|
||||
TimeoutOr::<T>::Timeout => None,
|
||||
TimeoutOr::<T>::Value(v) => Some(v),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Clone for TimeoutOr<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
Self::Timeout => Self::Timeout,
|
||||
Self::Value(t) => Self::Value(t.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: Debug> Debug for TimeoutOr<T> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
match self {
|
||||
Self::Timeout => write!(f, "Timeout"),
|
||||
Self::Value(arg0) => f.debug_tuple("Value").field(arg0).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: Display> Display for TimeoutOr<T> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Timeout => write!(f, ""),
|
||||
Self::Value(arg0) => write!(f, "{}", arg0),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T: Debug + Display> Error for TimeoutOr<T> {}
|
||||
+30
-25
@@ -1,5 +1,6 @@
|
||||
use crate::xx::*;
|
||||
use alloc::string::ToString;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[macro_export]
|
||||
@@ -11,6 +12,24 @@ macro_rules! assert_err {
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! io_error_other {
|
||||
($msg:expr) => {
|
||||
io::Error::new(io::ErrorKind::Other, $msg.to_string())
|
||||
};
|
||||
}
|
||||
|
||||
pub fn to_io_error_other<E: std::error::Error + Send + Sync + 'static>(x: E) -> io::Error {
|
||||
io::Error::new(io::ErrorKind::Other, x)
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! bail_io_error_other {
|
||||
($msg:expr) => {
|
||||
return io::Result::Err(io::Error::new(io::ErrorKind::Other, $msg.to_string()))
|
||||
};
|
||||
}
|
||||
|
||||
pub fn split_port(name: &str) -> Result<(String, Option<u16>), String> {
|
||||
if let Some(split) = name.rfind(':') {
|
||||
let hoststr = &name[0..split];
|
||||
@@ -129,7 +148,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<SocketAddr>, String> {
|
||||
pub fn listen_address_to_socket_addrs(listen_address: &str) -> EyreResult<Vec<SocketAddr>> {
|
||||
// If no address is specified, but the port is, use ipv4 and ipv6 unspecified
|
||||
// If the address is specified, only use the specified port and fail otherwise
|
||||
let ip_addrs = vec![
|
||||
@@ -138,12 +157,9 @@ pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<Socket
|
||||
];
|
||||
|
||||
Ok(if let Some(portstr) = listen_address.strip_prefix(':') {
|
||||
let port = portstr.parse::<u16>().map_err(|_| {
|
||||
format!(
|
||||
"Invalid port format in udp listen address: {}",
|
||||
listen_address
|
||||
)
|
||||
})?;
|
||||
let port = portstr
|
||||
.parse::<u16>()
|
||||
.wrap_err("Invalid port format in udp listen address")?;
|
||||
ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
|
||||
} else if let Ok(port) = listen_address.parse::<u16>() {
|
||||
ip_addrs.iter().map(|a| SocketAddr::new(*a, port)).collect()
|
||||
@@ -151,11 +167,11 @@ pub fn listen_address_to_socket_addrs(listen_address: &str) -> Result<Vec<Socket
|
||||
cfg_if! {
|
||||
if #[cfg(target_arch = "wasm32")] {
|
||||
use core::str::FromStr;
|
||||
vec![SocketAddr::from_str(listen_address).map_err(|_| format!("Unable to parse address: {}", listen_address))?]
|
||||
vec![SocketAddr::from_str(listen_address).wrap_err("Unable to parse address")?]
|
||||
} else {
|
||||
listen_address
|
||||
.to_socket_addrs()
|
||||
.map_err(|_| format!("Unable to resolve address: {}", listen_address))?
|
||||
.wrap_err("Unable to resolve address")?
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -185,7 +201,7 @@ cfg_if::cfg_if! {
|
||||
use std::os::unix::prelude::PermissionsExt;
|
||||
use nix::unistd::{Uid, Gid};
|
||||
|
||||
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(), String>
|
||||
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> EyreResult<()>
|
||||
{
|
||||
let path = path.as_ref();
|
||||
if !path.exists() {
|
||||
@@ -194,13 +210,13 @@ cfg_if::cfg_if! {
|
||||
|
||||
let uid = Uid::effective();
|
||||
let gid = Gid::effective();
|
||||
let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path '{:?}': {}",path, e))?;
|
||||
let meta = std::fs::metadata(path).wrap_err("unable to get metadata for path")?;
|
||||
|
||||
if meta.mode() != 0o600 {
|
||||
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path '{:?}': {}", path, e))?;
|
||||
std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).wrap_err("unable to set correct permissions on path")?;
|
||||
}
|
||||
if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() {
|
||||
return Err(format!("path has incorrect owner/group: {:?}", path));
|
||||
bail!("path has incorrect owner/group");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -208,24 +224,13 @@ cfg_if::cfg_if! {
|
||||
//use std::os::windows::fs::MetadataExt;
|
||||
//use windows_permissions::*;
|
||||
|
||||
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> Result<(),String>
|
||||
pub fn ensure_file_private_owner<P:AsRef<Path>>(path: P) -> EyreResult<()>
|
||||
{
|
||||
let path = path.as_ref();
|
||||
if !path.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// let uid = Uid::effective();
|
||||
// let gid = Gid::effective();
|
||||
//let meta = std::fs::metadata(path).map_err(|e| format!("unable to get metadata for path '{:?}': {}",path, e))?;
|
||||
|
||||
//if meta.mode() != 0o600 {
|
||||
// std::fs::set_permissions(path,std::fs::Permissions::from_mode(0o600)).map_err(|e| format!("unable to set correct permissions on path '{:?}': {}", path, e))?;
|
||||
//}
|
||||
|
||||
//if meta.uid() != uid.as_raw() || meta.gid() != gid.as_raw() {
|
||||
// chown(path, Some(uid), Some(gid)).map_err(|e| format!("unable to set correct owner on path '{:?}': {}", path, e))?;
|
||||
//}
|
||||
Ok(())
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user