this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 463 lines 14 kB view raw
1//! Different kinds of [`Tag`]s that can be parsed. 2 3use crate::error::ResolveError; 4use crate::label::DefaultLabel; 5use crate::label::Label; 6#[cfg(doc)] 7use crate::storage::Storage; 8use crate::storage::StorageLock; 9#[cfg(doc)] 10use crate::TagManager; 11#[cfg(feature = "either")] 12use either::Either; 13use itertools::intersperse_with; 14use std::fmt::Display; 15use std::fmt::Formatter; 16use std::fmt::Result as FmtResult; 17use std::hash::BuildHasher; 18use std::marker::PhantomData; 19use string_interner::backend::Backend as InternerBackend; 20use string_interner::DefaultSymbol; 21#[cfg(doc)] 22use string_interner::StringInterner; 23use string_interner::Symbol; 24 25/// A trait defining a [`Tag`] which contains interned data. 26/// 27/// The _only_ defining operation of a [`Tag`] is that it can be 28/// converted back into a [`String`] using the [`Storage`] that 29/// created it and the correct separator configured by the [`TagManager`] 30/// that built it. 31/// 32/// [`Tag`]s have an underlying [`Symbol`] used to define their storage. 33/// Internally, [`Tag`]s are just a set of [`Symbol`]s used to make 34/// storage and identity comparison cheap while enabling reconstruction 35/// of the original [`String`]. 36pub trait Tag { 37 /// The label of the [`TagManager`] used to produce the [`Tag`]. 38 type Label: Label; 39 40 /// The [`Symbol`] used by the [`Storage`] as a handle to the stored string data. 41 type Symbol: Symbol; 42 43 /// Get the [`TagKind`] of the current tag. 44 fn kind(&self) -> TagKind; 45 46 /// Try to resolve a [`Tag`] back into a [`String`]. 47 fn resolve<B, H>( 48 &self, 49 storage: &StorageLock<'_, Self::Label, B, H>, 50 key_value_separator: KeyValueSep, 51 path_separator: PathSep, 52 ) -> Result<String, ResolveError> 53 where 54 B: InternerBackend<Symbol = Self::Symbol>, 55 H: BuildHasher; 56} 57 58#[cfg(feature = "either")] 59// Auto-impl for `Either` wrapping two `Tag`s. 60impl<L, S, T1, T2> Tag for Either<T1, T2> 61where 62 L: Label, 63 S: Symbol, 64 T1: Tag<Label = L, Symbol = S>, 65 T2: Tag<Label = L, Symbol = S>, 66{ 67 type Label = L; 68 type Symbol = S; 69 70 fn resolve<B, H>( 71 &self, 72 storage: &StorageLock<'_, Self::Label, B, H>, 73 key_value_separator: KeyValueSep, 74 path_separator: PathSep, 75 ) -> Result<String, ResolveError> 76 where 77 B: InternerBackend<Symbol = Self::Symbol>, 78 H: BuildHasher, 79 { 80 match self { 81 Either::Left(t) => t.resolve(storage, key_value_separator, path_separator), 82 Either::Right(t) => t.resolve(storage, key_value_separator, path_separator), 83 } 84 } 85 86 fn kind(&self) -> TagKind { 87 match self { 88 Either::Left(t) => t.kind(), 89 Either::Right(t) => t.kind(), 90 } 91 } 92} 93 94//--------------------------------------------------------------------------- 95 96/// A [`Tag`] without internal structure. 97/// 98/// [`PlainTag`] interns the full contents of a tag together. 99#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 100pub struct PlainTag<L = DefaultLabel, S = DefaultSymbol>(S, PhantomData<L>) 101where 102 L: Label, 103 S: Symbol; 104 105impl<L: Label, S: Symbol> PlainTag<L, S> { 106 /// Construct a new [`PlainTag`]. 107 pub(crate) fn new<B, H>(storage: &mut StorageLock<'_, L, B, H>, raw: &str) -> Self 108 where 109 B: InternerBackend<Symbol = S>, 110 H: BuildHasher, 111 { 112 PlainTag(storage.get_or_intern(raw), PhantomData) 113 } 114 115 /// Resolve the whole tag into a [`String`]. 116 pub fn resolve<B, H>(&self, storage: &StorageLock<'_, L, B, H>) -> Result<String, ResolveError> 117 where 118 B: InternerBackend<Symbol = S>, 119 H: BuildHasher, 120 { 121 self.resolve_str(storage).map(ToString::to_string) 122 } 123 124 /// Resolve the whole tag into a string slice. 125 /// 126 /// Note that the returned string slice is a view into the underlying interner 127 /// data, which means you're holding a borrow on the interner as long as the slice 128 /// is held. If you want to let go of the borrow, copy the slice into a new owned 129 /// string. 130 pub fn resolve_str<'intern, B, H>( 131 &self, 132 storage: &'intern StorageLock<'_, L, B, H>, 133 ) -> Result<&'intern str, ResolveError> 134 where 135 B: InternerBackend<Symbol = S>, 136 H: BuildHasher, 137 { 138 storage.resolve(self.0).ok_or(ResolveError::TagNotFound) 139 } 140} 141 142impl<L: Label, S: Symbol> Tag for PlainTag<L, S> { 143 type Label = L; 144 type Symbol = S; 145 146 fn resolve<B, H>( 147 &self, 148 storage: &StorageLock<'_, Self::Label, B, H>, 149 _key_value_separator: KeyValueSep, 150 _path_separator: PathSep, 151 ) -> Result<String, ResolveError> 152 where 153 B: InternerBackend<Symbol = Self::Symbol>, 154 H: BuildHasher, 155 { 156 self.resolve(storage) 157 } 158 159 fn kind(&self) -> TagKind { 160 TagKind::Plain 161 } 162} 163 164//--------------------------------------------------------------------------- 165 166/// A [`Tag`] composed of a key and a value. 167/// 168/// [`KeyValueTag`] interns the key and value separately, on the expectation 169/// that keys especially will be frequently repeated across tags. 170#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 171pub struct KeyValueTag<L = DefaultLabel, S = DefaultSymbol>(S, S, PhantomData<L>) 172where 173 L: Label, 174 S: Symbol; 175 176impl<L: Label, S: Symbol> KeyValueTag<L, S> { 177 /// Construct a new [`KeyValueTag`]. 178 pub(crate) fn new<B, H>(storage: &mut StorageLock<'_, L, B, H>, key: &str, value: &str) -> Self 179 where 180 B: InternerBackend<Symbol = S>, 181 H: BuildHasher, 182 { 183 KeyValueTag( 184 storage.get_or_intern(key), 185 storage.get_or_intern(value), 186 PhantomData, 187 ) 188 } 189 190 /// Resolve the whole tag into a [`String`]. 191 pub fn resolve<B, H>( 192 &self, 193 storage: &StorageLock<'_, L, B, H>, 194 key_value_separator: KeyValueSep, 195 _path_separator: PathSep, 196 ) -> Result<String, ResolveError> 197 where 198 B: InternerBackend<Symbol = S>, 199 H: BuildHasher, 200 { 201 self.resolve_key_value(storage) 202 .map(|(key, value)| format!("{key}{key_value_separator}{value}")) 203 } 204 205 /// Resolve the key and value parts of the tag separately. 206 /// 207 /// Note that the returned string slices are views into the underlying interner 208 /// data, which means you're holding a borrow on the interner as long as the slices 209 /// are held. If you want to let go of the borrow, copy the slices into new owned 210 /// strings. 211 pub fn resolve_key_value<'intern, B, H>( 212 &self, 213 storage: &'intern StorageLock<'_, L, B, H>, 214 ) -> Result<(&'intern str, &'intern str), ResolveError> 215 where 216 B: InternerBackend<Symbol = S>, 217 H: BuildHasher, 218 { 219 let (key, value) = self.try_resolve_key_value(storage); 220 Ok((key?, value?)) 221 } 222 223 /// Try to resolve the key and value parts of the tag separately. 224 /// 225 /// This lets you resolve partial tags, if for some reason part of the tag 226 /// resolves and the other doesn't. 227 /// 228 /// Note that the returned string slices are views into the underlying interner 229 /// data, which means you're holding a borrow on the interner as long as the slices 230 /// are held. If you want to let go of the borrow, copy the slices into new owned 231 /// strings. 232 pub fn try_resolve_key_value<'intern, B, H>( 233 &self, 234 storage: &'intern StorageLock<'_, L, B, H>, 235 ) -> ( 236 Result<&'intern str, ResolveError>, 237 Result<&'intern str, ResolveError>, 238 ) 239 where 240 B: InternerBackend<Symbol = S>, 241 H: BuildHasher, 242 { 243 ( 244 storage.resolve(self.0).ok_or(ResolveError::KeyNotFound), 245 storage.resolve(self.1).ok_or(ResolveError::ValueNotFound), 246 ) 247 } 248} 249 250impl<L: Label, S: Symbol> Tag for KeyValueTag<L, S> { 251 type Label = L; 252 type Symbol = S; 253 254 fn resolve<B, H>( 255 &self, 256 storage: &StorageLock<'_, Self::Label, B, H>, 257 key_value_separator: KeyValueSep, 258 path_separator: PathSep, 259 ) -> Result<String, ResolveError> 260 where 261 B: InternerBackend<Symbol = Self::Symbol>, 262 H: BuildHasher, 263 { 264 self.resolve(storage, key_value_separator, path_separator) 265 } 266 267 fn kind(&self) -> TagKind { 268 TagKind::KeyValue 269 } 270} 271 272//--------------------------------------------------------------------------- 273 274/// A [`Tag`] composed of arbitrary parts. 275/// 276/// [`MultipartTag`] interns each part of the tag separately, on the 277/// expectation that individual parts will be frequently repeated. 278#[derive(Debug, Clone, PartialEq, Eq, Hash)] 279pub struct MultipartTag<L = DefaultLabel, S = DefaultSymbol>(Vec<S>, PhantomData<L>) 280where 281 L: Label, 282 S: Symbol; 283 284impl<L: Label, S: Symbol> MultipartTag<L, S> { 285 /// Construct a new [`MultipartTag`]. 286 pub(crate) fn new<'part, I, B, H>(storage: &mut StorageLock<'_, L, B, H>, parts: I) -> Self 287 where 288 I: Iterator<Item = &'part str>, 289 B: InternerBackend<Symbol = S>, 290 H: BuildHasher, 291 { 292 MultipartTag( 293 parts.map(|part| storage.get_or_intern(part)).collect(), 294 PhantomData, 295 ) 296 } 297 298 /// Resolve the whole tag into a [`String`]. 299 pub fn resolve<B, H>( 300 &self, 301 storage: &StorageLock<'_, L, B, H>, 302 _key_value_separator: KeyValueSep, 303 path_separator: PathSep, 304 ) -> Result<String, ResolveError> 305 where 306 B: InternerBackend<Symbol = S>, 307 H: BuildHasher, 308 { 309 intersperse_with(self.try_resolve_parts(storage), || Ok(path_separator.0)).try_fold( 310 String::new(), 311 |mut acc, res| { 312 res.map(|next| { 313 acc.push_str(next); 314 acc 315 }) 316 }, 317 ) 318 } 319 320 /// Resolve each part of the tag. 321 /// 322 /// Note that the returned string slices are views into the underlying interner 323 /// data, which means you're holding a borrow on the interner as long as the slices 324 /// are held. If you want to let go of the borrow, copy the slices into new owned 325 /// strings. 326 pub fn resolve_parts<'intern, B, H, C>( 327 &self, 328 storage: &'intern StorageLock<'_, L, B, H>, 329 ) -> Result<C, ResolveError> 330 where 331 B: InternerBackend<Symbol = S>, 332 H: BuildHasher, 333 C: FromIterator<&'intern str>, 334 { 335 self.try_resolve_parts(storage).collect() 336 } 337 338 /// Try to resolve each part of the tag. 339 /// 340 /// This lets you partially resolve the tag, if for some reason individual 341 /// parts don't resolve. 342 /// 343 /// Note that the returned string slices are views into the underlying interner 344 /// data, which means you're holding a borrow on the interner as long as the slices 345 /// are held. If you want to let go of the borrow, copy the slices into new owned 346 /// strings. 347 pub fn try_resolve_parts<'s, 'intern: 's, B, H>( 348 &'s self, 349 storage: &'intern StorageLock<'_, L, B, H>, 350 ) -> impl Iterator<Item = Result<&'intern str, ResolveError>> + 's 351 where 352 B: InternerBackend<Symbol = S>, 353 H: BuildHasher, 354 { 355 self.0 356 .iter() 357 .copied() 358 .map(|part| storage.resolve(part).ok_or(ResolveError::PartNotFound)) 359 } 360} 361 362impl<L: Label, S: Symbol> Tag for MultipartTag<L, S> { 363 type Label = L; 364 type Symbol = S; 365 366 fn resolve<B, H>( 367 &self, 368 storage: &StorageLock<'_, Self::Label, B, H>, 369 key_value_separator: KeyValueSep, 370 path_separator: PathSep, 371 ) -> Result<String, ResolveError> 372 where 373 B: InternerBackend<Symbol = Self::Symbol>, 374 H: BuildHasher, 375 { 376 self.resolve(storage, key_value_separator, path_separator) 377 } 378 379 fn kind(&self) -> TagKind { 380 TagKind::Multipart 381 } 382} 383 384/// The separator between keys and values in a key-value tag. 385/// 386/// The default separator is `":"`. 387#[derive(Debug, Copy, Clone)] 388pub struct KeyValueSep(pub &'static str); 389 390impl Display for KeyValueSep { 391 fn fmt(&self, f: &mut Formatter) -> FmtResult { 392 write!(f, "{}", self.0) 393 } 394} 395 396impl Default for KeyValueSep { 397 fn default() -> Self { 398 KeyValueSep(":") 399 } 400} 401 402/// The separator between path segments in a multipart tag. 403/// 404/// The default separator is `"/"`. 405#[derive(Debug, Copy, Clone)] 406pub struct PathSep(pub &'static str); 407 408impl Display for PathSep { 409 fn fmt(&self, f: &mut Formatter) -> FmtResult { 410 write!(f, "{}", self.0) 411 } 412} 413 414impl Default for PathSep { 415 fn default() -> Self { 416 PathSep("/") 417 } 418} 419 420/// The kind of tag being worked with. 421#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] 422pub enum TagKind { 423 /// A [`PlainTag`]. 424 Plain, 425 426 /// A [`KeyValueTag`]. 427 KeyValue, 428 429 /// A [`MultipartTag`]. 430 Multipart, 431 432 /// A type of [`Tag`] not otherwise known. 433 Other, 434} 435 436/// A trait to implement on types that _carry_ [`Tag`]s. 437/// 438/// This trait is generic over the tag type, to permit implementing 439/// it for multiple types of tags. 440pub trait Tagged<T: Tag> { 441 /// The type of iterator used to provide the [`Tag`]s. 442 /// 443 /// The lifetime bounds indicate that the tagged type and the 444 /// tags it produces need to outlive the references to those tags 445 /// returned by the tag iterator. 446 type TagIter<'item>: Iterator<Item = &'item T> 447 where 448 Self: 'item, 449 T: 'item; 450 451 /// Get if the tagged type has tags. 452 /// 453 /// This is included in the API because there's not a good way to get 454 /// the number of elements out of an iterator without using `count()`, 455 /// which consumes the iterator. 456 /// 457 /// `size_hint` unfortunately is `None` for the upper bound by default, 458 /// so it is frequently not useful. 459 fn has_tags(&self) -> bool; 460 461 /// Get the tags of the tagged type. 462 fn get_tags(&self) -> Self::TagIter<'_>; 463}