use std::fmt;
use bytes::Bytes;
use crate::model::common::location::Location;
use crate::model::error::ParseError;
use crate::model::extension_header::object_extension::ObjectExtension;
use super::constant::{ObjectForwardingPreference, ObjectStatus};
use super::datagram::Datagram;
use super::fetch_object::FetchObjectPayload;
use super::subgroup_object::SubgroupObject;
#[derive(Clone, PartialEq)]
pub struct Object {
pub track_alias: u64,
pub location: Location,
pub publisher_priority: u8,
pub forwarding_preference: ObjectForwardingPreference,
pub subgroup_id: Option<u64>,
pub status: ObjectStatus,
pub extensions: Option<Vec<ObjectExtension>>,
pub payload: Option<Bytes>,
}
impl fmt::Debug for Object {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Object")
.field("track_alias", &self.track_alias)
.field("location", &self.location)
.field("publisher_priority", &self.publisher_priority)
.field("forwarding_preference", &self.forwarding_preference)
.field("subgroup_id", &self.subgroup_id)
.field("status", &self.status)
.field("extensions", &self.extensions)
.field("payload_length", &self.payload.as_ref().map(|p| p.len()))
.finish()
}
}
impl Object {
pub fn try_from_datagram(
datagram: Datagram,
default_priority: u8,
) -> Result<(Self, bool), ParseError> {
let end_of_group = datagram.end_of_group;
let priority = datagram.publisher_priority.unwrap_or(default_priority);
let (status, payload) = if let Some(object_status) = datagram.object_status {
(object_status, None)
} else {
(ObjectStatus::Normal, datagram.payload)
};
Ok((
Object {
track_alias: datagram.track_alias,
location: Location {
group: datagram.group_id,
object: datagram.object_id,
},
publisher_priority: priority,
forwarding_preference: ObjectForwardingPreference::Datagram,
subgroup_id: None,
status,
extensions: datagram.extension_headers,
payload,
},
end_of_group,
))
}
pub fn try_from_subgroup(
subgroup_obj: SubgroupObject,
track_alias: u64, group_id: u64, subgroup_id: Option<u64>, publisher_priority: Option<u8>, ) -> Result<Self, ParseError> {
Ok(Object {
track_alias,
location: Location {
group: group_id,
object: subgroup_obj.object_id,
},
publisher_priority: publisher_priority.unwrap_or(0), forwarding_preference: ObjectForwardingPreference::Subgroup,
subgroup_id,
status: subgroup_obj.object_status.unwrap_or(ObjectStatus::Normal),
extensions: subgroup_obj.extension_headers,
payload: subgroup_obj.payload,
})
}
pub fn try_from_fetch(
fetch_obj: FetchObjectPayload,
track_alias: u64,
) -> Result<Self, ParseError> {
let subgroup_id = match fetch_obj.forwarding_preference {
ObjectForwardingPreference::Subgroup => Some(fetch_obj.subgroup_id),
ObjectForwardingPreference::Datagram => None,
};
let payload = if fetch_obj.payload.is_empty() {
None
} else {
Some(fetch_obj.payload)
};
Ok(Object {
track_alias,
location: Location {
group: fetch_obj.group_id,
object: fetch_obj.object_id,
},
publisher_priority: fetch_obj.publisher_priority,
forwarding_preference: fetch_obj.forwarding_preference,
subgroup_id,
status: ObjectStatus::Normal,
extensions: fetch_obj.extension_headers,
payload,
})
}
}
impl Object {
pub fn try_into_datagram(
self,
track_alias: u64,
end_of_group: bool,
default_priority: Option<u8>,
) -> Result<Datagram, ParseError> {
if self.forwarding_preference != ObjectForwardingPreference::Datagram {
return Err(ParseError::CastingError {
context: "Object::try_into_datagram(forwarding_preference)",
from_type: "Object",
to_type: "Datagram",
details: "Forwarding preference must be Datagram".to_string(),
});
}
let publisher_priority =
if default_priority.is_some() && default_priority == Some(self.publisher_priority) {
None
} else {
Some(self.publisher_priority)
};
if self.status != ObjectStatus::Normal {
if self.payload.is_some() {
return Err(ParseError::CastingError {
context: "Object::try_into_datagram(payload)",
from_type: "Object",
to_type: "Datagram",
details: "Payload must be None for non-Normal status".to_string(),
});
}
Ok(Datagram::new_status(
track_alias,
self.location.group,
self.location.object,
publisher_priority,
self.extensions,
self.status,
))
} else {
let payload = self.payload.ok_or(ParseError::CastingError {
context: "Object::try_into_datagram(payload)",
from_type: "Object",
to_type: "Datagram",
details: "Payload cannot be empty".to_string(),
})?;
Ok(Datagram::new_payload(
track_alias,
self.location.group,
self.location.object,
publisher_priority,
self.extensions,
payload,
end_of_group,
))
}
}
pub fn try_into_subgroup(self) -> Result<SubgroupObject, ParseError> {
if self.forwarding_preference != ObjectForwardingPreference::Subgroup {
return Err(ParseError::CastingError {
context: "Object::try_into_subgroup(forwarding_preference)",
from_type: "Object",
to_type: "SubgroupObject",
details: "Forwarding preference must be Subgroup".to_string(),
});
}
let _subgroup_id = self.subgroup_id.ok_or(ParseError::CastingError {
context: "Object::try_into_subgroup(subgroup_id)",
from_type: "Object",
to_type: "SubgroupObject",
details: "Subgroup ID must be present for Subgroup forwarding".to_string(),
})?;
let (payload, object_status) = match self.status {
ObjectStatus::Normal => (
Some(self.payload.ok_or(ParseError::CastingError {
context: "Object::try_into_subgroup(payload)",
from_type: "Object",
to_type: "SubgroupObject",
details: "Payload must be present for Normal status".to_string(),
})?),
None,
),
other_status => {
if self.payload.is_some() {
return Err(ParseError::CastingError {
context: "Object::try_into_subgroup(payload)",
from_type: "Object",
to_type: "SubgroupObject",
details: "Payload must be None for non-Normal status".to_string(),
});
}
(None, Some(other_status))
}
};
Ok(SubgroupObject {
object_id: self.location.object,
extension_headers: self.extensions,
object_status,
payload,
})
}
pub fn try_into_fetch(self) -> Result<FetchObjectPayload, ParseError> {
if self.status != ObjectStatus::Normal {
return Err(ParseError::CastingError {
context: "Object::try_into_fetch(status)",
from_type: "Object",
to_type: "FetchObjectPayload",
details:
"non-Normal status cannot be serialized as a fetch payload; use EndOfRange instead"
.to_string(),
});
}
let payload = self.payload.unwrap_or_default();
let subgroup_id = match self.forwarding_preference {
ObjectForwardingPreference::Subgroup => self.subgroup_id.ok_or(ParseError::CastingError {
context: "Object::try_into_fetch(subgroup_id)",
from_type: "Object",
to_type: "FetchObjectPayload",
details: "Subgroup ID must be present for Subgroup forwarding".to_string(),
})?,
ObjectForwardingPreference::Datagram => {
if self.subgroup_id.is_some() {
return Err(ParseError::CastingError {
context: "Object::try_into_fetch(subgroup_id)",
from_type: "Object",
to_type: "FetchObjectPayload",
details: "Subgroup ID must not be present for Datagram forwarding".to_string(),
});
}
self.location.object
}
};
Ok(FetchObjectPayload {
group_id: self.location.group,
subgroup_id,
object_id: self.location.object,
publisher_priority: self.publisher_priority,
forwarding_preference: self.forwarding_preference,
extension_headers: self.extensions,
payload,
})
}
}