use std::str::FromStr;
pub(crate) const LONG_HELP: &str = "
Accepts RFC6901 (JSON Pointer), RFC9535 (JSONPath), or JMESPath syntax.
If the expression starts with '/', it is a JSON Pointer.
If the expression starts with '~', _the rest_ is a JMESPath expression.
Otherwise, it is a JSONPath expression.
";
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
#[error(transparent)]
#[remain::sorted]
pub(crate) enum Error {
ExactlyOne(#[from] serde_json_path::ExactlyOneError),
Jmespath(#[from] jmespath::JmespathError),
Json(#[from] serde_json::Error),
JsonPath(#[from] serde_json_path::ParseError),
JsonPointer(#[from] jsonptr::ParseError),
Resolve(#[from] jsonptr::resolve::ResolveError),
}
#[derive(Clone, serde::Deserialize, serde::Serialize)]
pub(crate) enum Query {
#[serde(skip)]
JMESPath(jmespath::Expression<'static>),
#[serde(rename = "jsonptr")]
RFC6901(jsonptr::PointerBuf),
#[serde(rename = "jsonpath")]
RFC9535(serde_json_path::JsonPath),
}
impl Query {
pub(crate) fn evaluate<'value>(&self, value: &serde_json::Value) -> Result<serde_json::Value, Error> {
match self {
Self::RFC6901(jsonptr) => Ok(jsonptr.resolve(value)?.to_owned()),
Self::RFC9535(jsonpath) => Ok(jsonpath.query(value).exactly_one()?.to_owned()),
Self::JMESPath(jmespath) => Ok(serde_json::to_value(jmespath.search(value)?)?),
}
}
}
impl FromStr for Query {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let query = if s.is_empty() || s.starts_with('/') {
Self::RFC6901(s.parse()?)
} else {
match s.strip_prefix("~") {
Some(expression) => Self::JMESPath(jmespath::compile(expression)?),
None => Self::RFC9535(s.parse()?),
}
};
Ok(query)
}
}