Replace compat feature with more fine-grained compat-* features

… and document them.
This commit is contained in:
Jonas Platte 2023-04-20 13:31:47 +02:00
parent 57b7b2d0f5
commit 4c85fe9c78
No known key found for this signature in database
GPG Key ID: AAA7A61F696C3E0C
34 changed files with 211 additions and 117 deletions

View File

@ -16,7 +16,23 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[features]
compat = []
# OutgoingRequest and IncomingResponse implementations
client = []
# IncomingRequest and OutgoingResponse implementations
server = []
# Allow some mandatory fields in requests / responses to be missing, defaulting
# them to an empty string in deserialization.
compat-empty-string-null = []
# Unset avatars by sending an empty string, same as what Element Web does, c.f.
# https://github.com/matrix-org/matrix-spec/issues/378#issuecomment-1055831264
compat-unset-avatar = []
# Always serialize the threepids response field in `get_3pids::v3::Response`,
# even if its value is an empty list.
compat-get-3pids = []
unstable-exhaustive-types = ["ruma-common/unstable-exhaustive-types"]
unstable-msc2246 = []
unstable-msc2666 = []
@ -27,8 +43,6 @@ unstable-msc2965 = []
unstable-msc2967 = []
unstable-msc3488 = []
unstable-msc3575 = []
client = []
server = []
[dependencies]
assign = { workspace = true }

View File

@ -33,8 +33,14 @@ pub mod v3 {
pub struct Response {
/// A list of third party identifiers the homeserver has associated with the user's
/// account.
///
/// If the `compat-get-3pids` feature is enabled, this field will always be serialized,
/// even if its value is an empty list.
#[serde(default)]
#[cfg_attr(not(feature = "compat"), serde(skip_serializing_if = "Vec::is_empty"))]
#[cfg_attr(
not(feature = "compat-get-3pids"),
serde(skip_serializing_if = "Vec::is_empty")
)]
pub threepids: Vec<ThirdPartyIdentifier>,
}

View File

