use anyhow::{Context, Result};
use serde_json::Value;
use std::time::Duration;
const HTTP_TIMEOUT: Duration = Duration::from_secs(2);
fn build_request_body(content: &str, palace: Option<&str>, tags: &[String]) -> Value {
let mut obj = serde_json::Map::new();
obj.insert("content".to_string(), Value::String(content.to_string()));
if let Some(p) = palace {
obj.insert("palace".to_string(), Value::String(p.to_string()));
}
if !tags.is_empty() {
obj.insert(
"tags".to_string(),
Value::Array(tags.iter().cloned().map(Value::String).collect()),
);
}
Value::Object(obj)
}
pub async fn handle_note(content: String, palace: Option<String>, tags: Vec<String>) -> Result<()> {
if content.trim().is_empty() {
eprintln!("trusty-memory note: 'content' must not be empty");
std::process::exit(2);
}
let addr = match trusty_common::read_daemon_addr("trusty-memory") {
Ok(Some(a)) => a,
Ok(None) => {
eprintln!(
"trusty-memory note: daemon not running — memory write \
dropped. Start it with `trusty-memory start`."
);
return Ok(());
}
Err(e) => {
eprintln!("trusty-memory note: could not read daemon address: {e:#}");
return Ok(());
}
};
let base = if addr.starts_with("http://") || addr.starts_with("https://") {
addr
} else {
format!("http://{addr}")
};
let url = format!("{base}/api/v1/remember");
let body = build_request_body(&content, palace.as_deref(), &tags);
let client = match reqwest::Client::builder()
.timeout(HTTP_TIMEOUT)
.connect_timeout(HTTP_TIMEOUT)
.build()
.context("build http client")
{
Ok(c) => c,
Err(e) => {
eprintln!("trusty-memory note: could not build http client: {e:#}");
return Ok(());
}
};
match client.post(&url).json(&body).send().await {
Ok(resp) if resp.status().is_success() => {
println!("Queued.");
}
Ok(resp) => {
let status = resp.status();
let text = resp.text().await.unwrap_or_default();
eprintln!(
"trusty-memory note: daemon returned {status}: {text} (memory write dropped)"
);
}
Err(e) => {
eprintln!("trusty-memory note: POST {url} failed: {e:#} (memory write dropped)");
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn note_builds_expected_body() {
let b = build_request_body("hello", None, &[]);
assert_eq!(b["content"], "hello");
assert!(
b.get("palace").is_none(),
"palace must be omitted when None"
);
assert!(b.get("tags").is_none(), "tags must be omitted when empty");
let b = build_request_body(
"facts about quokkas",
Some("wildlife"),
&["marsupials".to_string(), "australia".to_string()],
);
assert_eq!(b["content"], "facts about quokkas");
assert_eq!(b["palace"], "wildlife");
let tags = b["tags"].as_array().expect("tags array");
assert_eq!(tags.len(), 2);
assert_eq!(tags[0], "marsupials");
assert_eq!(tags[1], "australia");
}
}