use super::cache::*;
use super::iterator::IntoObjectMapContentItem;
use super::object_map::*;
use crate::*;
use std::collections::VecDeque;
pub struct ObjectMapPathContentItem {
pub path: String,
pub value: ObjectMapContentItem,
pub content_type: ObjectMapSimpleContentType,
}
impl std::fmt::Debug for ObjectMapPathContentItem {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:?} item: {}={}",
self.content_type, self.path, self.value
)
}
}
#[derive(Debug)]
pub struct ObjectMapPathContentList {
pub list: Vec<ObjectMapPathContentItem>,
}
impl std::fmt::Display for ObjectMapPathContentList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "path content list: {:?}", self.list)
}
}
impl ObjectMapPathContentList {
pub fn new(capacity: usize) -> Self {
Self {
list: Vec::with_capacity(capacity),
}
}
}
struct PathIteratorSeg {
path: String,
it: ObjectMapBindIterator,
}
#[derive(Clone, Debug)]
pub struct ObjectMapPathIteratorOption {
pub leaf_object: bool,
pub mid_object: bool,
}
impl Default for ObjectMapPathIteratorOption {
fn default() -> Self {
Self {
leaf_object: true,
mid_object: false,
}
}
}
impl ObjectMapPathIteratorOption {
pub fn new(leaf_object: bool, mid_object: bool) -> Self {
Self {
leaf_object,
mid_object,
}
}
}
pub struct ObjectMapPathIterator {
cache: ObjectMapOpEnvCacheRef,
stack: VecDeque<PathIteratorSeg>,
opt: ObjectMapPathIteratorOption,
}
impl ObjectMapPathIterator {
pub async fn new(target: ObjectMapRef, cache: ObjectMapOpEnvCacheRef, opt: ObjectMapPathIteratorOption) -> Self {
let mut ret = Self {
cache,
stack: VecDeque::new(),
opt,
};
let seg = PathIteratorSeg {
path: "/".to_owned(),
it: ObjectMapBindIterator::new_with_target(target, ret.cache.clone()).await,
};
ret.stack.push_front(seg);
ret
}
fn join_path(parent: &str, key: &str) -> String {
if parent.ends_with("/") {
format!("{}{}", parent, key)
} else {
format!("{}/{}", parent, key)
}
}
pub fn is_end(&self) -> bool {
self.stack.is_empty()
}
pub async fn next(&mut self, step: usize) -> BuckyResult<ObjectMapPathContentList> {
assert!(step > 0);
let mut result = ObjectMapPathContentList::new(step);
let mut remaining = step;
loop {
if self.stack.is_empty() {
break;
}
let ret;
let path;
{
let current = self.stack.back_mut().unwrap();
path = current.path.clone();
ret = current.it.next(remaining).await?;
}
if ret.list.len() < remaining {
self.stack.pop_back();
}
for item in ret.list {
match item {
ObjectMapContentItem::Map((key, value)) => {
match value.obj_type_code() {
ObjectTypeCode::ObjectMap => {
let next_path = Self::join_path(&path, &key);
let mut got = self.opt.mid_object;
if let Err(e) = self.pending_objectmap(&next_path, &value).await {
warn!("iterator over objectmap error, now will treat as normal object! path={}, key={}, value={}, {}", path, key, value, e);
got = self.opt.leaf_object;
}
if got {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(Some(&key)),
content_type: ObjectMapSimpleContentType::Map,
};
result.list.push(item);
remaining -= 1;
}
}
_ => {
if self.opt.leaf_object {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(Some(&key)),
content_type: ObjectMapSimpleContentType::Map,
};
result.list.push(item);
remaining -= 1;
}
}
}
}
ObjectMapContentItem::DiffMap((key, value)) => {
match &value.diff {
Some(diff) => {
let next_path = Self::join_path(&path, &key);
let mut got = self.opt.mid_object;
if let Err(e) = self.pending_objectmap(&next_path, &diff).await {
warn!("iterator over objectmap diff error, now will treat as normal diff object! path={}, key={}, value={}, {}", path, key, value, e);
got = self.opt.leaf_object;
}
if got {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(Some(&key)),
content_type: ObjectMapSimpleContentType::DiffMap,
};
result.list.push(item);
remaining -= 1;
}
}
None => {
if self.opt.leaf_object {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(Some(&key)),
content_type: ObjectMapSimpleContentType::DiffMap,
};
result.list.push(item);
remaining -= 1;
}
}
}
}
ObjectMapContentItem::Set(value) => {
match value.obj_type_code() {
ObjectTypeCode::ObjectMap => {
let mut got = self.opt.mid_object;
if let Err(e) = self.pending_objectmap(&path, &value).await
{
warn!("iterator over objectmap error, now will treat as normal object! path={}, value={}, {}", path, value, e);
got = self.opt.leaf_object;
}
if got {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(None),
content_type: ObjectMapSimpleContentType::Set,
};
result.list.push(item);
remaining -= 1;
}
}
_ => {
if self.opt.leaf_object {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(None),
content_type: ObjectMapSimpleContentType::Set,
};
result.list.push(item);
remaining -= 1;
}
}
}
}
ObjectMapContentItem::DiffSet(value) => {
if self.opt.leaf_object {
let item = ObjectMapPathContentItem {
path: path.clone(),
value: value.into_content(None),
content_type: ObjectMapSimpleContentType::DiffSet,
};
result.list.push(item);
remaining -= 1;
}
}
}
}
if remaining == 0 {
assert_eq!(result.list.len(), step);
break;
}
}
Ok(result)
}
async fn pending_objectmap(&mut self, path: &str, value: &ObjectId) -> BuckyResult<()> {
let target = self.cache.get_object_map(value).await?;
if target.is_none() {
let msg = format!(
"iterator with sub objectmap but not found! path={}, objmap={}",
path, value
);
error!("{}", msg);
return Err(BuckyError::new(BuckyErrorCode::NotFound, msg));
}
let target = target.unwrap();
let it = ObjectMapBindIterator::new_with_target(target, self.cache.clone()).await;
let pending_seg = PathIteratorSeg {
path: path.to_owned(),
it,
};
self.stack.push_front(pending_seg);
Ok(())
}
}
#[cfg(test)]
mod test {
use super::super::cache::*;
use super::super::path::*;
use super::*;
use std::str::FromStr;
async fn gen_path(path: &ObjectMapPath) -> ObjectId {
let x1_value = ObjectId::from_str("95RvaS5anntyAoRUBi48vQoivWzX95M8xm4rkB93DdSt").unwrap();
let x1_value2 = ObjectId::from_str("95RvaS5aZKKM8ghTYmsTyhSEWD4pAmALoUSJx1yNxSx5").unwrap();
info!(
"typecode: {:?}, {:?}",
x1_value.obj_type_code(),
x1_value2.obj_type_code()
);
for i in 0..100 {
let key = format!("/a/b/{}", i);
path.insert_with_path(&key, &x1_value).await.unwrap();
}
for i in 0..100 {
let key = format!("/a/c/{}", i);
path.insert_with_path(&key, &x1_value).await.unwrap();
}
for i in 0..10 {
let key = format!("/a/b_{}", i);
path.insert_with_path(&key, &x1_value2).await.unwrap();
}
for i in 0..10 {
let key = format!("/a_{}", i);
path.insert_with_path(&key, &x1_value2).await.unwrap();
}
path.root()
}
async fn test_path_iterator() {
let noc = ObjectMapMemoryNOCCache::new();
let root_cache = ObjectMapRootMemoryCache::new_default_ref(None, noc);
let cache = ObjectMapOpEnvMemoryCache::new_ref(root_cache.clone());
let owner = ObjectId::default();
let root = ObjectMap::new(
ObjectMapSimpleContentType::Map,
Some(owner.clone()),
Some(owner.clone()),
)
.no_create_time()
.build();
let root_id = root.flush_id();
cache.put_object_map(&root_id, root, None).unwrap();
info!("new root: {}", root_id);
let path = ObjectMapPath::new(root_id.clone(), cache.clone(), true);
let path_root = gen_path(&path).await;
info!("generated path root: {}", path_root);
let root = cache.get_object_map(&path_root).await.unwrap();
let mut it = ObjectMapPathIterator::new(root.unwrap(), cache, ObjectMapPathIteratorOption::default()).await;
while !it.is_end() {
let list = it.next(1).await.unwrap();
info!("list: {} {:?}", 1, list.list);
}
}
#[test]
fn test() {
crate::init_simple_log("test-object-map-path-iterator", Some("debug"));
async_std::task::block_on(async move {
test_path_iterator().await;
});
}
}