Save back markdown file to server
This commit is contained in:
parent
1510eb4f3d
commit
e1b6017f06
|
@ -1,26 +1,32 @@
|
|||
use reqwest::Method;
|
||||
|
||||
// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
use {
|
||||
crate::api_client::ApiClient,
|
||||
crate::recipe::{Ingredient, Recipe},
|
||||
anyhow::Result,
|
||||
anyhow::{anyhow, Result},
|
||||
chrono::{Duration, Local},
|
||||
icalendar::Component,
|
||||
regex::Regex,
|
||||
reqwest::StatusCode,
|
||||
std::collections::HashSet,
|
||||
std::ops::Range,
|
||||
};
|
||||
|
||||
pub async fn with(api_client: &ApiClient, calendar_name: &str, days: u32) -> Result<()> {
|
||||
pub async fn with(
|
||||
api_client: &ApiClient,
|
||||
calendar_name: &str,
|
||||
location: &str,
|
||||
days: u32,
|
||||
) -> Result<()> {
|
||||
let ids = map_events_to_recipe_ids(api_client, calendar_name, days).await?;
|
||||
let ingredients = get_ingredients(api_client, ids).await?;
|
||||
let ingredients = merge_ingredients(ingredients);
|
||||
let md = prepare_grocery_list(&ingredients)?;
|
||||
|
||||
println!("{}", md);
|
||||
|
||||
// save_grocery_list(api_client, "", md).await?;
|
||||
// TODO filename is hardcoded for now
|
||||
save_grocery_list(api_client, location, &md).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -89,10 +95,63 @@ fn prepare_grocery_list(ingredients: &Vec<Ingredient>) -> Result<String> {
|
|||
"# Grocery list - {}",
|
||||
chrono::Local::now().format("%Y-%m-%d").to_string()
|
||||
)?;
|
||||
writeln!(out)?; // leave an empty line
|
||||
for ingredient in ingredients {
|
||||
let ingredient = ingredient.0.as_str();
|
||||
writeln!(out, "* {}", ingredient)?;
|
||||
writeln!(out, "- [ ] {}", ingredient)?;
|
||||
}
|
||||
|
||||
Ok(out)
|
||||
}
|
||||
|
||||
async fn save_grocery_list(api_client: &ApiClient, filename: &str, contents: &str) -> Result<()> {
|
||||
let dav_base_url = api_client
|
||||
.base_url()
|
||||
.join(&format!("remote.php/dav/files/{}/", api_client.username()))?;
|
||||
|
||||
let filename_components = filename.split('/').collect::<Vec<_>>();
|
||||
filename_components
|
||||
.iter()
|
||||
.take(filename_components.len() - 1)
|
||||
.fold(Ok(dav_base_url.clone()), |url, dir| {
|
||||
url.map(|u| u.join(&format!("{dir}/")).unwrap())
|
||||
.and_then(|url| {
|
||||
futures::executor::block_on(async {
|
||||
let response = api_client
|
||||
.rest()
|
||||
.request(Method::from_bytes(b"MKCOL").unwrap(), url.clone())
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match response.map(|r| r.status()) {
|
||||
Ok(StatusCode::OK)
|
||||
| Ok(StatusCode::METHOD_NOT_ALLOWED /* already exists */) => Ok(url),
|
||||
Ok(status) => Err(anyhow!(
|
||||
"Could not create WebDAV collection {}, server responded with {}",
|
||||
&url,
|
||||
status
|
||||
)),
|
||||
Err(e) => Err(anyhow!(e)),
|
||||
}
|
||||
})
|
||||
})
|
||||
})?;
|
||||
|
||||
let file_url = dav_base_url.join(filename).unwrap();
|
||||
let response = api_client
|
||||
.rest()
|
||||
.put(file_url.clone())
|
||||
.header("Content-Type", "text/markdown; charset=utf-8")
|
||||
.body(contents.to_owned())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
match response.status() {
|
||||
StatusCode::CREATED | StatusCode::NO_CONTENT => Ok(()),
|
||||
status => Err(anyhow!(
|
||||
"Cannot save grocery list at {}, server responded with status {}",
|
||||
file_url,
|
||||
status
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,7 +45,8 @@ fn setup_args() -> ArgMatches {
|
|||
Command::new("groceries")
|
||||
.about("Create a grocery list from scheduled calendar events")
|
||||
.arg(server_arg.clone())
|
||||
.arg(arg!(<calendar_name> ""))
|
||||
.arg(arg!(<calendar_name> "The name of the calendar to read from for scheduled recipes"))
|
||||
.arg(arg!(<filename> "The relative path for the Markdown file that will be saved on the server. Recommended ending it in '.md'."))
|
||||
.arg(arg!(-d --days <days> "Number of days in the future to consider")
|
||||
.value_parser(clap::builder::RangedU64ValueParser::<u32>::new().range(1..))
|
||||
.required(false)
|
||||
|
@ -94,7 +95,8 @@ async fn parse_args(args: &ArgMatches) -> Result<()> {
|
|||
.get_one::<String>("calendar_name")
|
||||
.expect("<calendar_name> is a mandatory parameter, it cannot be missing");
|
||||
let days = sub_matches.get_one::<u32>("days").unwrap();
|
||||
commands::groceries::with(&api_client, calendar_name.as_str(), *days).await
|
||||
let filename = sub_matches.get_one::<String>("filename").unwrap();
|
||||
commands::groceries::with(&api_client, calendar_name.as_str(), filename, *days).await
|
||||
}
|
||||
Some(("schedule", sub_matches)) => {
|
||||
let api_client = get_api_client(&sub_matches, &configuration)?;
|
||||
|
|
Loading…
Reference in New Issue