125 lines
3.2 KiB
Rust
125 lines
3.2 KiB
Rust
use crate::{
|
|
cuddle_state::Cuddle,
|
|
state::{
|
|
validated_project::{Project, Value},
|
|
ValidatedState,
|
|
},
|
|
};
|
|
|
|
pub struct GetCommand {
|
|
query_engine: ProjectQueryEngine,
|
|
}
|
|
|
|
impl GetCommand {
|
|
pub fn new(cuddle: Cuddle<ValidatedState>) -> Self {
|
|
Self {
|
|
query_engine: ProjectQueryEngine::new(
|
|
&cuddle
|
|
.state
|
|
.project
|
|
.expect("we should always have a project if get command is available"),
|
|
),
|
|
}
|
|
}
|
|
|
|
pub async fn execute(&self, query: &str) -> anyhow::Result<String> {
|
|
let res = self
|
|
.query_engine
|
|
.query(query)?
|
|
.ok_or(anyhow::anyhow!("query was not found in project"))?;
|
|
|
|
match res {
|
|
Value::String(s) => Ok(s),
|
|
Value::Bool(b) => Ok(b.to_string()),
|
|
Value::Array(value) => {
|
|
let val = serde_json::to_string_pretty(&value)?;
|
|
Ok(val)
|
|
}
|
|
Value::Map(value) => {
|
|
let val = serde_json::to_string_pretty(&value)?;
|
|
Ok(val)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn description() -> String {
|
|
"get returns a given variable from the project given a key, following a jq like schema (.project.name, etc.)"
|
|
.into()
|
|
}
|
|
}
|
|
|
|
pub struct ProjectQueryEngine {
|
|
project: Project,
|
|
}
|
|
|
|
impl ProjectQueryEngine {
|
|
pub fn new(project: &Project) -> Self {
|
|
Self {
|
|
project: project.clone(),
|
|
}
|
|
}
|
|
|
|
pub fn query(&self, query: &str) -> anyhow::Result<Option<Value>> {
|
|
let parts = query
|
|
.split('.')
|
|
.filter(|i| !i.is_empty())
|
|
.collect::<Vec<&str>>();
|
|
|
|
Ok(self.traverse(&parts, &self.project.value))
|
|
}
|
|
|
|
fn traverse(&self, query: &[&str], value: &Value) -> Option<Value> {
|
|
match query.split_first() {
|
|
Some((key, rest)) => match value {
|
|
Value::Map(items) => {
|
|
let item = items.get(*key)?;
|
|
|
|
self.traverse(rest, item)
|
|
}
|
|
_ => {
|
|
tracing::warn!(
|
|
"key: {} doesn't have a corresponding value: {:?}",
|
|
key,
|
|
value
|
|
);
|
|
None
|
|
}
|
|
},
|
|
None => Some(value.clone()),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use std::path::PathBuf;
|
|
|
|
use super::*;
|
|
|
|
#[tokio::test]
|
|
async fn test_can_query_item() -> anyhow::Result<()> {
|
|
let project = ProjectQueryEngine::new(&Project {
|
|
value: Value::Map(
|
|
[(
|
|
String::from("project"),
|
|
Value::Map(
|
|
[(
|
|
String::from("name"),
|
|
Value::String(String::from("something")),
|
|
)]
|
|
.into(),
|
|
),
|
|
)]
|
|
.into(),
|
|
),
|
|
root: PathBuf::new(),
|
|
});
|
|
|
|
let res = project.query(".project.name")?;
|
|
|
|
assert_eq!(Some(Value::String("something".into())), res);
|
|
|
|
Ok(())
|
|
}
|
|
}
|