@ -57,11 +57,11 @@ pub mod v3 {
///
/// If omitted, verification happens without client.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub submit_url: Option<String>,

View File

@ -60,11 +60,11 @@ pub mod v3 {
///
/// If omitted, verification happens without client.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub submit_url: Option<String>,

View File

@ -57,11 +57,11 @@ pub mod v3 {
///
/// If omitted, verification happens without client.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub submit_url: Option<String>,

View File

@ -53,11 +53,11 @@ pub mod v3 {
///
/// If omitted, verification happens without client.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub submit_url: Option<String>,

View File

@ -57,11 +57,11 @@ pub mod v3 {
///
/// If omitted, verification happens without client.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub submit_url: Option<String>,

View File

@ -60,11 +60,11 @@ pub mod v3 {
///
/// If omitted, verification happens without client.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub submit_url: Option<String>,

View File

@ -66,11 +66,11 @@ pub mod v3 {
/// The mxc avatar url of the user.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,
@ -103,7 +103,7 @@ pub mod v3 {
Some(mxc_uri!("mxc://localhost/wefuiwegh8742w"))
);
#[cfg(feature = "compat")]
#[cfg(feature = "compat-empty-string-null")]
{
let member = from_json_value::<RoomMember>(json!({
"display_name": "alice",

View File

@ -36,11 +36,11 @@ pub mod v3 {
pub struct Response {
/// The user's avatar URL, if set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -36,11 +36,11 @@ pub mod v3 {
pub struct Response {
/// The user's avatar URL, if set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -33,18 +33,24 @@ pub mod v3 {
///
/// `None` is used to unset the avatar.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
///
/// If you active the `compat-unset-avatar` feature, this field being `None` will result
/// in an empty string in serialization, which is the same thing Element Web does (c.f.
/// <https://github.com/matrix-org/matrix-spec/issues/378#issuecomment-1055831264>).
#[cfg_attr(
feature = "compat",
serde(
default,
deserialize_with = "ruma_common::serde::empty_string_as_none",
// https://github.com/matrix-org/matrix-spec/issues/378
serialize_with = "ruma_common::serde::none_as_empty_string"
)
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
#[cfg_attr(
feature = "compat-unset-avatar",
serde(serialize_with = "ruma_common::serde::none_as_empty_string")
)]
#[cfg_attr(
not(feature = "compat-unset-avatar"),
serde(skip_serializing_if = "Option::is_none")
)]
#[cfg_attr(not(feature = "compat"), serde(skip_serializing_if = "Option::is_none"))]
pub avatar_url: Option<OwnedMxcUri>,
/// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`.
@ -100,7 +106,7 @@ pub mod v3 {
assert_eq!(req.user_id, "@foo:bar.org");
assert_eq!(req.avatar_url, None);
#[cfg(feature = "compat")]
#[cfg(feature = "compat-empty-string-null")]
{
let req = Request::try_from_http_request(
http::Request::builder()

View File

@ -460,11 +460,11 @@ pub mod v3 {
pub struct UserProfile {
/// The user's avatar URL, if set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -27,7 +27,7 @@ pub struct SpaceHierarchyRoomsChunk {
/// The canonical alias of the room, if any.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub canonical_alias: Option<OwnedRoomAliasId>,
@ -56,11 +56,11 @@ pub struct SpaceHierarchyRoomsChunk {
/// The URL for the room's avatar, if one is set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will result
/// in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty string in
/// JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -91,11 +91,11 @@ pub mod v3 {
/// The avatar url, as an MXC, if one exists.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -23,7 +23,6 @@ server = []
api = ["dep:http"]
canonical-json = []
compat = ["ruma-macros/compat", "ruma-identifiers-validation/compat"]
events = []
js = ["dep:js-sys", "getrandom?/js", "uuid?/js"]
markdown = ["pulldown-cmark"]
@ -54,6 +53,25 @@ unstable-pdu = []
unstable-sanitize = ["dep:html5ever", "dep:phf"]
unstable-unspecified = []
# Don't validate the version part in `KeyId`.
compat-key-id = ["ruma-identifiers-validation/compat-key-id"]
# Allow some user IDs that are invalid even with the specified historical
# user ID scheme.
compat-user-id = ["ruma-identifiers-validation/compat-user-id"]
# Allow some mandatory fields to be missing, defaulting them to an empty string
# in deserialization.
compat-empty-string-null = []
# Allow certain fields to be `null` for compatibility, treating that the same as
# the field being absent.
compat-null = []
# Allow certain fields to be absent even though the spec marks them as
# mandatory. Deserialization will yield a default value like an empty string.
compat-optional = []
[dependencies]
base64 = { workspace = true }
bytes = "1.0.1"

View File

@ -20,7 +20,7 @@ pub struct PublicRoomsChunk {
/// The canonical alias of the room, if any.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "crate::serde::empty_string_as_none")
)]
pub canonical_alias: Option<OwnedRoomAliasId>,
@ -49,11 +49,11 @@ pub struct PublicRoomsChunk {
/// The URL for the room's avatar, if one is set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will result
/// in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty string in
/// JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "crate::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,
@ -120,8 +120,11 @@ pub struct Filter {
/// The room types to include in the results.
///
/// Includes all room types if it is empty.
///
/// If the `compat-null` feature is enabled, a `null` value is allowed in deserialization, and
/// treated the same way as an empty list.
#[serde(default, skip_serializing_if = "Vec::is_empty")]
#[cfg_attr(feature = "compat", serde(deserialize_with = "crate::serde::none_as_default"))]
#[cfg_attr(feature = "compat-null", serde(deserialize_with = "crate::serde::none_as_default"))]
pub room_types: Vec<RoomTypeFilter>,
}

View File

@ -42,11 +42,11 @@ impl Serialize for PresenceEvent {
pub struct PresenceEventContent {
/// The current avatar URL for this user.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will result
/// in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty string in
/// JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "crate::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,
@ -141,7 +141,7 @@ mod tests {
assert_eq!(ev.content.status_msg.as_deref(), Some("Making cupcakes"));
assert_eq!(ev.sender, "@example:localhost");
#[cfg(feature = "compat")]
#[cfg(feature = "compat-empty-string-null")]
{
let json = json!({
"content": {

View File

@ -57,11 +57,11 @@ pub use self::change::{Change, MembershipChange, MembershipDetails};
pub struct RoomMemberEventContent {
/// The avatar URL for this user, if any.
///
/// This is added by the homeserver. If you activate the `compat` feature, this field being an
/// empty string in JSON will result in `None` here during deserialization.
/// This is added by the homeserver. If you activate the `compat-empty-string-null` feature,
/// this field being an empty string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "crate::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -20,23 +20,23 @@ use crate::serde::Base64;
pub struct RoomThirdPartyInviteEventContent {
/// A user-readable string which represents the user who has been invited.
///
/// If you activate the `compat` feature, this field being absent in JSON will result in an
/// empty string here during deserialization.
#[cfg_attr(feature = "compat", serde(default))]
/// If the `compat-optional` feature is enabled, this field being absent in JSON will result
/// in an empty string instead of an error when deserializing.
#[cfg_attr(feature = "compat-optional", serde(default))]
pub display_name: String,
/// A URL which can be fetched to validate whether the key has been revoked.
///
/// If you activate the `compat` feature, this field being absent in JSON will result in an
/// empty string here during deserialization.
#[cfg_attr(feature = "compat", serde(default))]
/// If the `compat-optional` feature is enabled, this field being absent in JSON will result
/// in an empty string instead of an error when deserializing.
#[cfg_attr(feature = "compat-optional", serde(default))]
pub key_validity_url: String,
/// A base64-encoded Ed25519 key with which the token must be signed.
///
/// If you activate the `compat` feature, this field being absent in JSON will result in an
/// empty string here during deserialization.
#[cfg_attr(feature = "compat", serde(default = "Base64::empty"))]
/// If the `compat-optional` feature is enabled, this field being absent in JSON will result
/// in an empty string instead of an error when deserializing.
#[cfg_attr(feature = "compat-optional", serde(default = "Base64::empty"))]
pub public_key: Base64,
/// Keys with which the token may be signed.

View File

@ -28,9 +28,9 @@ use crate::{
pub struct RoomTombstoneEventContent {
/// A server-defined message.
///
/// If you activate the `compat` feature, this field being absent in JSON will result in an
/// empty string here during deserialization.
#[cfg_attr(feature = "compat", serde(default))]
/// If the `compat-optional` feature is enabled, this field being absent in JSON will result
/// in an empty string instead of an error when deserializing.
#[cfg_attr(feature = "compat-optional", serde(default))]
pub body: String,
/// The new room the client should be visiting.

View File

@ -188,7 +188,7 @@ mod tests {
assert!(!user_id.is_historical());
}
#[cfg(not(feature = "compat"))]
#[cfg(not(feature = "compat-user-id"))]
#[test]
fn invalid_user_id() {
let localpart = "τ";
@ -303,7 +303,7 @@ mod tests {
}
#[test]
#[cfg(not(feature = "compat"))]
#[cfg(not(feature = "compat-user-id"))]
fn invalid_characters_in_user_id_localpart() {
assert_eq!(
<&UserId>::try_from("@te\nst:example.com").unwrap_err(),

View File

@ -25,9 +25,9 @@ pub struct Protocol {
/// A content URI representing an icon for the third party protocol.
///
/// If you activate the `compat` feature, this field being absent in JSON will result in an
/// empty string here during deserialization.
#[cfg_attr(feature = "compat", serde(default))]
/// If the `compat-optional` feature is enabled, this field being absent in JSON will result
/// in an empty string instead of an error when deserializing.
#[cfg_attr(feature = "compat-optional", serde(default))]
pub icon: String,
/// The type definitions for the fields defined in `user_fields` and `location_fields`.

View File

@ -16,7 +16,10 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[features]
compat = []
# Allow some mandatory fields in requests / responses to be missing, defaulting
# them to an empty string in deserialization.
compat-empty-string-null = []
client = []
server = []
unstable-exhaustive-types = []

View File

@ -48,11 +48,11 @@ pub mod v1 {
/// Avatar URL for the user's avatar.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will
/// result in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -17,9 +17,12 @@ pub mod get_hierarchy;
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct SpaceHierarchyParentSummary {
/// The canonical alias of the room, if any.
///
/// If you activate the `compat-empty-string-null` feature, this field being an empty
/// string in JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub canonical_alias: Option<OwnedRoomAliasId>,
@ -48,11 +51,11 @@ pub struct SpaceHierarchyParentSummary {
/// The URL for the room's avatar, if one is set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will result
/// in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty string in
/// JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,
@ -147,9 +150,12 @@ impl From<SpaceHierarchyParentSummaryInit> for SpaceHierarchyParentSummary {
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
pub struct SpaceHierarchyChildSummary {
/// The canonical alias of the room, if any.
///
/// If you activate the `compat-empty-string-null` feature, this field being an empty string in
/// JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub canonical_alias: Option<OwnedRoomAliasId>,
@ -178,11 +184,11 @@ pub struct SpaceHierarchyChildSummary {
/// The URL for the room's avatar, if one is set.
///
/// If you activate the `compat` feature, this field being an empty string in JSON will result
/// in `None` here during deserialization.
/// If you activate the `compat-empty-string-null` feature, this field being an empty string in
/// JSON will result in `None` here during deserialization.
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(
feature = "compat",
feature = "compat-empty-string-null",
serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
)]
pub avatar_url: Option<OwnedMxcUri>,

View File

@ -12,7 +12,11 @@ rust-version = "1.64"
all-features = true
[features]
compat = []
# Don't validate the version part in `key_id::validate`.
compat-key-id = []
# Allow some user IDs that are invalid even with the specified historical
# user ID scheme.
compat-user-id = []
[dependencies]
js_int = { workspace = true }

View File

@ -6,13 +6,13 @@ pub fn validate(s: &str) -> Result<NonZeroU8, Error> {
let colon_idx =
NonZeroU8::new(s.find(':').ok_or(Error::MissingColon)? as u8).ok_or(Error::MissingColon)?;
#[cfg(not(feature = "compat"))]
#[cfg(not(feature = "compat-key-id"))]
validate_version(&s[colon_idx.get() as usize + 1..])?;
Ok(colon_idx)
}
#[cfg(not(feature = "compat"))]
#[cfg(not(feature = "compat-key-id"))]
fn validate_version(version: &str) -> Result<(), Error> {
if version.is_empty() {
return Err(Error::Empty);

View File

@ -27,13 +27,13 @@ pub fn localpart_is_fully_conforming(localpart: &str) -> Result<bool, Error> {
// If it's not fully conforming, check if it contains characters that are also disallowed
// for historical user IDs. If there are, return an error.
// See https://spec.matrix.org/latest/appendices/#historical-user-ids
#[cfg(not(feature = "compat"))]
#[cfg(not(feature = "compat-user-id"))]
let is_invalid = localpart.bytes().any(|b| b < 0x21 || b == b':' || b > 0x7E);
// In compat mode, allow anything except `:` to match Synapse. The `:` check is only needed
// because this function can be called through `UserId::parse_with_servername`, otherwise
// it would be impossible for the input to contain a `:`.
#[cfg(feature = "compat")]
#[cfg(feature = "compat-user-id")]
let is_invalid = localpart.as_bytes().contains(&b':');
if is_invalid {

View File

@ -14,9 +14,6 @@ rust-version = "1.64"
[lib]
proc-macro = true
[features]
compat = []
[dependencies]
once_cell = "1.13.0"
proc-macro-crate = "1.0.0"

View File

@ -14,7 +14,8 @@ edition = "2021"
all-features = true
[features]
compat = []
# Allow extra characters in signature IDs not allowed in the specification.
compat-signature-id = []
ring-compat = ["dep:subslice"]
unstable-exhaustive-types = []

View File

@ -85,9 +85,9 @@ fn split_id(id: &str) -> Result<(Algorithm, String), Error> {
let version = signature_id[1];
#[cfg(feature = "compat")]
#[cfg(feature = "compat-signature-id")]
const EXTRA_ALLOWED: [u8; 3] = [b'_', b'+', b'/'];
#[cfg(not(feature = "compat"))]
#[cfg(not(feature = "compat-signature-id"))]
const EXTRA_ALLOWED: [u8; 1] = [b'_'];
if !version.bytes().all(|ch| ch.is_ascii_alphanumeric() || EXTRA_ALLOWED.contains(&ch)) {
return Err(Error::InvalidVersion(version.into()));

View File

@ -88,17 +88,43 @@ full = [
"markdown",
]
# Increase compatibility with other parts of the Matrix ecosystem, at the
# expense of weird behaviour where things deviate from the specification.
#
# For example, some mandatory string fields are defaulted to an empty string if
# missing with this feature.
# Enable all compatibility hacks. Deprecated.
compat = [
"ruma-common/compat",
"ruma-client-api?/compat",
"ruma-federation-api?/compat",
"ruma-signatures?/compat",
"compat-key-id",
"compat-user-id",
"compat-empty-string-null",
"compat-null",
"compat-optional",
"compat-unset-avatar",
"compat-get-3pids",
"compat-signature-id",
]
# Don't validate the version part in `KeyId`.
compat-key-id = ["ruma-common/compat-key-id"]
# Allow some user IDs that are invalid even with the specified historical
# user ID scheme.
compat-user-id = ["ruma-common/compat-user-id"]
# Allow some mandatory fields in requests / responses to be missing, defaulting
# them to an empty string in deserialization.
compat-empty-string-null = [
"ruma-common/compat-empty-string-null",
"ruma-client-api?/compat-empty-string-null",
"ruma-federation-api?/compat-empty-string-null",
]
# Allow certain fields to be `null` for compatibility, treating that the same as
# the field being absent.
compat-null = ["ruma-common/compat-null"]
# Allow certain fields to be absent even though the spec marks them as
# mandatory. Deserialization will yield a default value like an empty string.
compat-optional = ["ruma-common/compat-optional"]
# Unset avatars by sending an empty string, same as what Element Web does, c.f.
# https://github.com/matrix-org/matrix-spec/issues/378#issuecomment-1055831264
compat-unset-avatar = ["ruma-client-api?/compat-unset-avatar"]
# Always serialize the threepids response field in `get_3pids::v3::Response`,
# even if its value is an empty list.
compat-get-3pids = ["ruma-client-api?/compat-get-3pids"]
# Allow extra characters in signature IDs not allowed in the specification.
compat-signature-id = ["ruma-signatures?/compat-signature-id"]
# Specific compatibility for past ring public/private key documents.
ring-compat = ["dep:ruma-signatures", "ruma-signatures?/ring-compat"]

View File

@ -189,7 +189,12 @@ impl CiTask {
/// Check ruma-common with onjy the required features with the stable version.
fn stable_common(&self) -> Result<()> {
cmd!("rustup run stable cargo check -p ruma-common --no-default-features --features client,server").run().map_err(Into::into)
cmd!(
"rustup run stable cargo check -p ruma-common
--no-default-features --features client,server"
)
.run()
.map_err(Into::into)
}
/// Run tests on all crates with almost all features with the stable version.
@ -204,7 +209,12 @@ impl CiTask {
/// Test ruma-common with the compat feature with the stable version.
fn test_common(&self) -> Result<()> {
cmd!("rustup run stable cargo test -p ruma-common --features events --features compat compat").run().map_err(Into::into)
cmd!(
"rustup run stable cargo test -p ruma-common
--features events,compat-empty-string-null,compat-user-id compat"
)
.run()
.map_err(Into::into)
}
/// Run all the tasks that use the nightly version.