use std::{process::ExitCode, sync::Arc};
use tokio::{sync::Semaphore, task::JoinSet};
use typesafe_sdk::{Client, Error, Noul, PreparedQuestions, Questions};
const MAX_IN_FLIGHT: usize = 4;
const STATES: [&str; 8] = [
"My invoice shows the wrong VAT number.",
"How do I change my password?",
"I was charged twice for one order.",
"The app crashes when I open settings.",
"Can I get a refund for last month?",
"Your product is great, thanks!",
"Where can I download my receipts?",
"The export button does nothing.",
];
#[tokio::main]
async fn main() -> ExitCode {
match run().await {
Ok(0) => ExitCode::SUCCESS,
Ok(_) => ExitCode::FAILURE,
Err(error) => {
eprintln!("error: {error}");
ExitCode::FAILURE
}
}
}
async fn run() -> Result<usize, Error> {
let client = Client::from_env()?;
client.warm_up().await?;
let questions: Arc<PreparedQuestions> = Arc::new(
Questions::new()
.noul("billing", Noul::new().instructions("Is this about billing?"))
.prepare()?,
);
let permits = Arc::new(Semaphore::new(MAX_IN_FLIGHT));
let mut tasks = JoinSet::new();
for (index, state) in STATES.into_iter().enumerate() {
let client = client.clone();
let questions = Arc::clone(&questions);
let permits = Arc::clone(&permits);
tasks.spawn(async move {
let _permit = permits.acquire_owned().await.expect("invariant: never closed");
let answer = client.system_one(state, &questions).send().await;
(index, answer.map(|response| response.answers().noul("billing").map(|a| a.noul())))
});
}
let mut failed = 0;
while let Some(joined) = tasks.join_next().await {
match joined {
Ok((index, Ok(billing))) => println!("{index}: billing {billing:?}"),
Ok((index, Err(error))) => {
failed += 1;
eprintln!("{index}: {error}");
}
Err(join_error) => {
failed += 1;
eprintln!("a task did not finish: {join_error}");
}
}
}
Ok(failed)
}