refactor: Ruma upstreaming, half-baked edition

Co-authored-by: Jade Ellis <jade@ellis.link>
This commit is contained in:
Ginger
2026-03-29 12:25:42 -04:00
parent 1cc9dbf2a4
commit 204bc1367e
141 changed files with 2715 additions and 2279 deletions
+4 -8
View File
@@ -57,10 +57,6 @@ pub enum Error {
#[error(transparent)]
Json(#[from] serde_json::Error),
#[error(transparent)]
JsParseInt(#[from] ruma::JsParseIntError), // js_int re-export
#[error(transparent)]
JsTryFromInt(#[from] ruma::JsTryFromIntError), // js_int re-export
#[error(transparent)]
Path(#[from] axum::extract::rejection::PathRejection),
#[error("Mutex poisoned: {0}")]
Poison(Cow<'static, str>),
@@ -90,7 +86,7 @@ pub enum Error {
// ruma/conduwuit
#[error("Arithmetic operation failed: {0}")]
Arithmetic(Cow<'static, str>),
#[error("{0}: {1}")]
#[error("{0:?}: {1}")]
BadRequest(ruma::api::client::error::ErrorKind, &'static str), //TODO: remove
#[error("{0}")]
BadServerResponse(Cow<'static, str>),
@@ -120,7 +116,7 @@ pub enum Error {
Mxid(#[from] ruma::IdParseError),
#[error("from {0}: {1}")]
Redaction(ruma::OwnedServerName, ruma::canonical_json::RedactionError),
#[error("{0}: {1}")]
#[error("{0:?}: {1}")]
Request(ruma::api::client::error::ErrorKind, Cow<'static, str>, http::StatusCode),
#[error(transparent)]
Ruma(#[from] ruma::api::client::error::Error),
@@ -167,13 +163,13 @@ impl Error {
/// Returns the Matrix error code / error kind
#[inline]
pub fn kind(&self) -> ruma::api::client::error::ErrorKind {
use ruma::api::client::error::ErrorKind::{FeatureDisabled, Unknown};
use ruma::api::client::error::ErrorKind::{Unknown, Unrecognized};
match self {
| Self::Federation(_, error) | Self::Ruma(error) =>
response::ruma_error_kind(error).clone(),
| Self::BadRequest(kind, ..) | Self::Request(kind, ..) => kind.clone(),
| Self::FeatureDisabled(..) => FeatureDisabled,
| Self::FeatureDisabled(..) => Unrecognized,
| _ => Unknown,
}
}
+5 -12
View File
@@ -4,7 +4,7 @@ use http_body_util::Full;
use ruma::api::{
OutgoingResponse,
client::{
error::{ErrorBody, ErrorKind},
error::{ErrorBody, ErrorKind, StandardErrorBody},
uiaa::UiaaResponse,
},
};
@@ -51,15 +51,9 @@ impl From<Error> for UiaaResponse {
return Self::AuthResponse(uiaainfo);
}
let body = ErrorBody::Standard {
kind: error.kind(),
message: error.message(),
};
let body = ErrorBody::Standard(StandardErrorBody::new(error.kind(), error.message()));
Self::MatrixError(ruma::api::client::error::Error {
status_code: error.status_code(),
body,
})
Self::MatrixError(ruma::api::client::error::Error::new(error.status_code(), body))
}
}
@@ -85,7 +79,7 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
| Unrecognized => StatusCode::METHOD_NOT_ALLOWED,
// 404
| NotFound | NotImplemented | FeatureDisabled | SenderIgnored { .. } =>
| NotFound =>
StatusCode::NOT_FOUND,
// 403
@@ -93,7 +87,6 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
| ThreepidAuthFailed
| UserDeactivated
| ThreepidDenied
| InviteBlocked
| WrongRoomKeysVersion { .. }
| UserSuspended
| Forbidden { .. } => StatusCode::FORBIDDEN,
@@ -108,7 +101,7 @@ pub(super) fn bad_request_code(kind: &ErrorKind) -> StatusCode {
}
pub(super) fn ruma_error_message(error: &ruma::api::client::error::Error) -> String {
if let ErrorBody::Standard { message, .. } = &error.body {
if let ErrorBody::Standard(StandardErrorBody { message, .. }) = &error.body {
return message.clone();
}
+1 -1
View File
@@ -2,7 +2,7 @@
use std::iter::once;
use ruma::{RoomVersionId, api::client::discovery::get_capabilities::RoomVersionStability};
use ruma::{RoomVersionId, api::client::discovery::get_capabilities::v3::RoomVersionStability};
use crate::{at, is_equal_to};
+1 -1
View File
@@ -67,7 +67,7 @@ fn matches_sender<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
}
fn matches_type<E: Event>(event: &E, filter: &RoomEventFilter) -> bool {
let kind = event.kind().to_cow_str();
let kind = event.kind().to_string();
if filter.not_types.iter().any(is_equal_to!(&kind)) {
return false;
+5 -2
View File
@@ -1,7 +1,7 @@
use ruma::{CanonicalJsonObject, OwnedEventId, RoomVersionId};
use serde_json::value::RawValue as RawJsonValue;
use crate::{Result, err};
use crate::{Err, Result, err};
/// Generates a correct eventId for the incoming pdu.
///
@@ -24,7 +24,10 @@ pub fn gen_event_id(
value: &CanonicalJsonObject,
room_version_id: &RoomVersionId,
) -> Result<OwnedEventId> {
let reference_hash = ruma::signatures::reference_hash(value, room_version_id)?;
let Some(rules) = room_version_id.rules() else {
return Err!("Cannot generate event ID for unknown room version {room_version_id}")
};
let reference_hash = ruma::signatures::reference_hash(value, &rules)?;
let event_id: OwnedEventId = format!("${reference_hash}").try_into()?;
Ok(event_id)
+2 -2
View File
@@ -21,12 +21,12 @@ impl TypeExt for &StateEventType {
impl TypeExt for TimelineEventType {
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
(self.into(), state_key.into())
(self.to_string().into(), state_key.into())
}
}
impl TypeExt for &TimelineEventType {
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
(self.clone().into(), state_key.into())
(self.clone().to_string().into(), state_key.into())
}
}
+2 -1
View File
@@ -4,8 +4,9 @@ pub mod event;
pub mod pdu;
pub mod state_key;
pub mod state_res;
pub mod versions;
pub use event::{Event, TypeExt as EventTypeExt};
pub use pdu::{Pdu, PduBuilder, PduCount, PduEvent, PduId, RawPduId, ShortId};
pub use state_key::StateKey;
pub use state_res::{RoomVersion, StateMap, TypeStateKey};
pub use state_res::{StateMap, TypeStateKey};
+3 -3
View File
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
use ruma::{
MilliSecondsSinceUnixEpoch, OwnedEventId,
events::{EventContent, MessageLikeEventType, StateEventType, TimelineEventType},
events::{MessageLikeEventContent, StateEventContent, TimelineEventType},
};
use serde::Deserialize;
use serde_json::value::{RawValue as RawJsonValue, to_raw_value};
@@ -33,7 +33,7 @@ type Unsigned = BTreeMap<String, serde_json::Value>;
impl Builder {
pub fn state<S, T>(state_key: S, content: &T) -> Self
where
T: EventContent<EventType = StateEventType>,
T: StateEventContent,
S: Into<StateKey>,
{
Self {
@@ -47,7 +47,7 @@ impl Builder {
pub fn timeline<T>(content: &T) -> Self
where
T: EventContent<EventType = MessageLikeEventType>,
T: MessageLikeEventContent,
{
Self {
event_type: content.event_type().into(),
+6 -2
View File
@@ -1,16 +1,20 @@
use ruma::{RoomVersionId, canonical_json::redact_content_in_place};
use serde_json::{Value as JsonValue, json, value::to_raw_value};
use crate::{Error, Result, err, implement};
use crate::{Err, Error, Result, err, implement};
#[implement(super::Pdu)]
pub fn redact(&mut self, room_version_id: &RoomVersionId, reason: JsonValue) -> Result {
let Some(rules) = room_version_id.rules() else {
return Err!("Cannot redact event for unknown room version {room_version_id}")
};
self.unsigned = None;
let mut content = serde_json::from_str(self.content.get())
.map_err(|e| err!(Request(BadJson("Failed to deserialize content into type: {e}"))))?;
redact_content_in_place(&mut content, room_version_id, self.kind.to_string())
redact_content_in_place(&mut content, &rules.redaction, self.kind.to_string())
.map_err(|e| Error::Redaction(self.sender.server_name().to_owned(), e))?;
let reason = serde_json::to_value(reason).expect("Failed to preserialize reason");
+3 -3
View File
@@ -318,7 +318,7 @@ impl TestStore<Pdu> {
.iter()
.map(|ev| {
(
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
ev.event_id().to_owned(),
)
})
@@ -328,7 +328,7 @@ impl TestStore<Pdu> {
.iter()
.map(|ev| {
(
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
ev.event_id().to_owned(),
)
})
@@ -338,7 +338,7 @@ impl TestStore<Pdu> {
.iter()
.map(|ev| {
(
(ev.event_type().clone().into(), ev.state_key().unwrap().into()),
(ev.event_type().to_string().into(), ev.state_key().unwrap().into()),
ev.event_id().to_owned(),
)
})
+56 -107
View File
@@ -5,16 +5,12 @@ use futures::{
future::{OptionFuture, join, join3},
};
use ruma::{
Int, OwnedUserId, RoomVersionId, UserId,
events::room::{
Int, OwnedUserId, RoomVersionId, UserId, events::room::{
create::RoomCreateEventContent,
join_rules::{JoinRule, RoomJoinRulesEventContent},
member::{MembershipState, ThirdPartyInvite},
power_levels::RoomPowerLevelsEventContent,
third_party_invite::RoomThirdPartyInviteEventContent,
},
int,
serde::{Base64, Raw},
}, int, room_version_rules::{RoomIdFormatVersion, RoomVersionRules}, serde::Raw,
};
use serde::{
Deserialize,
@@ -28,7 +24,6 @@ use super::{
deserialize_power_levels, deserialize_power_levels_content_fields,
deserialize_power_levels_content_invite, deserialize_power_levels_content_redact,
},
room_version::RoomVersion,
};
use crate::{debug, error, trace, warn};
@@ -65,13 +60,13 @@ pub fn auth_types_for_event(
sender: &UserId,
state_key: Option<&str>,
content: &RawJsonValue,
room_version: &RoomVersion,
room_version: &RoomVersionRules,
) -> serde_json::Result<Vec<(StateEventType, StateKey)>> {
if kind == &TimelineEventType::RoomCreate {
return Ok(vec![]);
}
let mut auth_types = if room_version.room_ids_as_hashes {
let mut auth_types = if room_version.room_id_format == RoomIdFormatVersion::V2 {
vec![
(StateEventType::RoomPowerLevels, StateKey::new()),
(StateEventType::RoomMember, sender.as_str().into()),
@@ -120,15 +115,16 @@ pub fn auth_types_for_event(
auth_types.push(key);
}
if membership == MembershipState::Invite {
if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
let key =
(StateEventType::RoomThirdPartyInvite, t_id.signed.token.into());
if !auth_types.contains(&key) {
auth_types.push(key);
}
}
}
// TODO: restore this once 3pid support isn't broken
// if membership == MembershipState::Invite {
// if let Some(Ok(t_id)) = content.third_party_invite.map(|t| t.deserialize()) {
// let key =
// (StateEventType::RoomThirdPartyInvite, t_id.signed.token.into());
// if !auth_types.contains(&key) {
// auth_types.push(key);
// }
// }
// }
}
}
}
@@ -155,7 +151,7 @@ pub fn auth_types_for_event(
)]
#[allow(clippy::suspicious_operation_groupings)]
pub async fn auth_check<E, F, Fut>(
room_version: &RoomVersion,
room_version: &RoomVersionRules,
incoming_event: &E,
current_third_party_invite: Option<&E>,
fetch_state: F,
@@ -218,13 +214,13 @@ where
return Ok(false);
}
if room_version.room_ids_as_hashes && incoming_event.room_id().is_some() {
if room_version.room_id_format == RoomIdFormatVersion::V2 && incoming_event.room_id().is_some() {
warn!("room create event incorrectly claims to have a room ID when it should not");
return Ok(false);
}
if !room_version.use_room_create_sender
&& !room_version.explicitly_privilege_room_creators
if !room_version.authorization.use_room_create_sender
&& !room_version.authorization.explicitly_privilege_room_creators
{
// If content has no creator field, reject
if content.creator.is_none() {
@@ -305,10 +301,10 @@ where
let claims_create_event = incoming_event
.auth_events()
.any(|id| id == room_create_event.event_id());
if room_version.room_ids_as_hashes && claims_create_event {
if room_version.room_id_format == RoomIdFormatVersion::V2 && claims_create_event {
warn!("event incorrectly references m.room.create event in auth events");
return Ok(false);
} else if !room_version.room_ids_as_hashes && !claims_create_event {
} else if !(room_version.room_id_format == RoomIdFormatVersion::V2) && !claims_create_event {
// If the create event is not referenced in the event's auth events, and this is
// a v11 room, reject
warn!(
@@ -332,7 +328,7 @@ where
// If the create event content has the field m.federate set to false and the
// sender domain of the event does not match the sender domain of the create
// event, reject.
if !room_version.room_ids_as_hashes
if !(room_version.room_id_format == RoomIdFormatVersion::V2)
&& !room_create_content.federate
&& room_create_event.sender().server_name() != incoming_event.sender().server_name()
{
@@ -345,7 +341,7 @@ where
}
// Only in some room versions 6 and below
if room_version.special_case_aliases_auth {
if room_version.authorization.special_case_room_aliases {
// 4. If type is m.room.aliases
if *incoming_event.event_type() == TimelineEventType::RoomAliases {
debug!("starting m.room.aliases check");
@@ -486,7 +482,7 @@ where
},
| _ => {
// If no power level event found the creator gets 100 everyone else gets 0
let is_creator = if room_version.use_room_create_sender {
let is_creator = if room_version.authorization.use_room_create_sender {
room_create_event.sender() == sender
} else {
#[allow(deprecated)]
@@ -497,7 +493,7 @@ where
if is_creator { int!(100) } else { int!(0) }
},
};
if room_version.explicitly_privilege_room_creators {
if room_version.authorization.explicitly_privilege_room_creators {
// If the user sent the create event, or is listed in additional_creators, just
// give them Int::MAX
if sender == room_create_event.sender()
@@ -559,7 +555,7 @@ where
if *incoming_event.event_type() == TimelineEventType::RoomPowerLevels {
debug!("starting m.room.power_levels check");
let mut creators = BTreeSet::new();
if room_version.explicitly_privilege_room_creators {
if room_version.authorization.explicitly_privilege_room_creators {
creators.insert(create_event.sender().to_owned());
for creator in room_create_content.additional_creators.iter().flatten() {
creators.insert(creator.deserialize()?);
@@ -593,7 +589,7 @@ where
// the sender of the redaction has the appropriate permissions per the
// power levels.
if room_version.extra_redaction_checks
if room_version.authorization.special_case_room_redaction
&& *incoming_event.event_type() == TimelineEventType::RoomRedaction
{
let redact_level = match power_levels_event {
@@ -618,7 +614,7 @@ where
}
fn is_creator<EV>(
v: &RoomVersion,
v: &RoomVersionRules,
c: &BTreeSet<OwnedUserId>,
ce: &EV,
user_id: &UserId,
@@ -627,9 +623,9 @@ fn is_creator<EV>(
where
EV: Event + Send + Sync,
{
if v.explicitly_privilege_room_creators {
if v.authorization.explicitly_privilege_room_creators {
c.contains(user_id)
} else if v.use_room_create_sender && !have_pls {
} else if v.authorization.use_room_create_sender && !have_pls {
ce.sender() == user_id
} else if !have_pls {
#[allow(deprecated)]
@@ -659,7 +655,7 @@ where
#[allow(clippy::too_many_arguments)]
#[allow(clippy::cognitive_complexity)]
fn valid_membership_change<E>(
room_version: &RoomVersion,
room_version: &RoomVersionRules,
target_user: &UserId,
target_user_membership_event: Option<&E>,
sender: &UserId,
@@ -700,7 +696,7 @@ where
let power_levels: RoomPowerLevelsEventContent = match &power_levels_event {
| Some(ev) => from_json_str(ev.content().get())?,
| None => RoomPowerLevelsEventContent::default(),
| None => RoomPowerLevelsEventContent::new(&room_version.authorization),
};
let mut sender_power = power_levels
@@ -714,7 +710,7 @@ where
let mut creators = BTreeSet::new();
creators.insert(create_room.sender().to_owned());
if room_version.explicitly_privilege_room_creators {
if room_version.authorization.explicitly_privilege_room_creators {
// Explicitly privilege room creators
// If the sender sent the create event, or in additional_creators, give them
// Int::MAX. Same case for target.
@@ -865,7 +861,7 @@ where
trace!(sender=%sender, "sender is invited to room, allowing join");
true
},
| JoinRule::Knock if !room_version.allow_knocking => {
| JoinRule::Knock if !room_version.authorization.knocking => {
warn!("Join rule is knock but room version does not allow knocking");
false
},
@@ -882,7 +878,7 @@ where
trace!(sender=%sender, "sender is invited or already joined to room, allowing join");
true
},
| JoinRule::KnockRestricted(_) if !room_version.knock_restricted_join_rule =>
| JoinRule::KnockRestricted(_) if !room_version.authorization.knock_restricted_join_rule =>
{
warn!(
"Join rule is knock_restricted but room version does not support it"
@@ -1127,7 +1123,7 @@ where
}
allow
},
| MembershipState::Knock if room_version.allow_knocking => {
| MembershipState::Knock if room_version.authorization.knocking => {
// 1. If the `join_rule` is anything other than `knock` or `knock_restricted`,
// reject.
if !matches!(join_rules, JoinRule::KnockRestricted(_) | JoinRule::Knock) {
@@ -1136,7 +1132,7 @@ where
);
false
} else if matches!(join_rules, JoinRule::KnockRestricted(_))
&& !room_version.knock_restricted_join_rule
&& !room_version.authorization.knock_restricted_join_rule
{
// 2. If the `join_rule` is `knock_restricted`, but the room does not support
// `knock_restricted`, reject.
@@ -1226,7 +1222,7 @@ fn can_send_event(event: &impl Event, ple: Option<&impl Event>, user_level: Int)
/// Confirm that the event sender has the required power levels.
#[allow(clippy::cognitive_complexity)]
fn check_power_levels(
room_version: &RoomVersion,
room_version: &RoomVersionRules,
power_event: &impl Event,
previous_power_event: Option<&impl Event>,
user_level: Int,
@@ -1382,7 +1378,7 @@ fn check_power_levels(
}
// Notifications, currently there is only @room
if room_version.limit_notifications_power_levels {
if room_version.authorization.limit_notifications_power_levels {
let old_level = old_state.notifications.room;
let new_level = new_state.notifications.room;
if old_level != new_level {
@@ -1451,7 +1447,7 @@ fn get_deserialize_levels(
/// Does the event redacting come from a user with enough power to redact the
/// given event.
fn check_redaction(
_room_version: &RoomVersion,
_room_version: &RoomVersionRules,
redaction_event: &impl Event,
user_level: Int,
redact_level: Int,
@@ -1501,79 +1497,32 @@ fn get_send_level(
}
fn verify_third_party_invite(
target_user: Option<&UserId>,
sender: &UserId,
tp_id: &ThirdPartyInvite,
current_third_party_invite: Option<&impl Event>,
_target_user: Option<&UserId>,
_sender: &UserId,
_tp_id: &ThirdPartyInvite,
_current_third_party_invite: Option<&impl Event>,
) -> bool {
// 1. Check for user being banned happens before this is called
// checking for mxid and token keys is done by ruma when deserializing
// The state key must match the invitee
if target_user != Some(&tp_id.signed.mxid) {
return false;
}
// If there is no m.room.third_party_invite event in the current room state with
// state_key matching token, reject
#[allow(clippy::manual_let_else)]
let current_tpid = match current_third_party_invite {
| Some(id) => id,
| None => return false,
};
if current_tpid.state_key() != Some(&tp_id.signed.token) {
return false;
}
if sender != current_tpid.sender() {
return false;
}
// If any signature in signed matches any public key in the
// m.room.third_party_invite event, allow
#[allow(clippy::manual_let_else)]
let tpid_ev =
match from_json_str::<RoomThirdPartyInviteEventContent>(current_tpid.content().get()) {
| Ok(ev) => ev,
| Err(_) => return false,
};
#[allow(clippy::manual_let_else)]
let decoded_invite_token = match Base64::parse(&tp_id.signed.token) {
| Ok(tok) => tok,
// FIXME: Log a warning?
| Err(_) => return false,
};
// A list of public keys in the public_keys field
for key in tpid_ev.public_keys.unwrap_or_default() {
if key.public_key == decoded_invite_token {
return true;
}
}
// A single public key in the public_key field
tpid_ev.public_key == decoded_invite_token
// TODO: implement proper verification here
true
}
#[cfg(test)]
mod tests {
use ruma::events::{
use ruma::{events::{
StateEventType, TimelineEventType,
room::{
join_rules::{
AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent, RoomMembership,
AllowRule, JoinRule, Restricted, RoomJoinRulesEventContent,
},
member::{MembershipState, RoomMemberEventContent},
},
};
}, room::RoomMembership, room_version_rules::RoomVersionRules};
use serde_json::value::to_raw_value as to_raw_json_value;
use crate::{
matrix::{Event, EventTypeExt, Pdu as PduEvent},
state_res::{
RoomVersion, StateMap,
StateMap,
event_auth::valid_membership_change,
test_utils::{
INITIAL_EVENTS, INITIAL_EVENTS_CREATE_ROOM, alice, charlie, ella, event_id,
@@ -1610,7 +1559,7 @@ mod tests {
assert!(
valid_membership_change(
&RoomVersion::V6,
&RoomVersionRules::V6,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
@@ -1655,7 +1604,7 @@ mod tests {
assert!(
!valid_membership_change(
&RoomVersion::V6,
&RoomVersionRules::V6,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
@@ -1700,7 +1649,7 @@ mod tests {
assert!(
valid_membership_change(
&RoomVersion::V6,
&RoomVersionRules::V6,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
@@ -1745,7 +1694,7 @@ mod tests {
assert!(
!valid_membership_change(
&RoomVersion::V6,
&RoomVersionRules::V6,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
@@ -1807,7 +1756,7 @@ mod tests {
assert!(
valid_membership_change(
&RoomVersion::V9,
&RoomVersionRules::V9,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
@@ -1825,7 +1774,7 @@ mod tests {
assert!(
!valid_membership_change(
&RoomVersion::V9,
&RoomVersionRules::V9,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
@@ -1879,7 +1828,7 @@ mod tests {
assert!(
valid_membership_change(
&RoomVersion::V7,
&RoomVersionRules::V7,
target_user,
fetch_state(StateEventType::RoomMember, target_user.as_str().into()).as_ref(),
sender,
+15 -28
View File
@@ -3,7 +3,7 @@
pub(crate) mod error;
pub mod event_auth;
mod power_levels;
mod room_version;
mod serde_backports;
#[cfg(test)]
mod test_utils;
@@ -20,25 +20,21 @@ use std::{
use futures::{Future, FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, future};
use ruma::{
EventId, Int, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId,
EventId, Int, MilliSecondsSinceUnixEpoch, OwnedEventId,
events::{
StateEventType, TimelineEventType,
room::member::{MembershipState, RoomMemberEventContent},
},
int,
int, room_version_rules::{RoomIdFormatVersion, RoomVersionRules, StateResolutionVersion},
};
use serde_json::from_str as from_json_str;
pub(crate) use self::error::Error;
use self::power_levels::PowerLevelsContentFields;
pub use self::{
event_auth::{auth_check, auth_types_for_event},
room_version::RoomVersion,
};
pub use self::event_auth::{auth_check, auth_types_for_event};
use crate::{
debug, debug_error, err,
matrix::{Event, StateKey},
state_res::room_version::StateResolutionVersion,
trace,
utils::stream::{BroadbandExt, IterStream, ReadyExt, TryBroadbandExt, WidebandExt},
warn,
@@ -77,7 +73,7 @@ type Result<T, E = Error> = crate::Result<T, E>;
//#[tracing::instrument(level event_fetch))]
#[allow(clippy::cognitive_complexity)]
pub async fn resolve<'a, Pdu, Sets, SetIter, Hasher, Fetch, FetchFut, Exists, ExistsFut>(
room_version: &RoomVersionId,
room_version: &RoomVersionRules,
state_sets: Sets,
auth_chain_sets: &'a [HashSet<OwnedEventId, Hasher>],
event_fetch: &Fetch,
@@ -94,11 +90,7 @@ where
Pdu: Event + Clone + Send + Sync,
for<'b> &'b Pdu: Event + Send,
{
use RoomVersionId::*;
let stateres_version = match room_version {
| V2 | V3 | V4 | V5 | V6 | V7 | V8 | V9 | V10 | V11 => StateResolutionVersion::V2,
| _ => StateResolutionVersion::V2_1,
};
let stateres_version = room_version.state_res;
debug!(version = ?stateres_version, "State resolution starting");
// Split non-conflicting and conflicting state
@@ -115,7 +107,7 @@ where
debug!(count = conflicting.len(), "conflicting events");
trace!(map = ?conflicting, "conflicting events");
let (conflicted_state_subgraph, initial_state) =
if stateres_version == StateResolutionVersion::V2_1 {
if let StateResolutionVersion::V2(v2_rules) = stateres_version && v2_rules.consider_conflicted_state_subgraph {
let csg = calculate_conflicted_subgraph(&conflicting, event_fetch)
.await
.ok_or_else(|| {
@@ -166,7 +158,6 @@ where
debug!(count = sorted_control_levels.len(), "power events");
trace!(list = ?sorted_control_levels, "sorted power events");
let room_version = RoomVersion::new(room_version)?;
// Sequentially auth check each control event.
let resolved_control = iterative_auth_check(
&room_version,
@@ -596,7 +587,7 @@ where
/// `event_auth::auth_check` function.
#[tracing::instrument(level = "trace", skip_all)]
async fn iterative_auth_check<'a, E, F, Fut, S>(
room_version: &RoomVersion,
room_version: &RoomVersionRules,
events_to_check: S,
unconflicted_state: StateMap<OwnedEventId>,
fetch_event: &F,
@@ -666,7 +657,7 @@ where
trace!(list = ?auth_types, event_id = event.event_id().as_str(), "auth types for event");
let mut auth_state = StateMap::new();
if room_version.room_ids_as_hashes {
if room_version.room_id_format == RoomIdFormatVersion::V2 {
trace!("room version uses hashed IDs, manually fetching create event");
let create_event_id_raw = event.room_id_or_hash().as_str().replace('!', "$");
let create_event_id = EventId::parse(&create_event_id_raw).map_err(|e| {
@@ -963,7 +954,7 @@ impl EventTypeExt for StateEventType {
impl EventTypeExt for TimelineEventType {
fn with_state_key(self, state_key: impl Into<StateKey>) -> (StateEventType, StateKey) {
(self.into(), state_key.into())
(self.to_string().into(), state_key.into())
}
}
@@ -983,18 +974,15 @@ mod tests {
use maplit::{hashmap, hashset};
use rand::seq::SliceRandom;
use ruma::{
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId,
events::{
MilliSecondsSinceUnixEpoch, OwnedEventId, RoomVersionId, events::{
StateEventType, TimelineEventType,
room::join_rules::{JoinRule, RoomJoinRulesEventContent},
},
int, uint,
}, int, room_version_rules::RoomVersionRules, uint
};
use serde_json::{json, value::to_raw_value as to_raw_json_value};
use super::{
StateMap, is_power_event,
room_version::RoomVersion,
test_utils::{
INITIAL_EVENTS, TestStore, alice, bob, charlie, do_check, ella, event_id,
member_content_ban, member_content_join, room_id, to_init_pdu_event, to_pdu_event,
@@ -1004,7 +992,6 @@ mod tests {
use crate::{
debug,
matrix::{Event, EventTypeExt, Pdu as PduEvent},
state_res::room_version::StateResolutionVersion,
utils::stream::IterStream,
};
@@ -1036,7 +1023,7 @@ mod tests {
.unwrap();
let resolved_power = super::iterative_auth_check(
&RoomVersion::V6,
&RoomVersionRules::V6,
sorted_power_events.iter().map(AsRef::as_ref).stream(),
HashMap::new(), // unconflicted events
&fetcher,
@@ -1437,7 +1424,7 @@ mod tests {
.collect();
let resolved =
match super::resolve(&RoomVersionId::V2, &state_sets, &auth_chain, &fetcher, &exists)
match super::resolve(&RoomVersionRules::V2, &state_sets, &auth_chain, &fetcher, &exists)
.await
{
| Ok(state) => state,
@@ -1550,7 +1537,7 @@ mod tests {
let fetcher = |id: OwnedEventId| ready(ev_map.get(&id).cloned());
let exists = |id: OwnedEventId| ready(ev_map.get(&id).is_some());
let resolved =
match super::resolve(&RoomVersionId::V6, &state_sets, &auth_chain, &fetcher, &exists)
match super::resolve(&RoomVersionRules::V6, &state_sets, &auth_chain, &fetcher, &exists)
.await
{
| Ok(state) => state,
+18 -23
View File
@@ -1,18 +1,13 @@
use std::collections::BTreeMap;
use ruma::{
Int, OwnedUserId, UserId,
events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent},
power_levels::{NotificationPowerLevels, default_power_level},
serde::{
deserialize_v1_powerlevel, vec_deserialize_int_powerlevel_values,
vec_deserialize_v1_powerlevel_values,
},
Int, OwnedUserId, UserId, events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent}, power_levels::{NotificationPowerLevels, default_power_level}, room_version_rules::{AuthorizationRules, RoomVersionRules}, serde::deserialize_v1_powerlevel
};
use super::serde_backports::*;
use serde::Deserialize;
use serde_json::{Error, from_str as from_json_str};
use super::{Result, RoomVersion};
use super::Result;
use crate::error;
#[derive(Deserialize)]
@@ -48,8 +43,8 @@ struct IntRoomPowerLevelsEventContent {
notifications: IntNotificationPowerLevels,
}
impl From<IntRoomPowerLevelsEventContent> for RoomPowerLevelsEventContent {
fn from(int_pl: IntRoomPowerLevelsEventContent) -> Self {
impl IntRoomPowerLevelsEventContent {
fn to_room_power_levels_content(self, auth_rules: &AuthorizationRules) -> RoomPowerLevelsEventContent {
let IntRoomPowerLevelsEventContent {
ban,
events,
@@ -61,9 +56,9 @@ impl From<IntRoomPowerLevelsEventContent> for RoomPowerLevelsEventContent {
users,
users_default,
notifications,
} = int_pl;
} = self;
let mut pl = Self::new();
let mut pl = RoomPowerLevelsEventContent::new(auth_rules);
pl.ban = ban;
pl.events = events;
pl.events_default = events_default;
@@ -101,18 +96,18 @@ impl From<IntNotificationPowerLevels> for NotificationPowerLevels {
#[inline]
pub(crate) fn deserialize_power_levels(
content: &str,
room_version: &RoomVersion,
room_version: &RoomVersionRules,
) -> Option<RoomPowerLevelsEventContent> {
if room_version.integer_power_levels {
deserialize_integer_power_levels(content)
if room_version.authorization.integer_power_levels {
deserialize_integer_power_levels(content, &room_version.authorization)
} else {
deserialize_legacy_power_levels(content)
}
}
fn deserialize_integer_power_levels(content: &str) -> Option<RoomPowerLevelsEventContent> {
fn deserialize_integer_power_levels(content: &str, auth_rules: &AuthorizationRules) -> Option<RoomPowerLevelsEventContent> {
match from_json_str::<IntRoomPowerLevelsEventContent>(content) {
| Ok(content) => Some(content.into()),
| Ok(content) => Some(content.to_room_power_levels_content(auth_rules)),
| Err(_) => {
error!("m.room.power_levels event is not valid with integer values");
None
@@ -174,9 +169,9 @@ impl From<IntPowerLevelsContentFields> for PowerLevelsContentFields {
#[inline]
pub(crate) fn deserialize_power_levels_content_fields(
content: &str,
room_version: &RoomVersion,
room_version: &RoomVersionRules,
) -> Result<PowerLevelsContentFields, Error> {
if room_version.integer_power_levels {
if room_version.authorization.integer_power_levels {
deserialize_integer_power_levels_content_fields(content)
} else {
deserialize_legacy_power_levels_content_fields(content)
@@ -216,9 +211,9 @@ impl From<IntPowerLevelsContentInvite> for PowerLevelsContentInvite {
pub(crate) fn deserialize_power_levels_content_invite(
content: &str,
room_version: &RoomVersion,
room_version: &RoomVersionRules,
) -> Result<PowerLevelsContentInvite, Error> {
if room_version.integer_power_levels {
if room_version.authorization.integer_power_levels {
from_json_str::<IntPowerLevelsContentInvite>(content).map(Into::into)
} else {
from_json_str(content)
@@ -246,9 +241,9 @@ impl From<IntPowerLevelsContentRedact> for PowerLevelsContentRedact {
pub(crate) fn deserialize_power_levels_content_redact(
content: &str,
room_version: &RoomVersion,
room_version: &RoomVersionRules,
) -> Result<PowerLevelsContentRedact, Error> {
if room_version.integer_power_levels {
if room_version.authorization.integer_power_levels {
from_json_str::<IntPowerLevelsContentRedact>(content).map(Into::into)
} else {
from_json_str(content)
-169
View File
@@ -1,169 +0,0 @@
use ruma::RoomVersionId;
use super::{Error, Result};
#[derive(Debug)]
#[allow(clippy::exhaustive_enums)]
pub enum RoomDisposition {
/// A room version that has a stable specification.
Stable,
/// A room version that is not yet fully specified.
Unstable,
}
#[derive(Debug)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum EventFormatVersion {
/// $id:server event id format
V1,
/// MSC1659-style $hash event id format: introduced for room v3
V2,
/// MSC1884-style $hash format: introduced for room v4
V3,
}
#[derive(Debug, PartialEq)]
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub enum StateResolutionVersion {
/// State resolution for rooms at version 1.
V1,
/// State resolution for room at version 2 or later.
V2,
/// State resolution for room at version 12 or later.
V2_1,
}
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
#[allow(clippy::struct_excessive_bools)]
pub struct RoomVersion {
/// The stability of this room.
pub disposition: RoomDisposition,
/// The format of the EventId.
pub event_format: EventFormatVersion,
/// Which state resolution algorithm is used.
pub state_res: StateResolutionVersion,
// FIXME: not sure what this one means?
pub enforce_key_validity: bool,
/// `m.room.aliases` had special auth rules and redaction rules
/// before room version 6.
///
/// before MSC2261/MSC2432,
pub special_case_aliases_auth: bool,
/// Strictly enforce canonical json, do not allow:
/// * Integers outside the range of [-2 ^ 53 + 1, 2 ^ 53 - 1]
/// * Floats
/// * NaN, Infinity, -Infinity
pub strict_canonicaljson: bool,
/// Verify notifications key while checking m.room.power_levels.
///
/// bool: MSC2209: Check 'notifications'
pub limit_notifications_power_levels: bool,
/// Extra rules when verifying redaction events.
pub extra_redaction_checks: bool,
/// Allow knocking in event authentication.
///
/// See [room v7 specification](https://spec.matrix.org/latest/rooms/v7/)
pub allow_knocking: bool,
/// Adds support for the restricted join rule.
///
/// See: [MSC3289](https://github.com/matrix-org/matrix-spec-proposals/pull/3289)
pub restricted_join_rules: bool,
/// Adds support for the knock_restricted join rule.
///
/// See: [MSC3787](https://github.com/matrix-org/matrix-spec-proposals/pull/3787)
pub knock_restricted_join_rule: bool,
/// Enforces integer power levels.
///
/// See: [MSC3667](https://github.com/matrix-org/matrix-spec-proposals/pull/3667)
pub integer_power_levels: bool,
/// Determine the room creator using the `m.room.create` event's `sender`,
/// instead of the event content's `creator` field.
///
/// See: [MSC2175](https://github.com/matrix-org/matrix-spec-proposals/pull/2175)
pub use_room_create_sender: bool,
/// Whether the room creators are considered superusers.
/// A superuser will always have infinite power levels in the room.
///
/// See: [MSC4289](https://github.com/matrix-org/matrix-spec-proposals/pull/4289)
pub explicitly_privilege_room_creators: bool,
/// Whether the room's m.room.create event ID is itself the room ID.
///
/// See: [MSC4291](https://github.com/matrix-org/matrix-spec-proposals/pull/4291)
pub room_ids_as_hashes: bool,
}
impl RoomVersion {
pub const V1: Self = Self {
disposition: RoomDisposition::Stable,
event_format: EventFormatVersion::V1,
state_res: StateResolutionVersion::V1,
enforce_key_validity: false,
special_case_aliases_auth: true,
strict_canonicaljson: false,
limit_notifications_power_levels: false,
extra_redaction_checks: true,
allow_knocking: false,
restricted_join_rules: false,
knock_restricted_join_rule: false,
integer_power_levels: false,
use_room_create_sender: false,
explicitly_privilege_room_creators: false,
room_ids_as_hashes: false,
};
pub const V10: Self = Self {
knock_restricted_join_rule: true,
integer_power_levels: true,
..Self::V9
};
pub const V11: Self = Self {
use_room_create_sender: true,
..Self::V10
};
pub const V12: Self = Self {
explicitly_privilege_room_creators: true,
room_ids_as_hashes: true,
..Self::V11
};
pub const V2: Self = Self {
state_res: StateResolutionVersion::V2,
..Self::V1
};
pub const V3: Self = Self {
event_format: EventFormatVersion::V2,
extra_redaction_checks: false,
..Self::V2
};
pub const V4: Self = Self {
event_format: EventFormatVersion::V3,
..Self::V3
};
pub const V5: Self = Self { enforce_key_validity: true, ..Self::V4 };
pub const V6: Self = Self {
special_case_aliases_auth: false,
strict_canonicaljson: true,
limit_notifications_power_levels: true,
..Self::V5
};
pub const V7: Self = Self { allow_knocking: true, ..Self::V6 };
pub const V8: Self = Self { restricted_join_rules: true, ..Self::V7 };
pub const V9: Self = Self::V8;
pub fn new(version: &RoomVersionId) -> Result<Self> {
Ok(match version {
| RoomVersionId::V1 => Self::V1,
| RoomVersionId::V2 => Self::V2,
| RoomVersionId::V3 => Self::V3,
| RoomVersionId::V4 => Self::V4,
| RoomVersionId::V5 => Self::V5,
| RoomVersionId::V6 => Self::V6,
| RoomVersionId::V7 => Self::V7,
| RoomVersionId::V8 => Self::V8,
| RoomVersionId::V9 => Self::V9,
| RoomVersionId::V10 => Self::V10,
| RoomVersionId::V11 => Self::V11,
| RoomVersionId::V12 => Self::V12,
| ver => return Err(Error::Unsupported(format!("found version `{ver}`"))),
})
}
}
@@ -0,0 +1,120 @@
//! These functions are copied from an old version of Ruma. power_levels.rs uses them to lazily deserialize power level events.
//! Upstream Ruma uses a much more elegant approach in its state resolution code, which we may want
//! to look into at some point.
use std::marker::PhantomData;
use std::fmt;
use serde::{Deserialize, Deserializer, de::{MapAccess, Visitor}};
use ruma::{Int, serde::deserialize_v1_powerlevel};
/// Take a Map with values of either an integer number or a string and deserialize
/// those to integer numbers in a Vec of sorted pairs.
///
/// To be used like this:
/// `#[serde(deserialize_with = "vec_deserialize_v1_powerlevel_values")]`
pub fn vec_deserialize_v1_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Ord,
{
#[repr(transparent)]
struct IntWrap(Int);
impl<'de> Deserialize<'de> for IntWrap {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserialize_v1_powerlevel(deserializer).map(IntWrap)
}
}
struct IntMapVisitor<T> {
_phantom: PhantomData<T>,
}
impl<T> IntMapVisitor<T> {
fn new() -> Self {
Self { _phantom: PhantomData }
}
}
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
where
T: Deserialize<'de> + Ord,
{
type Value = Vec<(T, Int)>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a map with integers or strings as values")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut res = Vec::new();
if let Some(hint) = map.size_hint() {
res.reserve(hint);
}
while let Some((k, IntWrap(v))) = map.next_entry()? {
res.push((k, v));
}
res.sort_unstable();
res.dedup_by(|a, b| a.0 == b.0);
Ok(res)
}
}
de.deserialize_map(IntMapVisitor::new())
}
/// Take a Map with integer values and deserialize those to a Vec of sorted pairs
///
/// To be used like this:
/// `#[serde(deserialize_with = "vec_deserialize_int_powerlevel_values")]`
pub fn vec_deserialize_int_powerlevel_values<'de, D, T>(de: D) -> Result<Vec<(T, Int)>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de> + Ord,
{
struct IntMapVisitor<T> {
_phantom: PhantomData<T>,
}
impl<T> IntMapVisitor<T> {
fn new() -> Self {
Self { _phantom: PhantomData }
}
}
impl<'de, T> Visitor<'de> for IntMapVisitor<T>
where
T: Deserialize<'de> + Ord,
{
type Value = Vec<(T, Int)>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("a map with integers as values")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut res = Vec::new();
if let Some(hint) = map.size_hint() {
res.reserve(hint);
}
while let Some(item) = map.next_entry()? {
res.push(item);
}
res.sort_unstable();
res.dedup_by(|a, b| a.0 == b.0);
Ok(res)
}
}
de.deserialize_map(IntMapVisitor::new())
}
+5 -8
View File
@@ -6,16 +6,13 @@ use std::{
use futures::future::ready;
use ruma::{
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, ServerSignatures,
UserId, event_id,
events::{
EventId, MilliSecondsSinceUnixEpoch, OwnedEventId, RoomId, RoomVersionId, ServerSignatures, UserId, event_id, events::{
TimelineEventType,
room::{
join_rules::{JoinRule, RoomJoinRulesEventContent},
member::{MembershipState, RoomMemberEventContent},
},
},
int, room_id, uint, user_id,
}, int, room_id, room_version_rules::RoomVersionRules, uint, user_id
};
use serde_json::{
json,
@@ -24,7 +21,7 @@ use serde_json::{
use super::auth_types_for_event;
use crate::{
Result, RoomVersion, info,
Result, info,
matrix::{Event, EventTypeExt, Pdu, StateMap, pdu::EventHash},
};
@@ -134,7 +131,7 @@ pub(crate) async fn do_check(
let fetch = |id: OwnedEventId| ready(event_map.get(&id).cloned());
let exists = |id: OwnedEventId| ready(event_map.get(&id).is_some());
let resolved =
super::resolve(&RoomVersionId::V6, state_sets, &auth_chain_sets, &fetch, &exists)
super::resolve(&RoomVersionRules::V6, state_sets, &auth_chain_sets, &fetch, &exists)
.await;
match resolved {
@@ -154,7 +151,7 @@ pub(crate) async fn do_check(
fake_event.sender(),
fake_event.state_key(),
fake_event.content(),
&RoomVersion::V6,
&RoomVersionRules::V6,
)
.unwrap();
+44
View File
@@ -0,0 +1,44 @@
use std::collections::BTreeMap;
pub fn versions() -> Vec<String> {
vec![
"r0.0.1".to_owned(),
"r0.1.0".to_owned(),
"r0.2.0".to_owned(),
"r0.3.0".to_owned(),
"r0.4.0".to_owned(),
"r0.5.0".to_owned(),
"r0.6.0".to_owned(),
"r0.6.1".to_owned(),
"v1.1".to_owned(),
"v1.2".to_owned(),
"v1.3".to_owned(),
"v1.4".to_owned(),
"v1.5".to_owned(),
"v1.8".to_owned(),
"v1.11".to_owned(),
"v1.12".to_owned(),
"v1.13".to_owned(),
"v1.14".to_owned(),
]
}
pub fn unstable_features() -> BTreeMap<String, bool> {
BTreeMap::from_iter([
("org.matrix.e2e_cross_signing".to_owned(), true),
("org.matrix.msc2285.stable".to_owned(), true), /* private read receipts (https://github.com/matrix-org/matrix-spec-proposals/pull/2285) */
("uk.half-shot.msc2666.query_mutual_rooms".to_owned(), true), /* query mutual rooms (https://github.com/matrix-org/matrix-spec-proposals/pull/2666) */
("org.matrix.msc2836".to_owned(), true), /* threading/threads (https://github.com/matrix-org/matrix-spec-proposals/pull/2836) */
("org.matrix.msc2946".to_owned(), true), /* spaces/hierarchy summaries (https://github.com/matrix-org/matrix-spec-proposals/pull/2946) */
("org.matrix.msc3026.busy_presence".to_owned(), true), /* busy presence status (https://github.com/matrix-org/matrix-spec-proposals/pull/3026) */
("org.matrix.msc3827".to_owned(), true), /* filtering of /publicRooms by room type (https://github.com/matrix-org/matrix-spec-proposals/pull/3827) */
("org.matrix.msc3952_intentional_mentions".to_owned(), true), /* intentional mentions (https://github.com/matrix-org/matrix-spec-proposals/pull/3952) */
("org.matrix.msc3916.stable".to_owned(), true), /* authenticated media (https://github.com/matrix-org/matrix-spec-proposals/pull/3916) */
("org.matrix.msc4180".to_owned(), true), /* stable flag for 3916 (https://github.com/matrix-org/matrix-spec-proposals/pull/4180) */
("uk.tcpip.msc4133".to_owned(), true), /* Extending User Profile API with Key:Value Pairs (https://github.com/matrix-org/matrix-spec-proposals/pull/4133) */
("us.cloke.msc4175".to_owned(), true), /* Profile field for user time zone (https://github.com/matrix-org/matrix-spec-proposals/pull/4175) */
("org.matrix.simplified_msc3575".to_owned(), true), /* Simplified Sliding sync (https://github.com/matrix-org/matrix-spec-proposals/pull/4186) */
("uk.timedout.msc4323".to_owned(), true), /* agnostic suspend (https://github.com/matrix-org/matrix-spec-proposals/pull/4323) */
("org.matrix.msc4155".to_owned(), true), /* invite filtering (https://github.com/matrix-org/matrix-spec-proposals/pull/4155) */
])
}
+13 -13
View File
@@ -9,6 +9,18 @@ pub mod log;
pub mod matrix;
pub mod metrics;
pub mod mods;
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
pub mod mods {
#[macro_export]
macro_rules! mod_ctor {
() => {};
}
#[macro_export]
macro_rules! mod_dtor {
() => {};
}
}
pub mod server;
pub mod utils;
@@ -27,7 +39,7 @@ pub use info::{
version::{name, version},
};
pub use matrix::{
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, RoomVersion, pdu, state_res,
Event, EventTypeExt, Pdu, PduCount, PduEvent, PduId, pdu, state_res,
};
pub use parking_lot::{Mutex as SyncMutex, RwLock as SyncRwLock};
pub use server::Server;
@@ -36,15 +48,3 @@ pub use utils::{implement, result, result::Result};
pub use crate as conduwuit_core;
conduwuit_macros::introspect_crate! {}
#[cfg(any(not(conduwuit_mods), not(feature = "conduwuit_mods")))]
pub mod mods {
#[macro_export]
macro_rules! mod_ctor {
() => {};
}
#[macro_export]
macro_rules! mod_dtor {
() => {};
}
}