use std::sync::Arc;
use crate::content::list::{read_counted_list, uncounted_list_entries, uncounted_list_entry};
use crate::content::map::{map_entries, map_entry, map_size};
use crate::content::property::{PropertyType, PropertyValue, read_property_value};
use crate::content::provider::SegmentProvider;
use crate::content::template::{ChildNodeArity, Template};
use crate::error::{Error, Result};
use crate::segment::record::RecordIdentifier;
#[derive(Clone, PartialEq, Debug)]
pub struct PropertyState {
pub name: String,
pub property_type: PropertyType,
pub values: PropertyValues,
}
#[derive(Clone, PartialEq, Debug)]
pub enum PropertyValues {
Single(PropertyValue),
Multiple(Vec<PropertyValue>),
}
#[derive(Clone, Copy)]
pub struct NodeState<'provider> {
provider: &'provider dyn SegmentProvider,
record_identifier: RecordIdentifier,
}
impl<'provider> NodeState<'provider> {
#[must_use]
pub fn new(
provider: &'provider dyn SegmentProvider,
record_identifier: RecordIdentifier,
) -> Self {
Self {
provider,
record_identifier,
}
}
#[must_use]
pub fn record_identifier(&self) -> RecordIdentifier {
self.record_identifier
}
pub fn template(&self) -> Result<Arc<Template>> {
let view = self.provider.segment(self.record_identifier.segment)?;
let template_identifier =
view.read_record_identifier(self.record_identifier.record_number, 0, 1)?;
self.provider.template(template_identifier)
}
pub fn stable_identifier(&self) -> Result<String> {
let view = self.provider.segment(self.record_identifier.segment)?;
let slot = view.read_record_identifier(self.record_identifier.record_number, 0, 0)?;
if slot == self.record_identifier {
return Ok(format!("{}:{}", slot.segment, slot.record_number as i32));
}
let target = self.provider.segment(slot.segment)?;
let bytes = target.read_bytes(slot.record_number, 0, 20)?;
let most_significant_bits = u64::from_be_bytes([
bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
]);
let least_significant_bits = u64::from_be_bytes([
bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
]);
let record_number = i32::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]);
let segment = crate::segment::identifier::SegmentIdentifier::new(
most_significant_bits,
least_significant_bits,
);
Ok(format!("{segment}:{record_number}"))
}
pub fn stable_identifier_bytes(&self) -> Result<[u8; 20]> {
let view = self.provider.segment(self.record_identifier.segment)?;
let slot = view.read_record_identifier(self.record_identifier.record_number, 0, 0)?;
if slot == self.record_identifier {
let mut bytes = [0u8; 20];
bytes[0..8].copy_from_slice(&slot.segment.most_significant_bits.to_be_bytes());
bytes[8..16].copy_from_slice(&slot.segment.least_significant_bits.to_be_bytes());
bytes[16..20].copy_from_slice(&slot.record_number.to_be_bytes());
return Ok(bytes);
}
let target = self.provider.segment(slot.segment)?;
let stored = target.read_bytes(slot.record_number, 0, 20)?;
let mut bytes = [0u8; 20];
bytes.copy_from_slice(stored);
Ok(bytes)
}
pub fn child_node_count(&self) -> Result<u64> {
match self.template()?.child_arity {
ChildNodeArity::Zero => Ok(0),
ChildNodeArity::One { .. } => Ok(1),
ChildNodeArity::Many => {
let map_identifier = self.child_map_identifier()?;
map_size(self.provider, map_identifier)
}
}
}
pub fn child_node(&self, name: &str) -> Result<Option<NodeState<'provider>>> {
match &self.template()?.child_arity {
ChildNodeArity::Zero => Ok(None),
ChildNodeArity::One { child_name } => {
if child_name == name {
let child_identifier = self.child_map_identifier()?;
Ok(Some(NodeState::new(self.provider, child_identifier)))
} else {
Ok(None)
}
}
ChildNodeArity::Many => {
let map_identifier = self.child_map_identifier()?;
Ok(map_entry(self.provider, map_identifier, name)?
.map(|child_identifier| NodeState::new(self.provider, child_identifier)))
}
}
}
pub fn child_node_entries(&self) -> Result<Vec<(String, NodeState<'provider>)>> {
match &self.template()?.child_arity {
ChildNodeArity::Zero => Ok(Vec::new()),
ChildNodeArity::One { child_name } => {
let child_identifier = self.child_map_identifier()?;
Ok(vec![(
child_name.clone(),
NodeState::new(self.provider, child_identifier),
)])
}
ChildNodeArity::Many => {
let map_identifier = self.child_map_identifier()?;
Ok(map_entries(self.provider, map_identifier)?
.into_iter()
.map(|entry| (entry.name, NodeState::new(self.provider, entry.value)))
.collect())
}
}
}
pub fn properties(&self) -> Result<Vec<PropertyState>> {
let template = self.template()?;
let mut properties = Vec::with_capacity(2 + template.properties.len());
if let Some(primary_type) = &template.primary_type {
properties.push(PropertyState {
name: "jcr:primaryType".to_owned(),
property_type: PropertyType::Name,
values: PropertyValues::Single(PropertyValue::Name(primary_type.clone())),
});
}
if !template.mixin_types.is_empty() {
properties.push(PropertyState {
name: "jcr:mixinTypes".to_owned(),
property_type: PropertyType::Name,
values: PropertyValues::Multiple(
template
.mixin_types
.iter()
.cloned()
.map(PropertyValue::Name)
.collect(),
),
});
}
for property_index in 0..template.properties.len() {
properties.push(self.property_at(&template, property_index)?);
}
Ok(properties)
}
pub fn stored_properties(&self) -> Result<Vec<PropertyState>> {
let template = self.template()?;
let mut properties = Vec::with_capacity(template.properties.len());
for property_index in 0..template.properties.len() {
properties.push(self.property_at(&template, property_index)?);
}
Ok(properties)
}
pub fn property(&self, name: &str) -> Result<Option<PropertyState>> {
let template = self.template()?;
if name == "jcr:primaryType"
&& let Some(primary_type) = &template.primary_type
{
return Ok(Some(PropertyState {
name: "jcr:primaryType".to_owned(),
property_type: PropertyType::Name,
values: PropertyValues::Single(PropertyValue::Name(primary_type.clone())),
}));
}
if name == "jcr:mixinTypes" && !template.mixin_types.is_empty() {
return Ok(Some(PropertyState {
name: "jcr:mixinTypes".to_owned(),
property_type: PropertyType::Name,
values: PropertyValues::Multiple(
template
.mixin_types
.iter()
.cloned()
.map(PropertyValue::Name)
.collect(),
),
}));
}
match template.property_by_name(name) {
None => Ok(None),
Some((property_index, _)) => Ok(Some(self.property_at(&template, property_index)?)),
}
}
fn property_at(&self, template: &Template, property_index: usize) -> Result<PropertyState> {
let property_template = &template.properties[property_index];
let list_identifier = self.property_list_identifier(template)?;
let value_identifier = uncounted_list_entry(
self.provider,
list_identifier,
template.properties.len() as u64,
property_index as u64,
)?;
let values = if property_template.is_multiple {
let counted = read_counted_list(self.provider, value_identifier)?;
let mut values = Vec::with_capacity(counted.size as usize);
if let Some(body) = counted.body {
for element in uncounted_list_entries(self.provider, body, u64::from(counted.size))?
{
values.push(read_property_value(
self.provider,
element,
property_template.property_type,
)?);
}
}
PropertyValues::Multiple(values)
} else {
PropertyValues::Single(read_property_value(
self.provider,
value_identifier,
property_template.property_type,
)?)
};
Ok(PropertyState {
name: property_template.name.clone(),
property_type: property_template.property_type,
values,
})
}
fn child_map_identifier(&self) -> Result<RecordIdentifier> {
let view = self.provider.segment(self.record_identifier.segment)?;
view.read_record_identifier(self.record_identifier.record_number, 0, 2)
}
fn property_list_identifier(&self, template: &Template) -> Result<RecordIdentifier> {
if template.properties.is_empty() {
return Err(Error::InvalidFormat {
details: format!(
"node {} has no properties and therefore no property value list",
self.record_identifier
),
});
}
let slot = if template.child_arity == ChildNodeArity::Zero {
2
} else {
3
};
let view = self.provider.segment(self.record_identifier.segment)?;
view.read_record_identifier(self.record_identifier.record_number, 0, slot)
}
}
impl std::fmt::Debug for NodeState<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(formatter, "NodeState({})", self.record_identifier)
}
}
#[cfg(test)]
mod tests {
use super::{NodeState, PropertyValues};
use crate::content::property::{PropertyType, PropertyValue};
use crate::content::provider::tests::MemorySegmentProvider;
use crate::content::template::tests::{TemplateArity, template_record};
use crate::content::value::BinaryValue;
use crate::segment::parsed_segment::tests::{data_segment_identifier, synthetic_data_segment};
use crate::segment::record::RecordIdentifier;
fn small_string_record(text: &str) -> Vec<u8> {
let mut bytes = vec![text.len() as u8];
bytes.extend_from_slice(text.as_bytes());
bytes
}
fn identifier_bytes(record_number: u32) -> [u8; 6] {
let mut bytes = [0u8; 6];
bytes[2..6].copy_from_slice(&record_number.to_be_bytes());
bytes
}
fn node_record(
own_record_number: u32,
template_record_number: u32,
extra_slots: &[u32],
) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&identifier_bytes(own_record_number));
bytes.extend_from_slice(&identifier_bytes(template_record_number));
for slot in extra_slots {
bytes.extend_from_slice(&identifier_bytes(*slot));
}
bytes
}
fn provider_with_property_node() -> (MemorySegmentProvider, RecordIdentifier) {
let segment = data_segment_identifier(1);
let mut records: Vec<(u32, u8, Vec<u8>)> = vec![
(1, 4, small_string_record("title")),
(2, 4, small_string_record("tags")),
(3, 4, small_string_record("nt:unstructured")),
(4, 4, small_string_record("Hello")),
(5, 4, small_string_record("a")),
(6, 4, small_string_record("b")),
];
let mut name_bucket = Vec::new();
name_bucket.extend_from_slice(&identifier_bytes(1));
name_bucket.extend_from_slice(&identifier_bytes(2));
records.push((10, 2, name_bucket));
let mut tags_bucket = Vec::new();
tags_bucket.extend_from_slice(&identifier_bytes(5));
tags_bucket.extend_from_slice(&identifier_bytes(6));
records.push((11, 2, tags_bucket));
let mut counted = 2u32.to_be_bytes().to_vec();
counted.extend_from_slice(&identifier_bytes(11));
records.push((12, 3, counted));
let mut value_bucket = Vec::new();
value_bucket.extend_from_slice(&identifier_bytes(4));
value_bucket.extend_from_slice(&identifier_bytes(12));
records.push((13, 2, value_bucket));
records.push((
20,
6,
template_record(Some(3), &[], &TemplateArity::Zero, Some(10), &[1, -1]),
));
records.push((30, 7, node_record(30, 20, &[13])));
let mut provider = MemorySegmentProvider::default();
provider.insert(segment, synthetic_data_segment(&[], &records));
(provider, RecordIdentifier::new(segment, 30))
}
#[test]
fn reads_properties_with_synthesized_type_properties() {
let (provider, node_identifier) = provider_with_property_node();
let node = NodeState::new(&provider, node_identifier);
let properties = node.properties().expect("properties");
let names: Vec<&str> = properties
.iter()
.map(|property| property.name.as_str())
.collect();
assert_eq!(names, ["jcr:primaryType", "title", "tags"]);
let primary_type = node
.property("jcr:primaryType")
.expect("read")
.expect("present");
assert_eq!(
primary_type.values,
PropertyValues::Single(PropertyValue::Name("nt:unstructured".to_owned()))
);
let title = node.property("title").expect("read").expect("present");
assert_eq!(title.property_type, PropertyType::String);
assert_eq!(
title.values,
PropertyValues::Single(PropertyValue::String("Hello".to_owned()))
);
let tags = node.property("tags").expect("read").expect("present");
assert_eq!(
tags.values,
PropertyValues::Multiple(vec![
PropertyValue::String("a".to_owned()),
PropertyValue::String("b".to_owned()),
])
);
assert_eq!(node.property("missing").expect("read"), None);
assert_eq!(node.property("jcr:mixinTypes").expect("read"), None);
assert_eq!(node.child_node_count().expect("count"), 0);
assert!(node.child_node_entries().expect("children").is_empty());
}
#[test]
fn resolves_single_child_nodes() {
let segment = data_segment_identifier(1);
let mut records: Vec<(u32, u8, Vec<u8>)> = vec![
(1, 4, small_string_record("jcr:content")),
(
20,
6,
template_record(None, &[], &TemplateArity::Zero, None, &[]),
),
(
21,
6,
template_record(None, &[], &TemplateArity::One(1), None, &[]),
),
];
records.push((30, 7, node_record(30, 20, &[])));
records.push((31, 7, node_record(31, 21, &[30])));
let mut provider = MemorySegmentProvider::default();
provider.insert(segment, synthetic_data_segment(&[], &records));
let parent = NodeState::new(&provider, RecordIdentifier::new(segment, 31));
assert_eq!(parent.child_node_count().expect("count"), 1);
let child = parent
.child_node("jcr:content")
.expect("read")
.expect("present");
assert_eq!(
child.record_identifier(),
RecordIdentifier::new(segment, 30)
);
assert!(parent.child_node("other").expect("read").is_none());
let entries = parent.child_node_entries().expect("entries");
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].0, "jcr:content");
}
#[test]
fn stable_identifier_of_uncompacted_node_is_its_own_identifier() {
let (provider, node_identifier) = provider_with_property_node();
let node = NodeState::new(&provider, node_identifier);
let stable = node.stable_identifier().expect("stable identifier");
assert_eq!(
stable,
format!(
"{}:{}",
node_identifier.segment, node_identifier.record_number
)
);
}
#[test]
fn reads_binary_properties() {
let segment = data_segment_identifier(1);
let content = vec![1u8, 2, 3];
let mut binary_record = vec![content.len() as u8];
binary_record.extend_from_slice(&content);
let records: Vec<(u32, u8, Vec<u8>)> = vec![
(1, 4, small_string_record("data")),
(4, 4, binary_record),
(
20,
6,
template_record(None, &[], &TemplateArity::Zero, Some(1), &[2]),
),
(30, 7, node_record(30, 20, &[4])),
];
let mut provider = MemorySegmentProvider::default();
provider.insert(segment, synthetic_data_segment(&[], &records));
let node = NodeState::new(&provider, RecordIdentifier::new(segment, 30));
let data = node.property("data").expect("read").expect("present");
assert_eq!(data.property_type, PropertyType::Binary);
assert_eq!(
data.values,
PropertyValues::Single(PropertyValue::Binary(BinaryValue::Inline {
length: 3,
record_identifier: RecordIdentifier::new(segment, 4),
}))
);
}
}