const POINTER_SHAPE: &str = "`extract.pointer` for INI must be `/key` for an unsectioned key or `/section/key` for a named section";
pub(crate) fn validate_pointer(pointer: &str) -> Result<(), String> {
pointer_parts(pointer).ok_or(POINTER_SHAPE)?;
crate::config::validate_json_pointer(pointer)
}
pub(crate) fn select(document: &str, pointer: &str) -> Result<String, String> {
let document = ini::Ini::load_from_str_noescape(document).map_err(|error| {
format!(
"stored value is not valid INI at line {}, column {}: {}",
error.line, error.col, error.msg
)
})?;
pointer_parts(pointer)
.and_then(|(section, key)| document.get_from(section, &key))
.map(str::to_owned)
.ok_or_else(|| format!("INI pointer '{pointer}' did not match the stored document"))
}
fn pointer_parts(pointer: &str) -> Option<(Option<String>, String)> {
let path = pointer.strip_prefix('/')?;
let (section, key) = match path.split_once('/') {
Some((section, key)) => (Some(section), key),
None => (None, path),
};
if section == Some("") || key.is_empty() || key.contains('/') {
return None;
}
Some((section.map(unescape_segment), unescape_segment(key)))
}
fn unescape_segment(segment: &str) -> String {
segment.replace("~1", "/").replace("~0", "~")
}