split / cleanup core utils.
Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
parent
5fe5ab279c
commit
90d9a997a5
13 changed files with 205 additions and 191 deletions
|
@ -10,7 +10,7 @@ use ruma::api::client::{
|
|||
error::ErrorKind,
|
||||
};
|
||||
|
||||
use crate::{services, utils::conduwuit_version, Error, Result, Ruma};
|
||||
use crate::{services, Error, Result, Ruma};
|
||||
|
||||
/// # `GET /_matrix/client/versions`
|
||||
///
|
||||
|
@ -145,7 +145,7 @@ pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
|||
|
||||
Ok(Json(serde_json::json!({
|
||||
"server": server_url,
|
||||
"version": conduwuit_version(),
|
||||
"version": conduit::version::conduwuit(),
|
||||
})))
|
||||
}
|
||||
|
||||
|
@ -156,7 +156,7 @@ pub(crate) async fn syncv3_client_server_json() -> Result<impl IntoResponse> {
|
|||
pub(crate) async fn conduwuit_server_version() -> Result<impl IntoResponse> {
|
||||
Ok(Json(serde_json::json!({
|
||||
"name": "conduwuit",
|
||||
"version": conduwuit_version(),
|
||||
"version": conduit::version::conduwuit(),
|
||||
})))
|
||||
}
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ pub(crate) async fn get_server_version_route(
|
|||
Ok(get_server_version::v1::Response {
|
||||
server: Some(get_server_version::v1::Server {
|
||||
name: Some("Conduwuit".to_owned()),
|
||||
version: Some(utils::conduwuit_version()),
|
||||
version: Some(conduit::version::conduwuit()),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@
|
|||
|
||||
use std::{panic, panic::PanicInfo};
|
||||
|
||||
/// Export all of the ancillary tools from here as well.
|
||||
pub use crate::utils::debug::*;
|
||||
|
||||
/// Log event at given level in debug-mode (when debug-assertions are enabled).
|
||||
/// In release-mode it becomes DEBUG level, and possibly subject to elision.
|
||||
///
|
||||
|
|
|
@ -7,12 +7,12 @@ pub mod mods;
|
|||
pub mod pducount;
|
||||
pub mod server;
|
||||
pub mod utils;
|
||||
pub mod version;
|
||||
|
||||
pub use config::Config;
|
||||
pub use error::{Error, Result, RumaResponse};
|
||||
pub use pducount::PduCount;
|
||||
pub use server::Server;
|
||||
pub use utils::conduwuit_version;
|
||||
|
||||
#[cfg(not(conduit_mods))]
|
||||
pub mod mods {
|
||||
|
|
40
src/core/utils/debug.rs
Normal file
40
src/core/utils/debug.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use std::fmt;
|
||||
|
||||
/// Debug-formats the given slice, but only up to the first `max_len` elements.
|
||||
/// Any further elements are replaced by an ellipsis.
|
||||
///
|
||||
/// See also [`slice_truncated()`],
|
||||
pub struct TruncatedSlice<'a, T> {
|
||||
inner: &'a [T],
|
||||
max_len: usize,
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for TruncatedSlice<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.inner.len() <= self.max_len {
|
||||
write!(f, "{:?}", self.inner)
|
||||
} else {
|
||||
f.debug_list()
|
||||
.entries(&self.inner[..self.max_len])
|
||||
.entry(&"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`TruncatedSlice`]. Useful for `#[instrument]`:
|
||||
///
|
||||
/// ```
|
||||
/// use conduit_core::utils::debug::slice_truncated;
|
||||
///
|
||||
/// #[tracing::instrument(fields(foos = slice_truncated(foos, 42)))]
|
||||
/// fn bar(foos: &[&str]);
|
||||
/// ```
|
||||
pub fn slice_truncated<T: fmt::Debug>(
|
||||
slice: &[T], max_len: usize,
|
||||
) -> tracing::field::DebugValue<TruncatedSlice<'_, T>> {
|
||||
tracing::field::debug(TruncatedSlice {
|
||||
inner: slice,
|
||||
max_len,
|
||||
})
|
||||
}
|
37
src/core/utils/html.rs
Normal file
37
src/core/utils/html.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use std::fmt;
|
||||
|
||||
/// Wrapper struct which will emit the HTML-escaped version of the contained
|
||||
/// string when passed to a format string.
|
||||
pub struct Escape<'a>(pub &'a str);
|
||||
|
||||
/// Copied from librustdoc:
|
||||
/// * <https://github.com/rust-lang/rust/blob/cbaeec14f90b59a91a6b0f17fc046c66fa811892/src/librustdoc/html/escape.rs>
|
||||
impl fmt::Display for Escape<'_> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Because the internet is always right, turns out there's not that many
|
||||
// characters to escape: http://stackoverflow.com/questions/7381974
|
||||
let Escape(s) = *self;
|
||||
let pile_o_bits = s;
|
||||
let mut last = 0;
|
||||
for (i, ch) in s.char_indices() {
|
||||
let s = match ch {
|
||||
'>' => ">",
|
||||
'<' => "<",
|
||||
'&' => "&",
|
||||
'\'' => "'",
|
||||
'"' => """,
|
||||
_ => continue,
|
||||
};
|
||||
fmt.write_str(&pile_o_bits[last..i])?;
|
||||
fmt.write_str(s)?;
|
||||
// NOTE: we only expect single byte characters here - which is fine as long as
|
||||
// we only match single byte characters
|
||||
last = i + 1;
|
||||
}
|
||||
|
||||
if last < s.len() {
|
||||
fmt.write_str(&pile_o_bits[last..])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
39
src/core/utils/json.rs
Normal file
39
src/core/utils/json.rs
Normal file
|
@ -0,0 +1,39 @@
|
|||
use std::{fmt, str::FromStr};
|
||||
|
||||
use ruma::{canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject};
|
||||
|
||||
use crate::Result;
|
||||
|
||||
/// Fallible conversion from any value that implements `Serialize` to a
|
||||
/// `CanonicalJsonObject`.
|
||||
///
|
||||
/// `value` must serialize to an `serde_json::Value::Object`.
|
||||
pub fn to_canonical_object<T: serde::Serialize>(value: T) -> Result<CanonicalJsonObject, CanonicalJsonError> {
|
||||
use serde::ser::Error;
|
||||
|
||||
match serde_json::to_value(value).map_err(CanonicalJsonError::SerDe)? {
|
||||
serde_json::Value::Object(map) => try_from_json_map(map),
|
||||
_ => Err(CanonicalJsonError::SerDe(serde_json::Error::custom("Value must be an object"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_from_str<'de, D: serde::de::Deserializer<'de>, T: FromStr<Err = E>, E: fmt::Display>(
|
||||
deserializer: D,
|
||||
) -> Result<T, D::Error> {
|
||||
struct Visitor<T: FromStr<Err = E>, E>(std::marker::PhantomData<T>);
|
||||
impl<T: FromStr<Err = Err>, Err: fmt::Display> serde::de::Visitor<'_> for Visitor<T, Err> {
|
||||
type Value = T;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "a parsable string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
v.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_str(Visitor(std::marker::PhantomData))
|
||||
}
|
|
@ -1,21 +1,26 @@
|
|||
pub mod content_disposition;
|
||||
pub mod debug;
|
||||
pub mod defer;
|
||||
pub mod html;
|
||||
pub mod json;
|
||||
pub mod sys;
|
||||
|
||||
use std::{
|
||||
cmp,
|
||||
cmp::Ordering,
|
||||
fmt,
|
||||
str::FromStr,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
|
||||
pub use debug::slice_truncated as debug_slice_truncated;
|
||||
pub use html::Escape as HtmlEscape;
|
||||
pub use json::{deserialize_from_str, to_canonical_object};
|
||||
use rand::prelude::*;
|
||||
use ring::digest;
|
||||
use ruma::{canonical_json::try_from_json_map, CanonicalJsonError, CanonicalJsonObject, OwnedUserId};
|
||||
use tracing::debug;
|
||||
use ruma::OwnedUserId;
|
||||
pub use sys::available_parallelism;
|
||||
|
||||
use crate::{Error, Result};
|
||||
|
||||
pub mod content_disposition;
|
||||
pub mod defer;
|
||||
|
||||
pub fn clamp<T: Ord>(val: T, min: T, max: T) -> T { cmp::min(cmp::max(val, min), max) }
|
||||
|
||||
#[must_use]
|
||||
|
@ -108,178 +113,6 @@ pub fn common_elements(
|
|||
}))
|
||||
}
|
||||
|
||||
/// Fallible conversion from any value that implements `Serialize` to a
|
||||
/// `CanonicalJsonObject`.
|
||||
///
|
||||
/// `value` must serialize to an `serde_json::Value::Object`.
|
||||
pub fn to_canonical_object<T: serde::Serialize>(value: T) -> Result<CanonicalJsonObject, CanonicalJsonError> {
|
||||
use serde::ser::Error;
|
||||
|
||||
match serde_json::to_value(value).map_err(CanonicalJsonError::SerDe)? {
|
||||
serde_json::Value::Object(map) => try_from_json_map(map),
|
||||
_ => Err(CanonicalJsonError::SerDe(serde_json::Error::custom("Value must be an object"))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deserialize_from_str<'de, D: serde::de::Deserializer<'de>, T: FromStr<Err = E>, E: fmt::Display>(
|
||||
deserializer: D,
|
||||
) -> Result<T, D::Error> {
|
||||
struct Visitor<T: FromStr<Err = E>, E>(std::marker::PhantomData<T>);
|
||||
impl<T: FromStr<Err = Err>, Err: fmt::Display> serde::de::Visitor<'_> for Visitor<T, Err> {
|
||||
type Value = T;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(formatter, "a parsable string")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
|
||||
where
|
||||
E: serde::de::Error,
|
||||
{
|
||||
v.parse().map_err(serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
deserializer.deserialize_str(Visitor(std::marker::PhantomData))
|
||||
}
|
||||
|
||||
// Copied from librustdoc:
|
||||
// https://github.com/rust-lang/rust/blob/cbaeec14f90b59a91a6b0f17fc046c66fa811892/src/librustdoc/html/escape.rs
|
||||
|
||||
/// Wrapper struct which will emit the HTML-escaped version of the contained
|
||||
/// string when passed to a format string.
|
||||
pub struct HtmlEscape<'a>(pub &'a str);
|
||||
|
||||
impl fmt::Display for HtmlEscape<'_> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Because the internet is always right, turns out there's not that many
|
||||
// characters to escape: http://stackoverflow.com/questions/7381974
|
||||
let HtmlEscape(s) = *self;
|
||||
let pile_o_bits = s;
|
||||
let mut last = 0;
|
||||
for (i, ch) in s.char_indices() {
|
||||
let s = match ch {
|
||||
'>' => ">",
|
||||
'<' => "<",
|
||||
'&' => "&",
|
||||
'\'' => "'",
|
||||
'"' => """,
|
||||
_ => continue,
|
||||
};
|
||||
fmt.write_str(&pile_o_bits[last..i])?;
|
||||
fmt.write_str(s)?;
|
||||
// NOTE: we only expect single byte characters here - which is fine as long as
|
||||
// we only match single byte characters
|
||||
last = i + 1;
|
||||
}
|
||||
|
||||
if last < s.len() {
|
||||
fmt.write_str(&pile_o_bits[last..])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// one true function for returning the conduwuit version with the necessary
|
||||
/// CONDUWUIT_VERSION_EXTRA env variables used if specified
|
||||
///
|
||||
/// Set the environment variable `CONDUWUIT_VERSION_EXTRA` to any UTF-8 string
|
||||
/// to include it in parenthesis after the SemVer version. A common value are
|
||||
/// git commit hashes.
|
||||
#[must_use]
|
||||
pub fn conduwuit_version() -> String {
|
||||
match option_env!("CONDUWUIT_VERSION_EXTRA") {
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => env!("CARGO_PKG_VERSION").to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Debug-formats the given slice, but only up to the first `max_len` elements.
|
||||
/// Any further elements are replaced by an ellipsis.
|
||||
///
|
||||
/// See also [`debug_slice_truncated()`],
|
||||
pub struct TruncatedDebugSlice<'a, T> {
|
||||
inner: &'a [T],
|
||||
max_len: usize,
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for TruncatedDebugSlice<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.inner.len() <= self.max_len {
|
||||
write!(f, "{:?}", self.inner)
|
||||
} else {
|
||||
f.debug_list()
|
||||
.entries(&self.inner[..self.max_len])
|
||||
.entry(&"...")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// See [`TruncatedDebugSlice`]. Useful for `#[instrument]`:
|
||||
///
|
||||
/// ```
|
||||
/// use conduit_core::utils::debug_slice_truncated;
|
||||
///
|
||||
/// #[tracing::instrument(fields(foos = debug_slice_truncated(foos, 42)))]
|
||||
/// fn bar(foos: &[&str]);
|
||||
/// ```
|
||||
pub fn debug_slice_truncated<T: fmt::Debug>(
|
||||
slice: &[T], max_len: usize,
|
||||
) -> tracing::field::DebugValue<TruncatedDebugSlice<'_, T>> {
|
||||
tracing::field::debug(TruncatedDebugSlice {
|
||||
inner: slice,
|
||||
max_len,
|
||||
})
|
||||
}
|
||||
|
||||
/// This is needed for opening lots of file descriptors, which tends to
|
||||
/// happen more often when using RocksDB and making lots of federation
|
||||
/// connections at startup. The soft limit is usually 1024, and the hard
|
||||
/// limit is usually 512000; I've personally seen it hit >2000.
|
||||
///
|
||||
/// * <https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6>
|
||||
/// * <https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741>
|
||||
#[cfg(unix)]
|
||||
pub fn maximize_fd_limit() -> Result<(), nix::errno::Errno> {
|
||||
use nix::sys::resource::{getrlimit, setrlimit, Resource::RLIMIT_NOFILE as NOFILE};
|
||||
|
||||
let (soft_limit, hard_limit) = getrlimit(NOFILE)?;
|
||||
if soft_limit < hard_limit {
|
||||
setrlimit(NOFILE, hard_limit, hard_limit)?;
|
||||
assert_eq!((hard_limit, hard_limit), getrlimit(NOFILE)?, "getrlimit != setrlimit");
|
||||
debug!(to = hard_limit, from = soft_limit, "Raised RLIMIT_NOFILE",);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the number of threads which could execute in parallel based on the
|
||||
/// hardware and administrative constraints of this system. This value should be
|
||||
/// used to hint the size of thread-pools and divide-and-conquer algorithms.
|
||||
///
|
||||
/// * <https://doc.rust-lang.org/std/thread/fn.available_parallelism.html>
|
||||
#[must_use]
|
||||
pub fn available_parallelism() -> usize {
|
||||
std::thread::available_parallelism()
|
||||
.expect("Unable to query for available parallelism.")
|
||||
.get()
|
||||
}
|
||||
|
||||
/// Boilerplate for wraps which are typed to never error.
|
||||
///
|
||||
/// * <https://doc.rust-lang.org/std/convert/enum.Infallible.html>
|
||||
|
|
36
src/core/utils/sys.rs
Normal file
36
src/core/utils/sys.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use tracing::debug;
|
||||
|
||||
use crate::Result;
|
||||
|
||||
/// This is needed for opening lots of file descriptors, which tends to
|
||||
/// happen more often when using RocksDB and making lots of federation
|
||||
/// connections at startup. The soft limit is usually 1024, and the hard
|
||||
/// limit is usually 512000; I've personally seen it hit >2000.
|
||||
///
|
||||
/// * <https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6>
|
||||
/// * <https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741>
|
||||
#[cfg(unix)]
|
||||
pub fn maximize_fd_limit() -> Result<(), nix::errno::Errno> {
|
||||
use nix::sys::resource::{getrlimit, setrlimit, Resource::RLIMIT_NOFILE as NOFILE};
|
||||
|
||||
let (soft_limit, hard_limit) = getrlimit(NOFILE)?;
|
||||
if soft_limit < hard_limit {
|
||||
setrlimit(NOFILE, hard_limit, hard_limit)?;
|
||||
assert_eq!((hard_limit, hard_limit), getrlimit(NOFILE)?, "getrlimit != setrlimit");
|
||||
debug!(to = hard_limit, from = soft_limit, "Raised RLIMIT_NOFILE",);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the number of threads which could execute in parallel based on the
|
||||
/// hardware and administrative constraints of this system. This value should be
|
||||
/// used to hint the size of thread-pools and divide-and-conquer algorithms.
|
||||
///
|
||||
/// * <https://doc.rust-lang.org/std/thread/fn.available_parallelism.html>
|
||||
#[must_use]
|
||||
pub fn available_parallelism() -> usize {
|
||||
std::thread::available_parallelism()
|
||||
.expect("Unable to query for available parallelism.")
|
||||
.get()
|
||||
}
|
28
src/core/version.rs
Normal file
28
src/core/version.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
/// one true function for returning the conduwuit version with the necessary
|
||||
/// CONDUWUIT_VERSION_EXTRA env variables used if specified
|
||||
///
|
||||
/// Set the environment variable `CONDUWUIT_VERSION_EXTRA` to any UTF-8 string
|
||||
/// to include it in parenthesis after the SemVer version. A common value are
|
||||
/// git commit hashes.
|
||||
#[must_use]
|
||||
pub fn conduwuit() -> String {
|
||||
match option_env!("CONDUWUIT_VERSION_EXTRA") {
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => match option_env!("CONDUIT_VERSION_EXTRA") {
|
||||
Some(extra) => {
|
||||
if extra.is_empty() {
|
||||
env!("CARGO_PKG_VERSION").to_owned()
|
||||
} else {
|
||||
format!("{} ({})", env!("CARGO_PKG_VERSION"), extra)
|
||||
}
|
||||
},
|
||||
None => env!("CARGO_PKG_VERSION").to_owned(),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -3,11 +3,10 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use clap::Parser;
|
||||
use conduit_core::utils::conduwuit_version;
|
||||
|
||||
/// Commandline arguments
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(version = conduwuit_version(), about, long_about = None)]
|
||||
#[clap(version = conduit::version::conduwuit(), about, long_about = None)]
|
||||
pub(crate) struct Args {
|
||||
#[arg(short, long)]
|
||||
/// Optional argument to the path of a conduwuit config TOML file
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use conduit::{
|
||||
conduwuit_version,
|
||||
config::Config,
|
||||
info,
|
||||
log::{LogLevelReloadHandles, ReloadHandle},
|
||||
utils::maximize_fd_limit,
|
||||
utils::sys::maximize_fd_limit,
|
||||
Error, Result,
|
||||
};
|
||||
use tokio::runtime;
|
||||
|
@ -43,7 +42,7 @@ impl Server {
|
|||
database_path = ?config.database_path,
|
||||
log_levels = %config.log,
|
||||
"{}",
|
||||
conduwuit_version(),
|
||||
conduit::version::conduwuit(),
|
||||
);
|
||||
|
||||
Ok(Arc::new(Server {
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{sync::Arc, time::Duration};
|
|||
|
||||
use reqwest::redirect;
|
||||
|
||||
use crate::{service::globals::resolver, utils::conduwuit_version, Config, Result};
|
||||
use crate::{service::globals::resolver, Config, Result};
|
||||
|
||||
pub struct Client {
|
||||
pub default: reqwest::Client,
|
||||
|
@ -87,7 +87,7 @@ impl Client {
|
|||
}
|
||||
|
||||
fn base(config: &Config) -> Result<reqwest::ClientBuilder> {
|
||||
let version = conduwuit_version();
|
||||
let version = conduit::version::conduwuit();
|
||||
|
||||
let user_agent = format!("Conduwuit/{version}");
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue