Subpackage common -> ariadne (#3323)

* Subpackage common -> ariadne

* add common

* Remove build

* only build labrinth

* common

* set sqlx offline

* copy dirs

* Fix build
This commit is contained in:
Jai Agrawal
2025-03-01 20:53:43 -08:00
committed by GitHub
parent 650ab71a83
commit 19787a3f51
80 changed files with 181 additions and 152 deletions

View File

@@ -63,7 +63,7 @@ base64 = "0.22.0"
sqlx = { version = "0.8.2", features = [ "runtime-tokio", "sqlite", "macros" ] }
labrinth = { path = "../../apps/labrinth" }
ariadne = { path = "../ariadne" }
[target.'cfg(windows)'.dependencies]
winreg = "0.52.0"

View File

@@ -1,5 +1,5 @@
use crate::state::{FriendsSocket, UserFriend};
use labrinth::common::users::UserStatus;
use ariadne::users::UserStatus;
#[tracing::instrument]
pub async fn friends() -> crate::Result<Vec<UserFriend>> {

View File

@@ -21,7 +21,7 @@ pub mod data {
Project, ProjectType, SearchResult, SearchResults, Settings,
TeamMember, Theme, User, UserFriend, Version, WindowSize,
};
pub use labrinth::common::users::UserStatus;
pub use ariadne::users::UserStatus;
}
pub mod prelude {

View File

@@ -15,7 +15,7 @@ pub enum ErrorKind {
#[error("Serialization error (websocket): {0}")]
WebsocketSerializationError(
#[from] labrinth::common::networking::serialization::SerializationError,
#[from] ariadne::networking::serialization::SerializationError,
),
#[error("Error parsing UUID: {0}")]

View File

@@ -1,6 +1,6 @@
//! Theseus state management system
use ariadne::users::{UserId, UserStatus};
use dashmap::DashMap;
use labrinth::common::users::{UserId, UserStatus};
use serde::{Deserialize, Serialize};
use std::{path::PathBuf, sync::Arc};
#[cfg(feature = "tauri")]

View File

@@ -5,6 +5,10 @@ use crate::event::FriendPayload;
use crate::state::tunnel::InternalTunnelSocket;
use crate::state::{ProcessManager, Profile, TunnelSocket};
use crate::util::fetch::{fetch_advanced, fetch_json, FetchSemaphore};
use ariadne::networking::message::{
ClientToServerMessage, ServerToClientMessage,
};
use ariadne::users::{UserId, UserStatus};
use async_tungstenite::tokio::{connect_async, ConnectStream};
use async_tungstenite::tungstenite::client::IntoClientRequest;
use async_tungstenite::tungstenite::Message;
@@ -14,10 +18,6 @@ use dashmap::DashMap;
use either::Either;
use futures::stream::SplitSink;
use futures::{SinkExt, StreamExt};
use labrinth::common::networking::message::{
ClientToServerMessage, ServerToClientMessage,
};
use labrinth::common::users::{UserId, UserStatus};
use reqwest::header::HeaderValue;
use reqwest::Method;
use serde::{Deserialize, Serialize};

View File

@@ -1,6 +1,6 @@
use crate::state::friends::{TunnelSockets, WriteSocket};
use crate::state::FriendsSocket;
use labrinth::common::networking::message::ClientToServerMessage;
use ariadne::networking::message::ClientToServerMessage;
use std::net::SocketAddr;
use std::sync::Arc;
use tokio::io::AsyncWriteExt;

View File

@@ -0,0 +1,15 @@
[package]
name = "ariadne"
version = "0.1.0"
edition = "2021"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
thiserror = "1.0"
uuid = { version = "1.2.2", features = ["v4", "fast-rng", "serde"] }
serde_bytes = "0.11"
rand = "0.8.5"
either = "1.13"
chrono = { version = "0.4.26", features = ["serde"] }
serde_cbor = "0.11"

216
packages/ariadne/src/ids.rs Normal file
View File

@@ -0,0 +1,216 @@
pub use super::users::UserId;
use thiserror::Error;
/// Generates a random 64 bit integer that is exactly `n` characters
/// long when encoded as base62.
///
/// Uses `rand`'s thread rng on every call.
///
/// # Panics
///
/// This method panics if `n` is 0 or greater than 11, since a `u64`
/// can only represent up to 11 character base62 strings
#[inline]
pub fn random_base62(n: usize) -> u64 {
random_base62_rng(&mut rand::thread_rng(), n)
}
/// Generates a random 64 bit integer that is exactly `n` characters
/// long when encoded as base62, using the given rng.
///
/// # Panics
///
/// This method panics if `n` is 0 or greater than 11, since a `u64`
/// can only represent up to 11 character base62 strings
pub fn random_base62_rng<R: rand::RngCore>(rng: &mut R, n: usize) -> u64 {
random_base62_rng_range(rng, n, n)
}
pub fn random_base62_rng_range<R: rand::RngCore>(
rng: &mut R,
n_min: usize,
n_max: usize,
) -> u64 {
use rand::Rng;
assert!(n_min > 0 && n_max <= 11 && n_min <= n_max);
// gen_range is [low, high): max value is `MULTIPLES[n] - 1`,
// which is n characters long when encoded
rng.gen_range(MULTIPLES[n_min - 1]..MULTIPLES[n_max])
}
const MULTIPLES: [u64; 12] = [
1,
62,
62 * 62,
62 * 62 * 62,
62 * 62 * 62 * 62,
62 * 62 * 62 * 62 * 62,
62 * 62 * 62 * 62 * 62 * 62,
62 * 62 * 62 * 62 * 62 * 62 * 62,
62 * 62 * 62 * 62 * 62 * 62 * 62 * 62,
62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62,
62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62,
u64::MAX,
];
/// An ID encoded as base62 for use in the API.
///
/// All ids should be random and encode to 8-10 character base62 strings,
/// to avoid enumeration and other attacks.
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct Base62Id(pub u64);
/// An error decoding a number from base62.
#[derive(Error, Debug)]
pub enum DecodingError {
/// Encountered a non-base62 character in a base62 string
#[error("Invalid character {0:?} in base62 encoding")]
InvalidBase62(char),
/// Encountered integer overflow when decoding a base62 id.
#[error("Base62 decoding overflowed")]
Overflow,
}
#[macro_export]
macro_rules! from_base62id {
($($struct:ty, $con:expr;)+) => {
$(
impl From<Base62Id> for $struct {
fn from(id: Base62Id) -> $struct {
$con(id.0)
}
}
impl From<$struct> for Base62Id {
fn from(id: $struct) -> Base62Id {
Base62Id(id.0)
}
}
)+
};
}
#[macro_export]
macro_rules! impl_base62_display {
($struct:ty) => {
impl std::fmt::Display for $struct {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&$crate::ids::base62_impl::to_base62(self.0))
}
}
};
}
impl_base62_display!(Base62Id);
#[macro_export]
macro_rules! base62_id_impl {
($struct:ty, $cons:expr) => {
$crate::ids::from_base62id!($struct, $cons;);
$crate::ids::impl_base62_display!($struct);
}
}
base62_id_impl!(UserId, UserId);
pub use {base62_id_impl, from_base62id, impl_base62_display};
pub mod base62_impl {
use serde::de::{self, Deserializer, Visitor};
use serde::ser::Serializer;
use serde::{Deserialize, Serialize};
use super::{Base62Id, DecodingError};
impl<'de> Deserialize<'de> for Base62Id {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct Base62Visitor;
impl Visitor<'_> for Base62Visitor {
type Value = Base62Id;
fn expecting(
&self,
formatter: &mut std::fmt::Formatter,
) -> std::fmt::Result {
formatter.write_str("a base62 string id")
}
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Base62Id(v))
}
fn visit_str<E>(self, string: &str) -> Result<Base62Id, E>
where
E: de::Error,
{
parse_base62(string).map(Base62Id).map_err(E::custom)
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_str(Base62Visitor)
} else {
deserializer.deserialize_u64(Base62Visitor)
}
}
}
impl Serialize for Base62Id {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
if serializer.is_human_readable() {
serializer.serialize_str(&to_base62(self.0))
} else {
serializer.serialize_u64(self.0)
}
}
}
const BASE62_CHARS: [u8; 62] =
*b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
pub fn to_base62(mut num: u64) -> String {
let length = (num as f64).log(62.0).ceil() as usize;
let mut output = String::with_capacity(length);
while num > 0 {
// Could be done more efficiently, but requires byte
// manipulation of strings & Vec<u8> -> String conversion
output.insert(0, BASE62_CHARS[(num % 62) as usize] as char);
num /= 62;
}
output
}
pub fn parse_base62(string: &str) -> Result<u64, DecodingError> {
let mut num: u64 = 0;
for c in string.chars() {
let next_digit;
if c.is_ascii_digit() {
next_digit = (c as u8 - b'0') as u64;
} else if c.is_ascii_uppercase() {
next_digit = 10 + (c as u8 - b'A') as u64;
} else if c.is_ascii_lowercase() {
next_digit = 36 + (c as u8 - b'a') as u64;
} else {
return Err(DecodingError::InvalidBase62(c));
}
// We don't want this panicking or wrapping on integer overflow
if let Some(n) =
num.checked_mul(62).and_then(|n| n.checked_add(next_digit))
{
num = n;
} else {
return Err(DecodingError::Overflow);
}
}
Ok(num)
}
}

