use ahash::AHashMap;
use serde_json::Value;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct StringTokens {
segments: Vec<String>,
aliases: AHashMap<String, usize>,
}
impl StringTokens {
pub fn parse_custom(raw: &str, separators: &[char]) -> Self {
let segments = raw
.split(|c| separators.contains(&c))
.filter(|s| !s.is_empty())
.map(|s| s.to_ascii_lowercase())
.collect();
Self {
segments,
aliases: AHashMap::new(),
}
}
pub fn parse(raw: &str) -> Self {
let separators = [':', '.', '/', '-'];
let segments = raw
.split(|c| separators.contains(&c))
.filter(|s| !s.is_empty())
.map(|s| s.to_ascii_lowercase())
.collect();
Self {
segments,
aliases: AHashMap::new(),
}
}
pub fn set_alias(&mut self, alias: String, index: usize) {
self.aliases.insert(alias, index);
}
pub fn by_alias(&self, alias: &str) -> Option<&str> {
if let Some(index) = self.aliases.get(alias) {
return self.segments.get(*index).map(|s| s.as_str());
}
None
}
pub fn get(&self, index: usize) -> Option<&str> {
self.segments.get(index).map(|s| s.as_str())
}
pub fn segments(&self) -> &[String] {
&self.segments
}
pub fn into_segments(self) -> Vec<String> {
self.segments
}
pub fn select_json<'a>(&self, value: &'a Value) -> Option<&'a Value> {
let mut current = value;
for segment in self.segments.iter() {
match current {
Value::Object(map) => {
current = map.get(segment)?;
}
Value::Array(arr) => {
let idx: usize = segment.parse().ok()?;
current = arr.get(idx)?;
}
_ => return None,
}
}
Some(current)
}
pub fn insert_json(&self, target: &mut Value, new_value: Value) -> bool {
if self.segments.is_empty() {
*target = new_value;
return true;
}
let mut current = target;
for (i, segment) in self.segments.iter().enumerate() {
let is_last = i + 1 == self.segments.len();
if let Ok(idx) = segment.parse::<usize>() {
if !current.is_array() {
*current = Value::Array(Vec::new());
}
let arr = current.as_array_mut().unwrap();
if idx >= arr.len() {
arr.resize(idx + 1, Value::Null);
}
if is_last {
arr[idx] = new_value;
return true;
} else {
let next_is_index = self
.segments
.get(i + 1)
.and_then(|s| s.parse::<usize>().ok())
.is_some();
if arr[idx].is_null() || !(arr[idx].is_object() || arr[idx].is_array()) {
arr[idx] = if next_is_index {
Value::Array(Vec::new())
} else {
Value::Object(serde_json::Map::new())
};
}
current = &mut arr[idx];
}
} else {
if !current.is_object() {
*current = Value::Object(serde_json::Map::new());
}
let map = current.as_object_mut().unwrap();
if is_last {
map.insert(segment.clone(), new_value);
return true;
} else {
let next_is_index = self
.segments
.get(i + 1)
.and_then(|s| s.parse::<usize>().ok())
.is_some();
let entry = map.entry(segment.clone()).or_insert_with(|| {
if next_is_index {
Value::Array(Vec::new())
} else {
Value::Object(serde_json::Map::new())
}
});
current = entry;
}
}
}
true
}
pub fn contains(&self, needle: &str) -> bool {
self.segments.iter().any(|s| s == needle)
}
pub fn contains_any(&self, needles: &[&str]) -> bool {
self.segments.iter().any(|s| needles.contains(&s.as_str()))
}
pub fn index_of(&self, needle: &str) -> Option<usize> {
self.segments.iter().position(|s| s == needle)
}
pub fn index_of_any(&self, needles: &[&str]) -> Option<usize> {
self.segments.iter().position(|s| needles.contains(&s.as_str()))
}
}