use anyhow::{Result, bail};
use super::auth;
const CALENDAR_API: &str = "https://www.googleapis.com/calendar/v3";
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
struct CreateEventRequest {
summary: String,
start: EventDateTimeRequest,
end: EventDateTimeRequest,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<String>,
}
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
struct EventDateTimeRequest {
#[serde(skip_serializing_if = "Option::is_none")]
date_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
date: Option<String>,
}
#[derive(serde::Deserialize)]
struct CreateEventResponse {
id: String,
#[serde(default)]
html_link: String,
}
pub fn run(
summary: &str,
start: &str,
end: &str,
description: Option<&str>,
location: Option<&str>,
account: Option<&str>,
) -> Result<()> {
let token = auth::get_access_token(account)?;
let start_dt = parse_datetime(start)?;
let end_dt = parse_datetime(end)?;
let request = CreateEventRequest {
summary: summary.to_string(),
start: start_dt,
end: end_dt,
description: description.map(|s| s.to_string()),
location: location.map(|s| s.to_string()),
};
let url = format!("{}/calendars/primary/events", CALENDAR_API);
let json_value = serde_json::to_value(&request)?;
let resp = match ureq::post(&url)
.set("Authorization", &format!("Bearer {}", token))
.set("Content-Type", "application/json")
.send_json(json_value)
{
Ok(r) => r,
Err(ureq::Error::Status(401, _)) => {
bail!(
"Calendar API returned 401 Unauthorized.\n\
Try re-authenticating with: corky cal auth"
);
}
Err(ureq::Error::Status(status, resp)) => {
let err_body = resp.into_string().unwrap_or_default();
bail!("Calendar API error (HTTP {}): {}", status, err_body);
}
Err(e) => return Err(e.into()),
};
let created: CreateEventResponse = resp.into_json()?;
println!("Created: {}", summary);
println!(" ID: {}", created.id);
if !created.html_link.is_empty() {
println!(" Link: {}", created.html_link);
}
Ok(())
}
fn parse_datetime(s: &str) -> Result<EventDateTimeRequest> {
if chrono::DateTime::parse_from_rfc3339(s).is_ok() {
return Ok(EventDateTimeRequest {
date_time: Some(s.to_string()),
date: None,
});
}
if chrono::NaiveDate::parse_from_str(s, "%Y-%m-%d").is_ok() {
return Ok(EventDateTimeRequest {
date_time: None,
date: Some(s.to_string()),
});
}
bail!(
"Invalid datetime '{}'. Use RFC 3339 (e.g. 2026-03-10T14:00:00-05:00) \
or YYYY-MM-DD for all-day events.",
s
)
}