View File

@@ -0,0 +1,3 @@
pub mod ids;
pub mod networking;
pub mod users;

View File

@@ -0,0 +1,3 @@
pub mod ids;
pub mod networking;
pub mod users;

View File

@@ -0,0 +1,65 @@
use crate::ids::UserId;
use crate::users::UserStatus;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ClientToServerMessage {
StatusUpdate {
profile_name: Option<String>,
},
SocketListen {
socket: Uuid,
},
SocketClose {
socket: Uuid,
},
SocketSend {
socket: Uuid,
#[serde(with = "serde_bytes")]
data: Vec<u8>,
},
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum ServerToClientMessage {
StatusUpdate {
status: UserStatus,
},
UserOffline {
id: UserId,
},
FriendStatuses {
statuses: Vec<UserStatus>,
},
FriendRequest {
from: UserId,
},
FriendRequestRejected {
from: UserId,
},
FriendSocketListening {
user: UserId,
socket: Uuid,
},
FriendSocketStoppedListening {
user: UserId,
},
SocketConnected {
to_socket: Uuid,
new_socket: Uuid,
},
SocketClosed {
socket: Uuid,
},
SocketData {
socket: Uuid,
#[serde(with = "serde_bytes")]
data: Vec<u8>,
},
}

View File

@@ -0,0 +1,2 @@
pub mod message;
pub mod serialization;

View File

@@ -0,0 +1,56 @@
use super::message::{ClientToServerMessage, ServerToClientMessage};
use either::Either;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum SerializationError {
#[error("Failed to (de)serialize message: {0}")]
SerializationFailed(#[from] serde_json::Error),
#[error("Failed to (de)serialize binary message: {0}")]
BinarySerializationFailed(#[from] serde_cbor::Error),
}
macro_rules! message_serialization {
($message_enum:ty $(,$binary_pattern:pat_param)* $(,)?) => {
impl $message_enum {
pub fn is_binary(&self) -> bool {
match self {
$(
$binary_pattern => true,
)*
_ => false,
}
}
pub fn serialize(
&self,
) -> Result<Either<String, Vec<u8>>, SerializationError> {
Ok(match self {
$(
$binary_pattern => Either::Right(serde_cbor::to_vec(self)?),
)*
_ => Either::Left(serde_json::to_string(self)?),
})
}
pub fn deserialize(
msg: Either<&str, &[u8]>,
) -> Result<Self, SerializationError> {
Ok(match msg {
Either::Left(text) => serde_json::from_str(&text)?,
Either::Right(bytes) => serde_cbor::from_slice(&bytes)?,
})
}
}
};
}
message_serialization!(
ClientToServerMessage,
ClientToServerMessage::SocketSend { .. },
);
message_serialization!(
ServerToClientMessage,
ServerToClientMessage::SocketData { .. },
);

View File

@@ -0,0 +1,15 @@
use super::ids::Base62Id;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)]
#[serde(from = "Base62Id")]
#[serde(into = "Base62Id")]
pub struct UserId(pub u64);
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserStatus {
pub user_id: UserId,
pub profile_name: Option<String>,
pub last_update: DateTime<Utc>,
}