use crate::base::*;
use crate::GlobalStateCategory;
use cyfs_base::*;
use std::borrow::Cow;
use std::{fmt, str::FromStr};
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum RequestGlobalStateRoot {
GlobalRoot(ObjectId),
DecRoot(ObjectId),
}
impl fmt::Display for RequestGlobalStateRoot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self {
Self::GlobalRoot(root) => {
write!(f, "root:{}", root)
}
Self::DecRoot(root) => {
write!(f, "dec-root:{}", root)
}
}
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct RequestGlobalStatePath {
pub global_state_category: Option<GlobalStateCategory>,
pub global_state_root: Option<RequestGlobalStateRoot>,
pub dec_id: Option<ObjectId>,
pub req_path: Option<String>,
}
impl fmt::Display for RequestGlobalStatePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.format_string())
}
}
impl fmt::Debug for RequestGlobalStatePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.format_string())
}
}
impl RequestGlobalStatePath {
pub fn new(dec_id: Option<ObjectId>, req_path: Option<impl Into<String>>) -> Self {
Self {
global_state_category: None,
global_state_root: None,
dec_id,
req_path: req_path.map(|v| v.into()),
}
}
pub fn new_system_dec(req_path: Option<impl Into<String>>) -> Self {
Self::new(Some(cyfs_core::get_system_dec_app().to_owned()), req_path)
}
pub fn set_root(&mut self, root: ObjectId) {
self.global_state_root = Some(RequestGlobalStateRoot::GlobalRoot(root));
}
pub fn set_dec_root(&mut self, dec_root: ObjectId) {
self.global_state_root = Some(RequestGlobalStateRoot::DecRoot(dec_root));
}
pub fn category(&self) -> GlobalStateCategory {
match &self.global_state_category {
Some(v) => *v,
None => GlobalStateCategory::RootState,
}
}
pub fn req_path(&self) -> Cow<str> {
match &self.req_path {
Some(v) => Cow::Borrowed(v.as_str()),
None => Cow::Borrowed("/"),
}
}
pub fn dec<'a>(&'a self, source: &'a RequestSourceInfo) -> &ObjectId {
match &self.dec_id {
Some(id) => id,
None => &source.dec,
}
}
pub fn parse(req_path: &str) -> BuckyResult<Self> {
let segs: Vec<&str> = req_path
.trim_start_matches('/')
.split('/')
.filter(|seg| !seg.is_empty())
.collect();
let mut index = 0;
let global_state_category = if index < segs.len() {
let seg = segs[index];
match seg {
"root-state" => {
index += 1;
Some(GlobalStateCategory::RootState)
}
"local-cache" => {
index += 1;
Some(GlobalStateCategory::LocalCache)
}
_ => None,
}
} else {
None
};
let global_state_root = if index < segs.len() {
let seg = segs[index];
match seg {
"current" => {
index += 1;
None
}
_ if seg.starts_with("root:") => {
index += 1;
let id = seg.strip_prefix("root:").unwrap();
let root = ObjectId::from_str(&id).map_err(|e| {
let msg = format!("invalid req_path's root id: {}, {}", seg, e);
error!("{msg}");
BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
})?;
Some(RequestGlobalStateRoot::GlobalRoot(root))
}
_ if seg.starts_with("dec-root:") => {
index += 1;
let id = seg.strip_prefix("dec-root:").unwrap();
let root = ObjectId::from_str(&id).map_err(|e| {
let msg = format!("invalid req_path's dec root id: {}, {}", seg, e);
error!("{msg}");
BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
})?;
Some(RequestGlobalStateRoot::DecRoot(root))
}
_ => None,
}
} else {
None
};
let dec_id = if index < segs.len() {
let seg = segs[index];
if OBJECT_ID_BASE58_RANGE.contains(&seg.len()) {
let dec_id = ObjectId::from_str(seg).map_err(|e| {
let msg = format!("invalid req_path's dec root id: {}, {}", seg, e);
error!("{msg}");
BuckyError::new(BuckyErrorCode::InvalidFormat, msg)
})?;
index += 1;
Some(dec_id)
} else {
None
}
} else {
None
};
let req_path = if index < segs.len() {
let path = segs[index..].join("/");
Some(format!("/{}/", path))
} else {
None
};
Ok(Self {
global_state_category,
global_state_root,
dec_id,
req_path,
})
}
pub fn format_string(&self) -> String {
let mut segs: Vec<Cow<str>> = vec![];
if let Some(v) = &self.global_state_category {
segs.push(Cow::Borrowed(v.as_str()));
}
if let Some(root) = &self.global_state_root {
segs.push(Cow::Owned(root.to_string()));
}
if let Some(id) = &self.dec_id {
let seg = Cow::Owned(id.to_string());
segs.push(seg);
};
if let Some(path) = &self.req_path {
segs.push(Cow::Borrowed(path.trim_start_matches('/')));
}
format!("/{}", segs.join("/"))
}
pub fn match_target(&self, target: &Self) -> bool {
if self.category() != target.category() {
return false;
}
if self.global_state_root != target.global_state_root {
return false;
}
if self.dec_id != target.dec_id {
return false;
}
target.req_path().starts_with(&*self.req_path())
}
}
impl FromStr for RequestGlobalStatePath {
type Err = BuckyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::parse(s)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test() {
let mut root = RequestGlobalStatePath {
global_state_category: None,
global_state_root: None,
dec_id: None,
req_path: Some("/a/b/".to_owned()),
};
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(root, r);
root.dec_id = Some(ObjectId::default());
let s = root.format_string();
println!("{}", s);
root.global_state_category = Some(GlobalStateCategory::RootState);
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(root, r);
root.global_state_root = Some(RequestGlobalStateRoot::DecRoot(ObjectId::default()));
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(root, r);
root.req_path = None;
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(root, r);
root.req_path = Some("/a/".to_owned());
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(root, r);
root.dec_id = Some(cyfs_core::get_system_dec_app().to_owned());
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(r.dec_id, Some(cyfs_core::get_system_dec_app().to_owned()));
let root = RequestGlobalStatePath {
global_state_category: None,
global_state_root: None,
dec_id: None,
req_path: None,
};
let s = root.format_string();
println!("{}", s);
let r = RequestGlobalStatePath::parse(&s).unwrap();
assert_eq!(root, r);
}
}