use alfrusco::{config, AsyncRunnable, Item, Workflow, WorkflowError}; use clap::Parser; use serde::{Deserialize, Serialize}; #[derive(Parser, Debug)] struct RandomUserWorkflow { pub keyword: Vec, #[arg(short, long, env)] pub name: Option, } #[tokio::main] pub async fn main() { env_logger::init(); let command = RandomUserWorkflow::parse(); alfrusco::execute_async(&config::AlfredEnvProvider, command, &mut std::io::stdout()).await; } #[async_trait::async_trait] impl AsyncRunnable for RandomUserWorkflow { type Error = RandomUserError; async fn run_async(self, wf: &mut Workflow) -> Result<(), RandomUserError> { if let Some(name) = self.name { wf.append_item(Item::new(format!("NAME DEFINED AS: '{}'", name))); return Ok(()); } let query = self.keyword.join(" "); wf.set_filter_keyword(query.clone()); let url = "https://randomuser.me/api/?inc=gender,name&results=50&seed=alfrusco"; let response = reqwest::get(url).await?; let response: RandomUserResponse = response.json().await?; wf.append_items( response .results .into_iter() .map(|r| { let title = format!("{} {} {}", r.name.title, r.name.first, r.name.last); Item::new(&title) .valid(false) .autocomplete("workflow:nonsense") .var("NAME", title) }) .collect(), ); Ok(()) } } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RandomUserResponse { pub results: Vec, pub info: serde_json::Value, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RandomUser { pub name: RandomUserName, } #[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct RandomUserName { pub title: String, pub first: String, pub last: String, } #[derive(Debug)] pub enum RandomUserError { Reqwest(reqwest::Error), Json(serde_json::Error), } impl From for RandomUserError { fn from(e: reqwest::Error) -> Self { Self::Reqwest(e) } } impl From for RandomUserError { fn from(e: serde_json::Error) -> Self { Self::Json(e) } } impl WorkflowError for RandomUserError {} impl std::fmt::Display for RandomUserError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { RandomUserError::Reqwest(e) => write!(f, "Reqwest error: {}", e), RandomUserError::Json(e) => write!(f, "JSON error: {}", e), } } } impl std::error::Error for RandomUserError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { RandomUserError::Reqwest(e) => Some(e), RandomUserError::Json(e) => Some(e), } } } #[cfg(test)] mod tests { use super::*; #[tokio::test] async fn test_random_user_response() { let command = RandomUserWorkflow { keyword: vec![], name: None, }; let mut buffer = Vec::new(); let dir = tempfile::tempdir().unwrap().into_path(); alfrusco::execute_async(&config::TestingProvider(dir), command, &mut buffer).await; let output = String::from_utf8(buffer).unwrap(); assert!(output.contains("\"title\":\"Mr Fletcher Hall\"")); } }