use core::fmt::Display;
use core::fmt::Formatter;
use core::fmt::Result as FmtResult;
use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ODataId(String);
impl ODataId {
#[must_use]
pub fn service_root() -> Self {
Self("/redfish/v1".into())
}
#[must_use]
pub fn last_segment(&self) -> Option<&str> {
let path = self.0.trim_end_matches('/');
path.rsplit_once('/')
.map(|(_, v)| v)
.or_else(|| (!path.is_empty()).then_some(path))
}
}
impl From<String> for ODataId {
fn from(s: String) -> Self {
Self(s)
}
}
impl Display for ODataId {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.0.fmt(f)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct ODataETag(String);
impl From<String> for ODataETag {
fn from(value: String) -> Self {
Self(value)
}
}
impl Display for ODataETag {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
self.0.fmt(f)
}
}
pub struct ODataType<'a> {
pub namespace: Vec<&'a str>,
pub type_name: &'a str,
}
impl ODataType<'_> {
#[must_use]
pub fn parse_from(v: &serde_json::Value) -> Option<ODataType<'_>> {
v.get("@odata.type")
.and_then(|v| v.as_str())
.and_then(|v| v.starts_with('#').then_some(&v[1..]))
.and_then(|v| {
let mut all = v.split('.').collect::<Vec<_>>();
all.pop().map(|type_name| ODataType {
namespace: all,
type_name,
})
})
}
}
#[cfg(test)]
mod tests {
use super::ODataId;
#[test]
fn last_segment_returns_last_path_segment() {
let id = ODataId("/redfish/v1/Systems/1".into());
assert_eq!(id.last_segment(), Some("1"));
}
#[test]
fn last_segment_ignores_trailing_slash() {
let id = ODataId("/redfish/v1/Systems/1/".into());
assert_eq!(id.last_segment(), Some("1"));
}
#[test]
fn last_segment_handles_multiple_trailing_slashes() {
let id = ODataId("/redfish/v1/Systems/1///".into());
assert_eq!(id.last_segment(), Some("1"));
}
#[test]
fn last_segment_returns_none_for_empty_string() {
let id = ODataId("".into());
assert_eq!(id.last_segment(), None);
}
#[test]
fn last_segment_returns_none_for_root_path() {
let id = ODataId("/".into());
assert_eq!(id.last_segment(), None);
}
#[test]
fn last_segment_returns_none_for_multiple_root_slashes() {
let id = ODataId("///".into());
assert_eq!(id.last_segment(), None);
}
#[test]
fn last_segment_returns_segment_for_single_component_relative_path() {
let id = ODataId("redfish".into());
assert_eq!(id.last_segment(), Some("redfish"));
}
#[test]
fn last_segment_returns_last_segment_for_relative_path() {
let id = ODataId("redfish/v1/Systems/1".into());
assert_eq!(id.last_segment(), Some("1"));
}
#[test]
fn last_segment_handles_leading_slash_before_single_segment() {
let id = ODataId("/redfish".into());
assert_eq!(id.last_segment(), Some("redfish"));
}
#[test]
fn service_root_last_segment_is_v1() {
assert_eq!(ODataId::service_root().last_segment(), Some("v1"));
}
}