use time::{OffsetDateTime, serde::rfc3339};
use super::AssemblyInfo;
#[cfg_attr(
feature = "borsh",
derive(borsh::BorshSerialize, borsh::BorshDeserialize)
)]
#[serde_with::serde_as]
#[derive(
Debug, Clone, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize,
)]
#[serde(rename_all = "camelCase")]
pub struct SessionInfo {
pub access_level: crate::model::SessionAccessLevel,
pub active_users: u8,
pub app_version: String,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub broadcast_key: bool,
#[serde(default)]
pub compatibility_hash: String,
#[serde(default)]
pub data_model_assemblies: Vec<AssemblyInfo>,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub description: String,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default = "has_ended_default")]
pub has_ended: bool,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub hide_from_listing: bool,
#[serde(rename = "hostUserId")]
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub host_id: Option<crate::id::User>,
pub host_machine_id: String,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub host_user_session_id: Option<crate::id::UserSession>,
pub host_username: String,
#[serde(rename = "sessionId")]
pub id: crate::id::Session,
#[serde(rename = "headlessHost")]
pub is_headless_host: bool,
#[serde(rename = "mobileFriendly")]
pub is_mobile_friendly: bool,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub is_valid: bool,
pub joined_users: u8,
#[cfg_attr(
feature = "borsh",
borsh(
serialize_with = "crate::util::borsh::time::ser",
deserialize_with = "crate::util::borsh::time::de"
)
)]
#[serde(rename = "lastUpdate")]
#[serde(with = "rfc3339")]
pub last_update_time: OffsetDateTime,
pub max_users: u8,
pub name: String,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub nested_session_ids: Vec<crate::id::Session>,
#[serde(rename = "normalizedSessionId")]
pub normalized_id: String,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub parent_session_ids: Vec<crate::id::Session>,
#[cfg_attr(
feature = "borsh",
borsh(
serialize_with = "crate::util::borsh::time::ser",
deserialize_with = "crate::util::borsh::time::de"
)
)]
#[serde(with = "rfc3339")]
pub session_begin_time: OffsetDateTime,
#[serde(default)]
pub system_compatibility_hash: String,
#[serde(default)]
pub tags: Vec<String>,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub thumbnail_url: Option<crate::AssetUrl>,
pub total_active_users: u8,
pub total_joined_users: u8,
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(default)]
pub universe_id: String,
#[serde(rename = "sessionURLs")]
pub urls: Vec<String>,
#[serde(rename = "sessionUsers")]
pub users: Vec<crate::model::SessionUser>,
#[serde(rename = "correspondingWorldId")]
#[serde_as(deserialize_as = "serde_with::DefaultOnNull")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub world: Option<crate::model::RecordId>,
}
const fn has_ended_default() -> bool { true }
#[must_use]
fn bad_xml_strip(str: &str) -> String {
let start_indexes = str.match_indices('<');
let end_indexes = str.match_indices('>');
let mut stripped_name = str.to_owned();
start_indexes.rev().zip(end_indexes.rev()).for_each(
|((start, _), (end, _))| {
if start < end {
stripped_name.replace_range(start..=end, "");
}
},
);
stripped_name
}
impl SessionInfo {
#[must_use]
pub fn stripped_name(&self) -> String { bad_xml_strip(&self.name) }
}
#[allow(clippy::too_many_lines)]
#[cfg(test)]
#[test]
fn session_info() {
use serde_json::json;
let json = json!( {
"name": "<color=yellow>Soko's Library</color> - Folders, Items, Avatars",
"description": "Folders, Items, Avatars, collected for new player equipping.",
"correspondingWorldId": {
"recordId": "R-62ab89f9-7b06-4487-8e36-35f2e46647f8",
"ownerId": "U-Lpsuchtie"
},
"tags": [
"fukuro",
"world",
"library",
"プリメロ工房",
"photo",
"public",
"folder",
"folders",
"items",
"avatars",
"soko"
],
"sessionId": "S-U-1QMVJqtmCsC:soko_library",
"normalizedSessionId": "s-u-1qmvjqtmcsc:soko_library",
"hostUserId": "U-1QMVJqtmCsC",
"hostUserSessionId": "ffa04206-bf29-4c02-98cd-2664ed6aaccb",
"hostMachineId": "7rpx1gn4dojdinqdhhwddqmbwqq1dw1jjdzjstpm3yisss5poq5o",
"hostUsername": "toaster_headless",
"compatibilityHash": "flPfsJqNoHFSFmtE9Nml8g==",
"systemCompatibilityHash": "Wdkgr1roe8mxg4hbQ8EeNQ==",
"dataModelAssemblies": [
{
"name": "Elements.Assets",
"compatibilityHash": "Wk+BcQBAZQudiVcIa5eRyQ=="
},
{
"name": "Elements.Core",
"compatibilityHash": "3XaWAwwz7srqNA3kWPtREA=="
},
{
"name": "FrooxEngine",
"compatibilityHash": "k6Bzkm46ALxejJ/He3tPCQ=="
},
{
"name": "FrooxEngine.Store",
"compatibilityHash": "K22/sKfODKjKTdTi7M/GnQ=="
},
{
"name": "ProtoFlux.Nodes.Core",
"compatibilityHash": "XxMkLAc6ulemxNEOJo7IOQ=="
},
{
"name": "ProtoFlux.Nodes.FrooxEngine",
"compatibilityHash": "w5eT+VlkgtJngh/EZmrhfg=="
},
{
"name": "ProtoFluxBindings",
"compatibilityHash": "xVTKyKC5GEjeT7iUKFUn5g=="
},
{
"name": "SkyFrost.Base",
"compatibilityHash": "te30htqkPqTW/nEKuspZsA=="
},
{
"name": "SkyFrost.Base.Models",
"compatibilityHash": "pi+qiFhdImZ42rvy8ybJnQ=="
}
],
"universeId": null,
"appVersion": "2024.7.25.1284",
"headlessHost": true,
"sessionURLs": [
"lnl-nat://be2ae106c5a14ea58ed05664ab2306bd/S-U-1QMVJqtmCsC:soko_library"
],
"parentSessionIds": [],
"nestedSessionIds": [],
"sessionUsers": [
{
"username": "toaster_headless",
"userID": "U-1QMVJqtmCsC",
"userSessionId": null,
"isPresent": false,
"outputDevice": null
}
],
"thumbnailUrl": null,
"joinedUsers": 0,
"activeUsers": 0,
"totalJoinedUsers": 0,
"totalActiveUsers": 0,
"maxUsers": 10,
"mobileFriendly": false,
"sessionBeginTime": "2024-07-28T22:00:44.403632Z",
"lastUpdate": "2024-07-28T23:25:47.013536Z",
"accessLevel": "Anyone",
"hideFromListing": false,
"broadcastKey": null,
"hasEnded": false,
"isValid": true
});
serde_json::from_value::<SessionInfo>(json).unwrap();
}