1#![allow(dead_code)]
2
3use std::borrow::Cow;
4use std::ops::{Index, Range};
5use std::fmt::{self, Debug};
6
7use pear::input::Length;
8
9use crate::ext::IntoOwned;
10
11pub use pear::input::Extent;
12
13pub type IndexedStr<'a> = Indexed<'a, str>;
14pub type IndexedBytes<'a> = Indexed<'a, [u8]>;
15
16pub trait AsPtr {
17 fn as_ptr(&self) -> *const u8;
18}
19
20impl AsPtr for str {
21 #[inline(always)]
22 fn as_ptr(&self) -> *const u8 {
23 str::as_ptr(self)
24 }
25}
26
27impl AsPtr for [u8] {
28 #[inline(always)]
29 fn as_ptr(&self) -> *const u8 {
30 <[u8]>::as_ptr(self)
31 }
32}
33
34#[derive(PartialEq)]
36pub enum Indexed<'a, T: ?Sized + ToOwned> {
37 Indexed(usize, usize),
39 Concrete(Cow<'a, T>)
41}
42
43impl<A, T: ?Sized + ToOwned> From<Extent<A>> for Indexed<'_, T> {
44 fn from(e: Extent<A>) -> Self {
45 Indexed::Indexed(e.start, e.end)
46 }
47}
48
49impl<'a, T: ?Sized + ToOwned + 'a> From<Cow<'a, T>> for Indexed<'a, T> {
50 #[inline(always)]
51 fn from(value: Cow<'a, T>) -> Indexed<'a, T> {
52 Indexed::Concrete(value)
53 }
54}
55
56impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T> {
57 #[inline(always)]
59 pub fn indices(self) -> (usize, usize) {
60 match self {
61 Indexed::Indexed(a, b) => (a, b),
62 _ => panic!("cannot convert indexed T to U unless indexed")
63 }
64 }
65
66 #[inline(always)]
68 pub fn coerce<U: ?Sized + ToOwned>(self) -> Indexed<'a, U> {
69 match self {
70 Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
71 _ => panic!("cannot convert indexed T to U unless indexed")
72 }
73 }
74
75 #[inline(always)]
77 pub fn coerce_lifetime<'b>(self) -> Indexed<'b, T> {
78 match self {
79 Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
80 _ => panic!("cannot coerce lifetime unless indexed")
81 }
82 }
83}
84
85impl<T: 'static + ?Sized + ToOwned> IntoOwned for Indexed<'_, T> {
86 type Owned = Indexed<'static, T>;
87
88 fn into_owned(self) -> Indexed<'static, T> {
89 match self {
90 Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
91 Indexed::Concrete(cow) => Indexed::Concrete(IntoOwned::into_owned(cow))
92 }
93 }
94}
95
96use std::ops::Add;
97
98impl<'a, T: ?Sized + ToOwned + 'a> Add for Indexed<'a, T> {
99 type Output = Indexed<'a, T>;
100
101 #[inline]
102 fn add(self, other: Indexed<'a, T>) -> Indexed<'a, T> {
103 match self {
104 Indexed::Indexed(a, b) => match other {
105 Indexed::Indexed(c, d) if b == c && a < d => Indexed::Indexed(a, d),
106 _ => panic!("+ requires indexed")
107 }
108 _ => panic!("+ requires indexed")
109 }
110 }
111}
112
113impl<'a, T: ?Sized + ToOwned + 'a> Indexed<'a, T>
114 where T: Length + AsPtr + Index<Range<usize>, Output = T>
115{
116 pub fn checked_from(needle: &T, haystack: &T) -> Option<Indexed<'a, T>> {
119 let needle_start = needle.as_ptr() as usize;
120 let haystack_start = haystack.as_ptr() as usize;
121 if needle_start < haystack_start {
122 return None;
123 }
124
125 let needle_end = needle_start + needle.len();
126 let haystack_end = haystack_start + haystack.len();
127 if needle_end > haystack_end {
128 return None;
129 }
130
131 let start = needle_start - haystack_start;
132 let end = start + needle.len();
133 Some(Indexed::Indexed(start, end))
134 }
135
136 pub unsafe fn unchecked_from(needle: &T, haystack: &T) -> Indexed<'a, T> {
144 let haystack_start = haystack.as_ptr() as usize;
145 let needle_start = needle.as_ptr() as usize;
146
147 let start = needle_start - haystack_start;
148 let end = start + needle.len();
149 Indexed::Indexed(start, end)
150 }
151
152 #[inline]
154 pub fn is_indexed(&self) -> bool {
155 match *self {
156 Indexed::Indexed(..) => true,
157 Indexed::Concrete(..) => false,
158 }
159 }
160
161 #[inline]
163 pub fn is_empty(&self) -> bool {
164 self.len() == 0
165 }
166
167 pub fn into_concrete(self, source: &Option<Cow<'_, T>>) -> Cow<'a, T> {
173 if self.is_indexed() && source.is_none() {
174 panic!("cannot concretize indexed str to str without base string!")
175 }
176
177 match self {
178 Indexed::Indexed(i, j) => Cow::Owned(source.as_ref().unwrap()[i..j].to_owned()),
179 Indexed::Concrete(string) => string,
180 }
181 }
182
183 pub fn from_cow_source<'s>(&'s self, source: &'s Option<Cow<'_, T>>) -> &'s T {
191 if self.is_indexed() && source.is_none() {
192 panic!("cannot convert indexed str to str without base string!")
193 }
194
195 match *self {
196 Indexed::Indexed(i, j) => &source.as_ref().unwrap()[i..j],
197 Indexed::Concrete(ref mstr) => mstr.as_ref(),
198 }
199 }
200
201 pub fn from_source<'s>(&'s self, source: Option<&'s T>) -> &'s T {
209 if self.is_indexed() && source.is_none() {
210 panic!("Cannot convert indexed str to str without base string!")
211 }
212
213 match *self {
214 Indexed::Indexed(i, j) => &source.unwrap()[(i as usize)..(j as usize)],
215 Indexed::Concrete(ref mstr) => &*mstr,
216 }
217 }
218}
219
220impl<'a, T: ToOwned + ?Sized + 'a> Clone for Indexed<'a, T> {
221 fn clone(&self) -> Self {
222 match *self {
223 Indexed::Indexed(a, b) => Indexed::Indexed(a, b),
224 Indexed::Concrete(ref cow) => Indexed::Concrete(cow.clone())
225 }
226 }
227}
228
229impl<'a, T: ?Sized + 'a> Debug for Indexed<'a, T>
230 where T: ToOwned + Debug, T::Owned: Debug
231{
232 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
233 match *self {
234 Indexed::Indexed(a, b) => fmt::Debug::fmt(&(a, b), f),
235 Indexed::Concrete(ref cow) => fmt::Debug::fmt(cow, f),
236 }
237 }
238}
239
240impl<'a, T: ?Sized + Length + ToOwned + 'a> Length for Indexed<'a, T> {
241 #[inline(always)]
242 fn len(&self) -> usize {
243 match *self {
244 Indexed::Indexed(a, b) => (b - a) as usize,
245 Indexed::Concrete(ref cow) => cow.len()
246 }
247 }
248}