Skip to content

Commit

Permalink
Show leading slash when formatting entity paths (#4537)
Browse files Browse the repository at this point in the history
### What

![image](https://github.com/rerun-io/rerun/assets/1148717/e1ea3b95-0495-4047-bf69-c4fc080f14e5)

…I also decided to refactor the `EntityPaths` a bit. Sorry.
Best reviewed commit by commit.

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
  * Full build: [app.rerun.io](https://app.rerun.io/pr/4537/index.html)
* Partial build:
[app.rerun.io](https://app.rerun.io/pr/4537/index.html?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
- Useful for quick testing when changes do not affect examples in any
way
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG

- [PR Build Summary](https://build.rerun.io/pr/4537)
- [Docs
preview](https://rerun.io/preview/14fdfa3f9b4b959f765ea0ed81c0c32ca5697bbd/docs)
<!--DOCS-PREVIEW-->
- [Examples
preview](https://rerun.io/preview/14fdfa3f9b4b959f765ea0ed81c0c32ca5697bbd/examples)
<!--EXAMPLES-PREVIEW-->
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)
  • Loading branch information
emilk authored Dec 14, 2023
1 parent 2e99a7b commit 0eaad73
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 213 deletions.
78 changes: 46 additions & 32 deletions crates/re_log_types/src/path/entity_path.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::sync::Arc;

use re_string_interner::InternedString;
use re_types_core::SizeBytes;

use crate::{hash::Hash64, path::entity_path_impl::EntityPathImpl, EntityPathPart};
use crate::{hash::Hash64, EntityPathPart};

// ----------------------------------------------------------------------------

Expand Down Expand Up @@ -99,13 +100,13 @@ pub struct EntityPath {

// [`Arc`] used for cheap cloning, and to keep down the size of [`EntityPath`].
// We mostly use the hash for lookups and comparisons anyway!
path: Arc<EntityPathImpl>,
parts: Arc<Vec<EntityPathPart>>,
}

impl EntityPath {
#[inline]
pub fn root() -> Self {
Self::from(EntityPathImpl::root())
Self::from(vec![])
}

#[inline]
Expand All @@ -125,58 +126,58 @@ impl EntityPath {
/// Treat the string as one opaque string, NOT splitting on any slashes.
///
/// The given string is expected to be unescaped, i.e. any `\` is treated as a normal character.
pub fn from_single_string(string: impl Into<String>) -> Self {
pub fn from_single_string(string: impl Into<InternedString>) -> Self {
Self::new(vec![EntityPathPart::new(string)])
}

#[inline]
pub fn iter(&self) -> impl Iterator<Item = &EntityPathPart> {
self.path.iter()
self.parts.iter()
}

#[inline]
pub fn last(&self) -> Option<&EntityPathPart> {
self.path.last()
self.parts.last()
}

#[inline]
pub fn as_slice(&self) -> &[EntityPathPart] {
self.path.as_slice()
self.parts.as_slice()
}

#[inline]
pub fn to_vec(&self) -> Vec<EntityPathPart> {
self.path.to_vec()
self.parts.to_vec()
}

#[inline]
pub fn is_root(&self) -> bool {
self.path.is_root()
self.parts.is_empty()
}

/// Is this equals to, or a descendant of, the given path.
#[inline]
pub fn starts_with(&self, prefix: &EntityPath) -> bool {
prefix.len() <= self.len() && self.path.iter().zip(prefix.iter()).all(|(a, b)| a == b)
prefix.len() <= self.len() && self.iter().zip(prefix.iter()).all(|(a, b)| a == b)
}

/// Is this a strict descendant of the given path.
#[inline]
pub fn is_descendant_of(&self, other: &EntityPath) -> bool {
other.len() < self.len() && self.path.iter().zip(other.iter()).all(|(a, b)| a == b)
other.len() < self.len() && self.iter().zip(other.iter()).all(|(a, b)| a == b)
}

/// Is this a direct child of the other path.
#[inline]
pub fn is_child_of(&self, other: &EntityPath) -> bool {
other.len() + 1 == self.len() && self.path.iter().zip(other.iter()).all(|(a, b)| a == b)
other.len() + 1 == self.len() && self.iter().zip(other.iter()).all(|(a, b)| a == b)
}

/// Number of parts
#[inline]
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> usize {
self.path.len()
self.parts.len()
}

#[inline]
Expand All @@ -193,7 +194,10 @@ impl EntityPath {
/// Return [`None`] if root.
#[must_use]
pub fn parent(&self) -> Option<Self> {
self.path.parent().map(Self::from)
self.parts
.len()
.checked_sub(1)
.map(|n_minus_1| Self::new(self.parts[..n_minus_1].to_vec()))
}

pub fn join(&self, other: &Self) -> Self {
Expand Down Expand Up @@ -231,27 +235,20 @@ impl FromIterator<EntityPathPart> for EntityPath {
}
}

impl From<EntityPathImpl> for EntityPath {
impl From<Vec<EntityPathPart>> for EntityPath {
#[inline]
fn from(path: EntityPathImpl) -> Self {
fn from(path: Vec<EntityPathPart>) -> Self {
Self {
hash: EntityPathHash(Hash64::hash(&path)),
path: Arc::new(path),
parts: Arc::new(path),
}
}
}

impl From<Vec<EntityPathPart>> for EntityPath {
#[inline]
fn from(path: Vec<EntityPathPart>) -> Self {
Self::from(EntityPathImpl::from(path.iter()))
}
}

impl From<&[EntityPathPart]> for EntityPath {
#[inline]
fn from(path: &[EntityPathPart]) -> Self {
Self::from(EntityPathImpl::from(path.iter()))
Self::from(path.to_vec())
}
}

Expand Down Expand Up @@ -331,7 +328,7 @@ impl Loggable for EntityPath {
re_types_core::datatypes::Utf8::to_arrow(
data.into_iter()
.map(Into::into)
.map(|ent_path| re_types_core::datatypes::Utf8(ent_path.path.to_string().into())),
.map(|ent_path| re_types_core::datatypes::Utf8(ent_path.to_string().into())),
)
}

Expand All @@ -351,15 +348,16 @@ impl Loggable for EntityPath {
impl serde::Serialize for EntityPath {
#[inline]
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.path.serialize(serializer)
self.parts.serialize(serializer)
}
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for EntityPath {
#[inline]
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
EntityPathImpl::deserialize(deserializer).map(Self::from)
let parts = Vec::<EntityPathPart>::deserialize(deserializer)?;
Ok(Self::new(parts))
}
}

Expand All @@ -384,31 +382,47 @@ impl nohash_hasher::IsEnabled for EntityPath {}
// ----------------------------------------------------------------------------

impl std::cmp::Ord for EntityPath {
#[inline]
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.path.cmp(&other.path)
self.parts.cmp(&other.parts)
}
}

impl std::cmp::PartialOrd for EntityPath {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.path.cmp(&other.path))
Some(self.parts.cmp(&other.parts))
}
}

// ----------------------------------------------------------------------------

impl std::fmt::Debug for EntityPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.path.fmt(f)
// Same as `Display` - since we always prefix paths with a slash, they are easily recognizable.
write!(f, "{self}")
}
}

impl std::fmt::Display for EntityPath {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.path.fmt(f)
use std::fmt::Write as _;

if self.is_root() {
f.write_char('/')
} else {
// We always lead with a slash
for comp in self.iter() {
f.write_char('/')?;
comp.fmt(f)?;
}
Ok(())
}
}
}

// ----------------------------------------------------------------------------

#[cfg(test)]
mod tests {
use super::*;
Expand Down
108 changes: 0 additions & 108 deletions crates/re_log_types/src/path/entity_path_impl.rs

This file was deleted.

Loading

0 comments on commit 0eaad73

Please sign in to comment.