use crate::crypto::{CryptoParameters, SecrecyMode, SecurityLevel};
use crate::prelude::HeaderObfuscatorSettings;
use crate::utils;
use packed_struct::derive::PrimitiveEnum_u8;
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display, Formatter};
use std::path::PathBuf;
use strum::VariantNames;
#[cfg(feature = "typescript")]
use ts_rs::TS;
use uuid::Uuid;
#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum ConnectMode {
Standard { force_login: bool },
Fetch { force_login: bool },
}
impl Default for ConnectMode {
fn default() -> Self {
Self::Standard { force_login: false }
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct VirtualObjectMetadata {
pub name: String,
pub date_created: String,
pub author: String,
pub plaintext_length: usize,
pub group_count: usize,
pub object_id: ObjectId,
pub cid: u64,
pub transfer_type: TransferType,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
#[repr(transparent)]
pub struct ObjectId(pub u128);
impl Debug for ObjectId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl Display for ObjectId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
}
}
impl ObjectId {
pub fn random() -> Self {
Uuid::new_v4().as_u128().into()
}
pub const fn zero() -> Self {
Self(0)
}
}
impl From<u128> for ObjectId {
fn from(value: u128) -> Self {
Self(value)
}
}
impl VirtualObjectMetadata {
pub fn serialize(&self) -> Vec<u8> {
bincode::serialize(self).unwrap()
}
pub fn deserialize_from<'a, T: AsRef<[u8]> + 'a>(input: T) -> Option<Self> {
bincode::deserialize(input.as_ref()).ok()
}
pub fn get_security_level(&self) -> Option<SecurityLevel> {
match &self.transfer_type {
TransferType::FileTransfer => None,
TransferType::RemoteEncryptedVirtualFilesystem { security_level, .. } => {
Some(*security_level)
}
}
}
}
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum ObjectTransferOrientation {
Receiver { is_revfs_pull: bool },
Sender,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
#[allow(variant_size_differences)]
pub enum ObjectTransferStatus {
TransferBeginning,
ReceptionBeginning(PathBuf, VirtualObjectMetadata),
TransferTick(usize, usize, f32),
ReceptionTick(usize, usize, f32),
TransferComplete,
ReceptionComplete,
Fail(String),
}
impl ObjectTransferStatus {
pub fn is_tick_type(&self) -> bool {
matches!(
self,
ObjectTransferStatus::TransferTick(_, _, _)
| ObjectTransferStatus::ReceptionTick(_, _, _)
)
}
pub fn is_finished_type(&self) -> bool {
matches!(
self,
ObjectTransferStatus::TransferComplete
| ObjectTransferStatus::ReceptionComplete
| ObjectTransferStatus::Fail(_)
)
}
}
impl std::fmt::Display for ObjectTransferStatus {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ObjectTransferStatus::TransferBeginning => {
write!(f, "Transfer beginning")
}
ObjectTransferStatus::ReceptionBeginning(_, vfm) => {
write!(f, "Download for object {vfm:?} beginning")
}
ObjectTransferStatus::TransferTick(relative_group_id, total_groups, transfer_rate) => {
utils::print_tick(f, *relative_group_id, *total_groups, *transfer_rate)
}
ObjectTransferStatus::ReceptionTick(relative_group_id, total_groups, transfer_rate) => {
utils::print_tick(f, *relative_group_id, *total_groups, *transfer_rate)
}
ObjectTransferStatus::TransferComplete => {
write!(f, "Transfer complete")
}
ObjectTransferStatus::ReceptionComplete => {
write!(f, "Download complete")
}
ObjectTransferStatus::Fail(reason) => {
write!(f, "Failure. Reason: {reason}")
}
}
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct SessionSecuritySettings {
pub security_level: SecurityLevel,
pub secrecy_mode: SecrecyMode,
pub crypto_params: CryptoParameters,
pub header_obfuscator_settings: HeaderObfuscatorSettings,
}
#[derive(
Debug,
Serialize,
Deserialize,
Copy,
Clone,
Eq,
PartialEq,
Default,
PrimitiveEnum_u8,
strum::EnumString,
strum::EnumIter,
strum::EnumCount,
strum_macros::VariantNames,
)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum UdpMode {
#[default]
Disabled,
Enabled,
}
impl UdpMode {
pub fn variants() -> Vec<String> {
Self::VARIANTS.iter().map(|s| s.to_string()).collect()
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum MemberState {
EnteredGroup { cids: Vec<u64> },
LeftGroup { cids: Vec<u64> },
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum GroupMemberAlterMode {
Leave,
Kick,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct MessageGroupOptions {
pub group_type: GroupType,
pub id: u128,
#[cfg_attr(feature = "typescript", ts(skip))]
#[serde(default)]
pub hierarchy: GroupHierarchyMode,
}
impl Default for MessageGroupOptions {
fn default() -> Self {
Self {
group_type: GroupType::Private,
id: Uuid::new_v4().as_u128(),
hierarchy: GroupHierarchyMode::default(),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
pub enum GroupHierarchyMode {
#[default]
Flat,
CommandHierarchy {
read_policy: ReadPolicy,
#[serde(default)]
ranks: std::collections::HashMap<u64, CommandPath>,
},
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ReadPolicy {
SuperiorOnly,
BroadcastAudit,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
pub struct CommandPath(pub Vec<String>);
impl CommandPath {
pub fn root() -> Self {
CommandPath(Vec::new())
}
pub fn parse(path: &str) -> Self {
CommandPath(
path.split('/')
.filter(|s| !s.is_empty())
.map(str::to_string)
.collect(),
)
}
pub fn child(&self, segment: &str) -> Self {
let mut segs = self.0.clone();
segs.push(segment.to_string());
CommandPath(segs)
}
pub fn depth(&self) -> usize {
self.0.len()
}
pub fn is_ancestor_of(&self, other: &CommandPath) -> bool {
other.0.len() >= self.0.len() && other.0[..self.0.len()] == self.0[..]
}
}
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Copy, Clone)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum GroupType {
Public,
Private,
}
#[derive(Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct MessageGroupKey {
pub cid: u64,
pub mgid: u128,
}
impl MessageGroupKey {
pub fn new(cid: u64, mgid: u128) -> Self {
Self { cid, mgid }
}
}
impl std::fmt::Debug for MessageGroupKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{self}")
}
}
impl std::fmt::Display for MessageGroupKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "[{}:{}]", self.cid, self.mgid)
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum TransferType {
FileTransfer,
RemoteEncryptedVirtualFilesystem {
virtual_path: PathBuf,
security_level: SecurityLevel,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum ClientConnectionType {
Server {
session_cid: u64,
},
Extended {
session_cid: u64,
interserver_cid: u64,
},
}
impl ClientConnectionType {
#[inline]
pub fn session_cid(&self) -> u64 {
match self {
ClientConnectionType::Server { session_cid } => *session_cid,
ClientConnectionType::Extended { session_cid, .. } => *session_cid,
}
}
#[inline]
pub fn interserver_cid(&self) -> Option<u64> {
match self {
ClientConnectionType::Server { .. } => None,
ClientConnectionType::Extended {
interserver_cid, ..
} => Some(*interserver_cid),
}
}
}
impl Display for ClientConnectionType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
ClientConnectionType::Server { session_cid } => {
write!(f, "C2S Server (cid={session_cid})")
}
ClientConnectionType::Extended {
session_cid,
interserver_cid,
} => {
write!(
f,
"C2S Extended (cid={session_cid}, icid={interserver_cid})"
)
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum PeerConnectionType {
LocalGroupPeer {
session_cid: u64,
peer_cid: u64,
},
ExternalGroupPeer {
session_cid: u64,
interserver_cid: u64,
peer_cid: u64,
},
}
impl PeerConnectionType {
#[inline]
pub fn get_original_session_cid(&self) -> u64 {
match self {
PeerConnectionType::LocalGroupPeer { session_cid, .. } => *session_cid,
PeerConnectionType::ExternalGroupPeer { session_cid, .. } => *session_cid,
}
}
#[inline]
pub fn get_original_target_cid(&self) -> u64 {
match self {
PeerConnectionType::LocalGroupPeer { peer_cid, .. } => *peer_cid,
PeerConnectionType::ExternalGroupPeer { peer_cid, .. } => *peer_cid,
}
}
pub fn reverse(&self) -> PeerConnectionType {
match self {
PeerConnectionType::LocalGroupPeer {
session_cid,
peer_cid,
} => PeerConnectionType::LocalGroupPeer {
session_cid: *peer_cid,
peer_cid: *session_cid,
},
PeerConnectionType::ExternalGroupPeer {
session_cid,
interserver_cid,
peer_cid,
} => PeerConnectionType::ExternalGroupPeer {
session_cid: *peer_cid,
interserver_cid: *interserver_cid,
peer_cid: *session_cid,
},
}
}
pub fn as_virtual_connection(self) -> VirtualConnectionType {
match self {
PeerConnectionType::LocalGroupPeer {
session_cid,
peer_cid,
} => VirtualConnectionType::LocalGroupPeer {
session_cid,
peer_cid,
},
PeerConnectionType::ExternalGroupPeer {
session_cid,
interserver_cid,
peer_cid,
} => VirtualConnectionType::ExternalGroupPeer {
session_cid,
interserver_cid,
peer_cid,
},
}
}
}
impl Display for PeerConnectionType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
PeerConnectionType::LocalGroupPeer {
session_cid,
peer_cid,
} => {
write!(f, "hLAN {session_cid} <-> {peer_cid}")
}
PeerConnectionType::ExternalGroupPeer {
session_cid,
interserver_cid,
peer_cid,
} => {
write!(f, "hWAN {session_cid} <-> {interserver_cid} <-> {peer_cid}")
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum VirtualConnectionType {
LocalGroupPeer {
session_cid: u64,
peer_cid: u64,
},
ExternalGroupPeer {
session_cid: u64,
interserver_cid: u64,
peer_cid: u64,
},
LocalGroupServer {
session_cid: u64,
},
ExternalGroupServer {
session_cid: u64,
interserver_cid: u64,
},
}
pub type VirtualTargetType = VirtualConnectionType;
pub const C2S_IDENTITY_CID: u64 = 0;
impl VirtualConnectionType {
pub fn serialize(&self) -> Vec<u8> {
bincode::serialize(self).unwrap()
}
pub fn deserialize_from<'a, T: AsRef<[u8]> + 'a>(this: T) -> Option<Self> {
bincode::deserialize(this.as_ref()).ok()
}
pub fn get_target_cid(&self) -> u64 {
match self {
VirtualConnectionType::LocalGroupServer { .. } => C2S_IDENTITY_CID,
VirtualConnectionType::LocalGroupPeer { peer_cid, .. } => *peer_cid,
VirtualConnectionType::ExternalGroupPeer { peer_cid, .. } => *peer_cid,
VirtualConnectionType::ExternalGroupServer {
interserver_cid, ..
} => *interserver_cid,
}
}
pub fn get_session_cid(&self) -> u64 {
match self {
VirtualConnectionType::LocalGroupServer { session_cid } => *session_cid,
VirtualConnectionType::LocalGroupPeer { session_cid, .. } => *session_cid,
VirtualConnectionType::ExternalGroupPeer { session_cid, .. } => *session_cid,
VirtualConnectionType::ExternalGroupServer { session_cid, .. } => *session_cid,
}
}
pub fn is_server_connection(&self) -> bool {
matches!(
self,
VirtualConnectionType::LocalGroupServer { .. }
| VirtualConnectionType::ExternalGroupServer { .. }
)
}
pub fn is_peer_connection(&self) -> bool {
matches!(
self,
VirtualConnectionType::LocalGroupPeer { .. }
| VirtualConnectionType::ExternalGroupPeer { .. }
)
}
pub fn try_as_peer_connection(&self) -> Option<PeerConnectionType> {
match self {
VirtualConnectionType::LocalGroupPeer {
session_cid,
peer_cid,
} => Some(PeerConnectionType::LocalGroupPeer {
session_cid: *session_cid,
peer_cid: *peer_cid,
}),
VirtualConnectionType::ExternalGroupPeer {
session_cid,
interserver_cid,
peer_cid,
} => Some(PeerConnectionType::ExternalGroupPeer {
session_cid: *session_cid,
interserver_cid: *interserver_cid,
peer_cid: *peer_cid,
}),
_ => None,
}
}
pub fn try_as_client_connection(&self) -> Option<ClientConnectionType> {
match self {
VirtualConnectionType::LocalGroupServer { session_cid } => {
Some(ClientConnectionType::Server {
session_cid: *session_cid,
})
}
VirtualConnectionType::ExternalGroupServer {
session_cid,
interserver_cid,
} => Some(ClientConnectionType::Extended {
session_cid: *session_cid,
interserver_cid: *interserver_cid,
}),
_ => None,
}
}
pub fn is_local_group(&self) -> bool {
matches!(
self,
VirtualConnectionType::LocalGroupPeer { .. }
| VirtualConnectionType::LocalGroupServer { .. }
)
}
pub fn is_external_group(&self) -> bool {
!self.is_local_group()
}
pub fn set_target_cid(&mut self, target_cid: u64) {
match self {
VirtualConnectionType::LocalGroupPeer { peer_cid, .. }
| VirtualConnectionType::ExternalGroupPeer { peer_cid, .. } => *peer_cid = target_cid,
_ => {}
}
}
pub fn set_session_cid(&mut self, cid: u64) {
match self {
VirtualConnectionType::LocalGroupPeer { session_cid, .. }
| VirtualConnectionType::ExternalGroupPeer { session_cid, .. }
| VirtualConnectionType::LocalGroupServer { session_cid }
| VirtualConnectionType::ExternalGroupServer { session_cid, .. } => *session_cid = cid,
}
}
}
impl Display for VirtualConnectionType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
VirtualConnectionType::LocalGroupServer { session_cid } => {
write!(f, "C2S Local (cid={session_cid})")
}
VirtualConnectionType::LocalGroupPeer {
session_cid,
peer_cid,
} => {
write!(f, "P2P Local ({session_cid} -> {peer_cid})")
}
VirtualConnectionType::ExternalGroupPeer {
session_cid,
interserver_cid,
peer_cid,
} => {
write!(
f,
"P2P External ({session_cid} -> {interserver_cid} -> {peer_cid})"
)
}
VirtualConnectionType::ExternalGroupServer {
session_cid,
interserver_cid,
} => {
write!(f, "C2S External ({session_cid} -> {interserver_cid})")
}
}
}
}
impl From<PeerConnectionType> for VirtualConnectionType {
fn from(peer: PeerConnectionType) -> Self {
peer.as_virtual_connection()
}
}
impl From<ClientConnectionType> for VirtualConnectionType {
fn from(client: ClientConnectionType) -> Self {
match client {
ClientConnectionType::Server { session_cid } => {
VirtualConnectionType::LocalGroupServer { session_cid }
}
ClientConnectionType::Extended {
session_cid,
interserver_cid,
} => VirtualConnectionType::ExternalGroupServer {
session_cid,
interserver_cid,
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn command_path_parse_root_child_depth() {
assert_eq!(CommandPath::root().depth(), 0);
let p = CommandPath::parse("/HQ/Bn1/Co-A");
assert_eq!(p.0, vec!["HQ", "Bn1", "Co-A"]);
assert_eq!(p.depth(), 3);
assert_eq!(CommandPath::parse("///HQ//Bn1/").0, vec!["HQ", "Bn1"]);
assert_eq!(CommandPath::parse("").depth(), 0);
let c = CommandPath::parse("/HQ").child("Bn1");
assert_eq!(c.0, vec!["HQ", "Bn1"]);
}
#[test]
fn command_path_is_ancestor_of() {
let root = CommandPath::root();
let hq = CommandPath::parse("/HQ");
let bn1 = CommandPath::parse("/HQ/Bn1");
let coa = CommandPath::parse("/HQ/Bn1/Co-A");
let cob = CommandPath::parse("/HQ/Bn1/Co-B");
assert!(hq.is_ancestor_of(&hq));
assert!(root.is_ancestor_of(&coa));
assert!(hq.is_ancestor_of(&bn1));
assert!(bn1.is_ancestor_of(&coa));
assert!(!coa.is_ancestor_of(&bn1));
assert!(!coa.is_ancestor_of(&cob));
assert!(!cob.is_ancestor_of(&coa));
}
#[test]
fn virtual_connection_type_accessors_and_predicates() {
let server = VirtualConnectionType::LocalGroupServer { session_cid: 7 };
let peer = VirtualConnectionType::LocalGroupPeer {
session_cid: 7,
peer_cid: 9,
};
let ext_peer = VirtualConnectionType::ExternalGroupPeer {
session_cid: 1,
interserver_cid: 2,
peer_cid: 3,
};
let ext_server = VirtualConnectionType::ExternalGroupServer {
session_cid: 4,
interserver_cid: 5,
};
assert_eq!(server.get_target_cid(), C2S_IDENTITY_CID);
assert_eq!(server.get_session_cid(), 7);
assert_eq!(peer.get_target_cid(), 9);
assert_eq!(ext_peer.get_target_cid(), 3);
assert_eq!(ext_server.get_target_cid(), 5);
assert_eq!(ext_peer.get_session_cid(), 1);
assert!(server.is_server_connection() && !server.is_peer_connection());
assert!(peer.is_peer_connection() && !peer.is_server_connection());
assert!(server.is_local_group() && !server.is_external_group());
assert!(ext_peer.is_external_group() && !ext_peer.is_local_group());
assert!(peer.try_as_peer_connection().is_some());
assert!(server.try_as_peer_connection().is_none());
assert!(server.try_as_client_connection().is_some());
assert!(peer.try_as_client_connection().is_none());
}
#[test]
fn virtual_connection_type_mutators_and_roundtrip() {
let mut peer = VirtualConnectionType::LocalGroupPeer {
session_cid: 7,
peer_cid: 9,
};
peer.set_target_cid(42);
peer.set_session_cid(11);
assert_eq!(peer.get_target_cid(), 42);
assert_eq!(peer.get_session_cid(), 11);
let mut server = VirtualConnectionType::LocalGroupServer { session_cid: 1 };
server.set_target_cid(99);
assert_eq!(server.get_target_cid(), C2S_IDENTITY_CID);
server.set_session_cid(2);
assert_eq!(server.get_session_cid(), 2);
let bytes = peer.serialize();
assert_eq!(VirtualConnectionType::deserialize_from(&bytes), Some(peer));
assert!(VirtualConnectionType::deserialize_from([0xFFu8; 1]).is_none());
let _ = format!("{server}{peer}");
}
#[test]
fn peer_connection_type_reverse_and_convert() {
let local = PeerConnectionType::LocalGroupPeer {
session_cid: 1,
peer_cid: 2,
};
assert_eq!(local.get_original_session_cid(), 1);
assert_eq!(local.get_original_target_cid(), 2);
let rev = local.reverse();
assert_eq!(rev.get_original_session_cid(), 2);
assert_eq!(rev.get_original_target_cid(), 1);
let ext = PeerConnectionType::ExternalGroupPeer {
session_cid: 1,
interserver_cid: 5,
peer_cid: 2,
};
assert_eq!(ext.reverse().get_original_session_cid(), 2);
let v: VirtualConnectionType = local.into();
assert_eq!(v.try_as_peer_connection(), Some(local));
assert_eq!(VirtualConnectionType::from(ext).get_target_cid(), 2);
let _ = format!("{local}{ext}");
}
#[test]
fn client_connection_type_accessors() {
let server = ClientConnectionType::Server { session_cid: 8 };
let ext = ClientConnectionType::Extended {
session_cid: 8,
interserver_cid: 3,
};
assert_eq!(server.session_cid(), 8);
assert_eq!(server.interserver_cid(), None);
assert_eq!(ext.interserver_cid(), Some(3));
assert_eq!(
VirtualConnectionType::from(server),
VirtualConnectionType::LocalGroupServer { session_cid: 8 }
);
assert!(matches!(
VirtualConnectionType::from(ext),
VirtualConnectionType::ExternalGroupServer { .. }
));
let _ = format!("{server}{ext}");
}
#[test]
fn object_transfer_status_predicates_and_display() {
assert!(ObjectTransferStatus::TransferTick(1, 2, 3.0).is_tick_type());
assert!(ObjectTransferStatus::ReceptionTick(1, 2, 3.0).is_tick_type());
assert!(!ObjectTransferStatus::TransferBeginning.is_tick_type());
assert!(ObjectTransferStatus::TransferComplete.is_finished_type());
assert!(ObjectTransferStatus::ReceptionComplete.is_finished_type());
assert!(ObjectTransferStatus::Fail("x".into()).is_finished_type());
assert!(!ObjectTransferStatus::TransferBeginning.is_finished_type());
for s in [
ObjectTransferStatus::TransferBeginning,
ObjectTransferStatus::TransferTick(1, 4, 2.5),
ObjectTransferStatus::ReceptionTick(2, 4, 1.0),
ObjectTransferStatus::TransferComplete,
ObjectTransferStatus::ReceptionComplete,
ObjectTransferStatus::Fail("boom".into()),
] {
assert!(!format!("{s}").is_empty());
}
}
#[test]
fn virtual_object_metadata_roundtrip_and_security_level() {
let file = VirtualObjectMetadata {
name: "f".into(),
date_created: "now".into(),
author: "a".into(),
plaintext_length: 10,
group_count: 1,
object_id: ObjectId::zero(),
cid: 5,
transfer_type: TransferType::FileTransfer,
};
let bytes = file.serialize();
let back = VirtualObjectMetadata::deserialize_from(&bytes).unwrap();
assert_eq!(back.cid, 5);
assert!(file.get_security_level().is_none());
let revfs = VirtualObjectMetadata {
transfer_type: TransferType::RemoteEncryptedVirtualFilesystem {
virtual_path: PathBuf::from("/v"),
security_level: SecurityLevel::High,
},
..file
};
assert!(matches!(
revfs.get_security_level(),
Some(SecurityLevel::High)
));
assert!(VirtualObjectMetadata::deserialize_from([0u8; 1]).is_none());
}
#[test]
fn object_id_and_message_group_key_helpers() {
assert_eq!(ObjectId::zero().0, 0);
assert_eq!(ObjectId::from(42u128).0, 42);
assert_ne!(ObjectId::random(), ObjectId::random());
assert_eq!(format!("{}", ObjectId::from(7u128)), "7");
let key = MessageGroupKey::new(3, 99);
assert_eq!(key.cid, 3);
assert_eq!(key.mgid, 99);
assert!(!format!("{key}").is_empty());
assert!(!format!("{key:?}").is_empty());
}
#[test]
fn udp_mode_and_defaults() {
assert_eq!(UdpMode::default(), UdpMode::Disabled);
assert!(!UdpMode::variants().is_empty());
assert_eq!(GroupHierarchyMode::default(), GroupHierarchyMode::Flat);
assert_eq!(
MessageGroupOptions::default().group_type,
GroupType::Private
);
}
}