Skip to content

Commit c92fee7

Browse files
committed
fixes incorrect implementation for root pointers
1 parent 7d09f28 commit c92fee7

File tree

4 files changed

+177
-83
lines changed

4 files changed

+177
-83
lines changed

src/pointer.rs

Lines changed: 80 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,19 @@ impl Pointer {
5959
pub fn new(tokens: &[impl AsRef<str>]) -> Self {
6060
let mut inner = String::new();
6161
for t in tokens.iter().map(Token::new) {
62-
inner.push_str(&prepend(t.encoded()));
63-
}
64-
if inner.is_empty() {
6562
inner.push('/');
63+
inner.push_str(t.encoded());
6664
}
6765
Pointer {
6866
inner,
6967
err: None,
7068
count: tokens.len(),
7169
}
7270
}
71+
/// Extracts a string slice containing the entire encoded `Pointer`.
72+
pub fn as_str(&self) -> &str {
73+
&self.inner
74+
}
7375

7476
/// Returns `true` if the `Pointer` is valid. The only way an invalid
7577
/// `Pointer` can occur is through deserialization.
@@ -82,10 +84,6 @@ impl Pointer {
8284
/// Pushes a `Token` onto the front of this `Pointer`.
8385
pub fn push_front(&mut self, token: Token) {
8486
self.count += 1;
85-
if self.inner == "/" {
86-
self.inner.push_str(token.encoded());
87-
return;
88-
}
8987
self.inner.insert(0, '/');
9088
self.inner.insert_str(1, token.encoded());
9189
}
@@ -100,50 +98,73 @@ impl Pointer {
10098

10199
/// Removes and returns the last `Token` in the `Pointer` if it exists.
102100
pub fn pop_back(&mut self) -> Option<Token> {
103-
if self.inner.len() == 1 {
101+
if self.is_root() {
104102
return None;
105103
}
106104
if self.count > 0 {
107105
self.count -= 1;
108106
}
107+
self.inner[1..]
108+
.rsplit_once('/')
109+
.map_or(Some((&self.inner[1..], "")), Option::Some)
110+
.map(|(f, b)| (f.to_owned(), b.to_owned()))
111+
.map(|(front, back)| {
112+
if !front.is_empty() {
113+
self.inner = String::from("/") + &front;
114+
} else {
115+
self.inner = "".to_owned();
116+
}
117+
Token::from_encoded(back)
118+
})
109119

110-
self.rsplit_once().map(|(front, back)| {
111-
self.inner = prepend(&front);
112-
Token::from_encoded(back)
113-
})
120+
// self.rsplit_once().map(|(front, back)| {
121+
// self.inner = maybe_prepend_slash(&front);
122+
123+
// })
114124
}
115125
/// Removes and returns the first `Token` in the `Pointer` if it exists.
116126
pub fn pop_front(&mut self) -> Option<Token> {
117-
if self.inner.len() == 1 {
127+
if self.inner.is_empty() {
118128
return None;
119129
}
120130
if self.count > 0 {
121131
self.count -= 1;
122132
}
123133

124-
self.split_once().map(|(front, rest)| {
125-
self.inner = prepend(&rest);
126-
front.into()
127-
})
134+
self.inner[1..]
135+
.split_once('/')
136+
.map_or(Some((&self.inner[1..], "")), Option::Some)
137+
.map(|(f, b)| (f.to_owned(), b.to_owned()))
138+
.map(|(front, back)| {
139+
if !back.is_empty() {
140+
self.inner = String::from("/") + &back;
141+
} else {
142+
self.inner = String::new()
143+
}
144+
front.into()
145+
})
128146
}
129147
/// Returns the number of tokens in the `Pointer`.
130148
pub fn count(&self) -> usize {
131149
self.count
132150
}
133-
/// Returns `true` if the JSON Pointer equals `"/"`.
151+
/// Returns `true` if the JSON Pointer equals `""`.
134152
pub fn is_root(&self) -> bool {
135-
self.inner == "/"
153+
self.inner.is_empty()
136154
}
137155

138156
/// Returns the last `Token` in the `Pointer`.
139157
pub fn back(&self) -> Option<Token> {
140-
self.rsplit_once().map(|(front, last)| {
141-
if last.is_empty() {
142-
Token::from_encoded(front)
143-
} else {
144-
Token::from_encoded(last)
145-
}
146-
})
158+
self.inner[1..]
159+
.rsplit_once('/')
160+
.map_or(Some((&self.inner[1..], "")), Option::Some)
161+
.map(|(front, back)| {
162+
if !back.is_empty() {
163+
Token::from_encoded(back)
164+
} else {
165+
Token::from_encoded(front)
166+
}
167+
})
147168
}
148169
/// Returns the last token in the `Pointer`.
149170
///
@@ -153,7 +174,13 @@ impl Pointer {
153174
}
154175
/// Returns the first `Token` in the `Pointer`.
155176
pub fn front(&self) -> Option<Token> {
156-
self.split_once().map(|(front, _)| front.into())
177+
if self.is_root() {
178+
return None;
179+
}
180+
self.inner[1..]
181+
.split_once('/')
182+
.map_or(Some((&self.inner[1..], "")), Option::Some)
183+
.map(|(front, _)| Token::from_encoded(front))
157184
}
158185
/// Returns the first `Token` in the `Pointer`.
159186
///
@@ -202,6 +229,13 @@ impl Pointer {
202229
index: usize,
203230
token: Token,
204231
) -> Result<Option<Token>, ReplaceTokenError> {
232+
if self.is_root() {
233+
return Err(ReplaceTokenError {
234+
count: self.count,
235+
index,
236+
pointer: self.clone(),
237+
});
238+
}
205239
let mut tokens = self.tokens().collect::<Vec<_>>();
206240
if index > tokens.len() {
207241
return Err(ReplaceTokenError {
@@ -211,23 +245,21 @@ impl Pointer {
211245
});
212246
}
213247
let old = tokens.get(index).cloned();
214-
215248
tokens[index] = token;
216249

217-
self.inner = prepend(
218-
&tokens
250+
self.inner = String::from("/")
251+
+ &tokens
219252
.iter()
220-
.map(|t| t.encoded())
253+
.map(Token::encoded)
221254
.collect::<Vec<_>>()
222-
.join("/"),
223-
);
255+
.join("/");
224256
Ok(old)
225257
}
226258

227259
/// Clears the `Pointer`, setting it to root (`"/"`).
228260
pub fn clear(&mut self) {
229261
self.count = 0;
230-
self.inner = prepend("")
262+
self.inner = String::from("");
231263
}
232264

233265
/// Returns an iterator of `Token`s in the `Pointer`.
@@ -772,33 +804,12 @@ impl Pointer {
772804
s.next();
773805
s
774806
}
775-
fn split_once(&self) -> Option<(String, String)> {
776-
if self.is_root() {
777-
None
778-
} else {
779-
self.inner[1..]
780-
.split_once('/')
781-
.map_or(Some((&self.inner[1..], "")), Option::Some)
782-
.map(|(f, b)| (f.to_owned(), b.to_owned()))
783-
}
784-
}
785-
786-
fn rsplit_once(&self) -> Option<(String, String)> {
787-
if self.is_root() {
788-
None
789-
} else {
790-
self.inner[1..]
791-
.rsplit_once('/')
792-
.map_or(Some((&self.inner[1..], "")), Option::Some)
793-
.map(|(f, b)| (f.to_owned(), b.to_owned()))
794-
}
795-
}
796807
}
797808

798809
impl Default for Pointer {
799810
fn default() -> Self {
800811
Self {
801-
inner: "/".to_owned(),
812+
inner: "".to_string(),
802813
err: None,
803814
count: 0,
804815
}
@@ -918,12 +929,12 @@ impl TryFrom<&str> for Pointer {
918929

919930
fn validate_and_format(value: &str) -> Result<(usize, String), MalformedPointerError> {
920931
let mut chars = value.chars();
921-
let mut next = chars.next();
922-
match next {
932+
933+
match chars.next() {
923934
Some('#') => {
924-
next = chars.next();
935+
let next = chars.next();
925936
if next.is_none() {
926-
return Ok((0, "/".into()));
937+
return Ok((0, "".to_string()));
927938
}
928939
if next != Some('/') {
929940
return Err(MalformedPointerError::NoLeadingSlash(value.into()));
@@ -934,24 +945,27 @@ fn validate_and_format(value: &str) -> Result<(usize, String), MalformedPointerE
934945
return Err(MalformedPointerError::NoLeadingSlash(value.into()));
935946
}
936947
None => {
937-
return Ok((0, "/".into()));
948+
return Ok((0, "".to_string()));
938949
}
939950
}
940951
let mut res = String::with_capacity(value.len());
941952
res.push('/');
942953
let mut count = 1; // accounting for the first slash
954+
943955
while let Some(c) = chars.next() {
944956
res.push(c);
945-
if c == '~' {
946-
match chars.next() {
957+
match c {
958+
'~' => match chars.next() {
947959
Some('0') => res.push('0'),
948960
Some('1') => res.push('1'),
949961
_ => {
950962
return Err(MalformedPointerError::InvalidEncoding(value.to_string()));
951963
}
964+
},
965+
'/' => {
966+
count += 1;
952967
}
953-
} else if c == '/' {
954-
count += 1;
968+
_ => {}
955969
}
956970
}
957971
Ok((count, res))
@@ -1006,7 +1020,7 @@ impl FromStr for Pointer {
10061020
}
10071021
}
10081022

1009-
fn prepend(s: &str) -> String {
1023+
fn prepend_slash(s: &str) -> String {
10101024
if !s.starts_with('/') {
10111025
"/".to_string() + s
10121026
} else {

0 commit comments

Comments
 (0)