Run cargo fmt on all Rust source files
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -79,7 +79,10 @@ async fn upload_asset(
|
||||
if !is_allowed_content_type(&content_type) {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("Unsupported file type: {}. Only images are allowed.", content_type),
|
||||
format!(
|
||||
"Unsupported file type: {}. Only images are allowed.",
|
||||
content_type
|
||||
),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
@@ -100,7 +103,10 @@ async fn upload_asset(
|
||||
if data.len() > MAX_FILE_SIZE {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("File too large. Maximum size is {} MB.", MAX_FILE_SIZE / 1024 / 1024),
|
||||
format!(
|
||||
"File too large. Maximum size is {} MB.",
|
||||
MAX_FILE_SIZE / 1024 / 1024
|
||||
),
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
@@ -149,7 +155,11 @@ async fn upload_asset(
|
||||
|
||||
/// Validate that a path component doesn't contain directory traversal
|
||||
fn validate_path_component(component: &str) -> Result<(), String> {
|
||||
if component.contains("..") || component.contains('/') || component.contains('\\') || component.is_empty() {
|
||||
if component.contains("..")
|
||||
|| component.contains('/')
|
||||
|| component.contains('\\')
|
||||
|| component.is_empty()
|
||||
{
|
||||
return Err("Invalid path component".to_string());
|
||||
}
|
||||
Ok(())
|
||||
@@ -199,12 +209,7 @@ async fn get_asset(Path((project, filename)): Path<(String, String)>) -> impl In
|
||||
let stream = ReaderStream::new(file);
|
||||
let body = Body::from_stream(stream);
|
||||
|
||||
(
|
||||
StatusCode::OK,
|
||||
[(header::CONTENT_TYPE, content_type)],
|
||||
body,
|
||||
)
|
||||
.into_response()
|
||||
(StatusCode::OK, [(header::CONTENT_TYPE, content_type)], body).into_response()
|
||||
}
|
||||
|
||||
fn is_allowed_content_type(content_type: &str) -> bool {
|
||||
@@ -220,11 +225,7 @@ fn is_allowed_content_type(content_type: &str) -> bool {
|
||||
}
|
||||
|
||||
fn get_content_type(filename: &str) -> &'static str {
|
||||
let ext = filename
|
||||
.rsplit('.')
|
||||
.next()
|
||||
.unwrap_or("")
|
||||
.to_lowercase();
|
||||
let ext = filename.rsplit('.').next().unwrap_or("").to_lowercase();
|
||||
|
||||
match ext.as_str() {
|
||||
"jpg" | "jpeg" => "image/jpeg",
|
||||
@@ -248,7 +249,13 @@ fn generate_unique_filename(dir: &StdPath, original: &str) -> String {
|
||||
// Sanitize filename
|
||||
let sanitized_name: String = name
|
||||
.chars()
|
||||
.map(|c| if c.is_alphanumeric() || c == '-' || c == '_' { c } else { '_' })
|
||||
.map(|c| {
|
||||
if c.is_alphanumeric() || c == '-' || c == '_' {
|
||||
c
|
||||
} else {
|
||||
'_'
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let base_filename = format!("{}{}", sanitized_name, ext);
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
use axum::{
|
||||
body::Bytes,
|
||||
extract::Path,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Json, Router,
|
||||
body::Bytes, extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json,
|
||||
Router,
|
||||
};
|
||||
use chrono::{NaiveDate, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
|
||||
use crate::services::filesystem;
|
||||
use crate::config;
|
||||
use crate::services::filesystem;
|
||||
use crate::services::frontmatter;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -35,7 +31,12 @@ pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(list_daily_notes))
|
||||
.route("/today", get(get_or_create_today))
|
||||
.route("/{date}", get(get_daily_note).post(create_daily_note).put(update_daily_note))
|
||||
.route(
|
||||
"/{date}",
|
||||
get(get_daily_note)
|
||||
.post(create_daily_note)
|
||||
.put(update_daily_note),
|
||||
)
|
||||
}
|
||||
|
||||
/// List all daily notes
|
||||
@@ -52,7 +53,7 @@ async fn list_daily_notes() -> impl IntoResponse {
|
||||
|
||||
fn list_daily_notes_impl() -> Result<Vec<DailyNoteSummary>, String> {
|
||||
let daily_dir = config::data_dir().join("daily");
|
||||
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if !daily_dir.exists() {
|
||||
fs::create_dir_all(&daily_dir).map_err(|e| e.to_string())?;
|
||||
@@ -70,7 +71,7 @@ fn list_daily_notes_impl() -> Result<Vec<DailyNoteSummary>, String> {
|
||||
}
|
||||
|
||||
let filename = path.file_stem().and_then(|s| s.to_str()).unwrap_or("");
|
||||
|
||||
|
||||
// Validate date format
|
||||
if NaiveDate::parse_from_str(filename, "%Y-%m-%d").is_err() {
|
||||
continue;
|
||||
@@ -102,7 +103,7 @@ fn list_daily_notes_impl() -> Result<Vec<DailyNoteSummary>, String> {
|
||||
/// Get or create today's daily note
|
||||
async fn get_or_create_today() -> impl IntoResponse {
|
||||
let today = Utc::now().format("%Y-%m-%d").to_string();
|
||||
|
||||
|
||||
match get_daily_note_impl(&today) {
|
||||
Ok(note) => Json(note).into_response(),
|
||||
Err(_) => {
|
||||
@@ -123,14 +124,16 @@ async fn get_or_create_today() -> impl IntoResponse {
|
||||
async fn get_daily_note(Path(date): Path<String>) -> impl IntoResponse {
|
||||
// Validate date format
|
||||
if NaiveDate::parse_from_str(&date, "%Y-%m-%d").is_err() {
|
||||
return (StatusCode::BAD_REQUEST, "Invalid date format. Use YYYY-MM-DD").into_response();
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"Invalid date format. Use YYYY-MM-DD",
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
match get_daily_note_impl(&date) {
|
||||
Ok(note) => Json(note).into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to get daily note: {}", err),
|
||||
@@ -171,16 +174,18 @@ async fn create_daily_note(
|
||||
) -> impl IntoResponse {
|
||||
// Validate date format
|
||||
if NaiveDate::parse_from_str(&date, "%Y-%m-%d").is_err() {
|
||||
return (StatusCode::BAD_REQUEST, "Invalid date format. Use YYYY-MM-DD").into_response();
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"Invalid date format. Use YYYY-MM-DD",
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
let content = body.and_then(|b| b.content.clone());
|
||||
|
||||
match create_daily_note_impl(&date, content.as_deref()) {
|
||||
Ok(note) => (StatusCode::CREATED, Json(note)).into_response(),
|
||||
Err(err) if err.contains("already exists") => {
|
||||
(StatusCode::CONFLICT, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("already exists") => (StatusCode::CONFLICT, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to create daily note: {}", err),
|
||||
@@ -191,7 +196,7 @@ async fn create_daily_note(
|
||||
|
||||
fn create_daily_note_impl(date: &str, initial_content: Option<&str>) -> Result<DailyNote, String> {
|
||||
let daily_dir = config::data_dir().join("daily");
|
||||
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if !daily_dir.exists() {
|
||||
fs::create_dir_all(&daily_dir).map_err(|e| e.to_string())?;
|
||||
@@ -204,10 +209,9 @@ fn create_daily_note_impl(date: &str, initial_content: Option<&str>) -> Result<D
|
||||
}
|
||||
|
||||
let now = Utc::now().to_rfc3339();
|
||||
|
||||
|
||||
// Parse date for display
|
||||
let parsed_date = NaiveDate::parse_from_str(date, "%Y-%m-%d")
|
||||
.map_err(|e| e.to_string())?;
|
||||
let parsed_date = NaiveDate::parse_from_str(date, "%Y-%m-%d").map_err(|e| e.to_string())?;
|
||||
let display_date = parsed_date.format("%A, %B %d, %Y").to_string();
|
||||
|
||||
// Create frontmatter
|
||||
@@ -238,14 +242,12 @@ fn create_daily_note_impl(date: &str, initial_content: Option<&str>) -> Result<D
|
||||
);
|
||||
|
||||
// Use provided content or default template
|
||||
let body = initial_content
|
||||
.map(|c| c.to_string())
|
||||
.unwrap_or_else(|| {
|
||||
format!(
|
||||
"# {}\n\n## Today's Focus\n\n- \n\n## Notes\n\n\n\n## Tasks\n\n- [ ] \n",
|
||||
display_date
|
||||
)
|
||||
});
|
||||
let body = initial_content.map(|c| c.to_string()).unwrap_or_else(|| {
|
||||
format!(
|
||||
"# {}\n\n## Today's Focus\n\n- \n\n## Notes\n\n\n\n## Tasks\n\n- [ ] \n",
|
||||
display_date
|
||||
)
|
||||
});
|
||||
|
||||
let content = frontmatter::serialize_frontmatter(&fm, &body)?;
|
||||
|
||||
@@ -261,22 +263,21 @@ fn create_daily_note_impl(date: &str, initial_content: Option<&str>) -> Result<D
|
||||
}
|
||||
|
||||
/// Update a daily note's content
|
||||
async fn update_daily_note(
|
||||
Path(date): Path<String>,
|
||||
body: Bytes,
|
||||
) -> impl IntoResponse {
|
||||
async fn update_daily_note(Path(date): Path<String>, body: Bytes) -> impl IntoResponse {
|
||||
// Validate date format
|
||||
if NaiveDate::parse_from_str(&date, "%Y-%m-%d").is_err() {
|
||||
return (StatusCode::BAD_REQUEST, "Invalid date format. Use YYYY-MM-DD").into_response();
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
"Invalid date format. Use YYYY-MM-DD",
|
||||
)
|
||||
.into_response();
|
||||
}
|
||||
|
||||
let content = String::from_utf8_lossy(&body).to_string();
|
||||
|
||||
match update_daily_note_impl(&date, &content) {
|
||||
Ok(note) => Json(note).into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to update daily note: {}", err),
|
||||
|
||||
@@ -4,4 +4,4 @@ pub mod git;
|
||||
pub mod notes;
|
||||
pub mod projects;
|
||||
pub mod search;
|
||||
pub mod tasks;
|
||||
pub mod tasks;
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
use axum::{
|
||||
extract::Path,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use axum::{extract::Path, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
|
||||
|
||||
use crate::models::note::{Note, NoteSummary};
|
||||
use crate::services::filesystem;
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/{id}", get(get_note).put(update_note).delete(delete_note))
|
||||
Router::new().route("/{id}", get(get_note).put(update_note).delete(delete_note))
|
||||
}
|
||||
|
||||
pub async fn list_notes() -> impl IntoResponse {
|
||||
@@ -50,10 +43,7 @@ pub async fn create_note() -> impl IntoResponse {
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_note(
|
||||
Path(id): Path<String>,
|
||||
body: String,
|
||||
) -> impl IntoResponse {
|
||||
async fn update_note(Path(id): Path<String>, body: String) -> impl IntoResponse {
|
||||
match filesystem::update_note(&id, &body) {
|
||||
Ok(note) => Json::<Note>(note).into_response(),
|
||||
Err(err) if err.starts_with("Note not found") => {
|
||||
|
||||
@@ -8,14 +8,13 @@ use axum::{
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
|
||||
use crate::config;
|
||||
use crate::routes::tasks::{
|
||||
CreateTaskRequest, UpdateTaskMetaRequest,
|
||||
list_project_tasks_handler, create_task_handler, get_task_handler,
|
||||
update_task_content_handler, toggle_task_handler, update_task_meta_handler,
|
||||
delete_task_handler,
|
||||
create_task_handler, delete_task_handler, get_task_handler, list_project_tasks_handler,
|
||||
toggle_task_handler, update_task_content_handler, update_task_meta_handler, CreateTaskRequest,
|
||||
UpdateTaskMetaRequest,
|
||||
};
|
||||
use crate::services::filesystem;
|
||||
use crate::config;
|
||||
use crate::services::frontmatter;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
@@ -75,15 +74,34 @@ pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(list_projects).post(create_project))
|
||||
.route("/{id}", get(get_project))
|
||||
.route("/{id}/content", get(get_project_content).put(update_project_content))
|
||||
.route(
|
||||
"/{id}/content",
|
||||
get(get_project_content).put(update_project_content),
|
||||
)
|
||||
// Task routes (file-based)
|
||||
.route("/{id}/tasks", get(get_project_tasks).post(create_project_task))
|
||||
.route("/{id}/tasks/{task_id}", get(get_project_task).put(update_project_task).delete(delete_project_task))
|
||||
.route(
|
||||
"/{id}/tasks",
|
||||
get(get_project_tasks).post(create_project_task),
|
||||
)
|
||||
.route(
|
||||
"/{id}/tasks/{task_id}",
|
||||
get(get_project_task)
|
||||
.put(update_project_task)
|
||||
.delete(delete_project_task),
|
||||
)
|
||||
.route("/{id}/tasks/{task_id}/toggle", put(toggle_project_task))
|
||||
.route("/{id}/tasks/{task_id}/meta", put(update_project_task_meta))
|
||||
// Note routes
|
||||
.route("/{id}/notes", get(list_project_notes).post(create_project_note))
|
||||
.route("/{id}/notes/{note_id}", get(get_project_note).put(update_project_note).delete(delete_project_note))
|
||||
.route(
|
||||
"/{id}/notes",
|
||||
get(list_project_notes).post(create_project_note),
|
||||
)
|
||||
.route(
|
||||
"/{id}/notes/{note_id}",
|
||||
get(get_project_note)
|
||||
.put(update_project_note)
|
||||
.delete(delete_project_note),
|
||||
)
|
||||
}
|
||||
|
||||
// ============ Task Handlers ============
|
||||
@@ -355,10 +373,7 @@ async fn get_project_content(Path(id): Path<String>) -> impl IntoResponse {
|
||||
}
|
||||
}
|
||||
|
||||
async fn update_project_content(
|
||||
Path(id): Path<String>,
|
||||
body: String,
|
||||
) -> impl IntoResponse {
|
||||
async fn update_project_content(Path(id): Path<String>, body: String) -> impl IntoResponse {
|
||||
let index_path = config::data_dir()
|
||||
.join("projects")
|
||||
.join(&id)
|
||||
@@ -618,7 +633,9 @@ async fn create_project_note(
|
||||
.into_response()
|
||||
}
|
||||
|
||||
async fn get_project_note(Path((project_id, note_id)): Path<(String, String)>) -> impl IntoResponse {
|
||||
async fn get_project_note(
|
||||
Path((project_id, note_id)): Path<(String, String)>,
|
||||
) -> impl IntoResponse {
|
||||
let notes_dir = config::data_dir()
|
||||
.join("projects")
|
||||
.join(&project_id)
|
||||
@@ -638,7 +655,7 @@ async fn get_project_note(Path((project_id, note_id)): Path<(String, String)>) -
|
||||
|
||||
if let Ok(content) = fs::read_to_string(&path) {
|
||||
let (fm, body, _) = frontmatter::parse_frontmatter(&content);
|
||||
|
||||
|
||||
let file_id = fm
|
||||
.get(&serde_yaml::Value::from("id"))
|
||||
.and_then(|v| v.as_str())
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
use axum::{
|
||||
extract::Query,
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use axum::{extract::Query, http::StatusCode, response::IntoResponse, routing::get, Json, Router};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::services::search;
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
response::IntoResponse,
|
||||
routing::get,
|
||||
Json, Router,
|
||||
};
|
||||
use axum::{http::StatusCode, response::IntoResponse, routing::get, Json, Router};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fs;
|
||||
use std::path::Path as StdPath;
|
||||
|
||||
use crate::services::filesystem;
|
||||
use crate::config;
|
||||
use crate::services::filesystem;
|
||||
use crate::services::frontmatter;
|
||||
|
||||
/// Task summary for list views
|
||||
@@ -73,8 +68,7 @@ pub struct UpdateTaskMetaRequest {
|
||||
}
|
||||
|
||||
pub fn router() -> Router {
|
||||
Router::new()
|
||||
.route("/", get(list_all_tasks_handler))
|
||||
Router::new().route("/", get(list_all_tasks_handler))
|
||||
}
|
||||
|
||||
// ============ Handler Functions (called from projects.rs) ============
|
||||
@@ -96,7 +90,12 @@ pub async fn create_task_handler(
|
||||
project_id: String,
|
||||
payload: CreateTaskRequest,
|
||||
) -> impl IntoResponse {
|
||||
match create_task_impl(&project_id, &payload.title, payload.section.as_deref(), payload.parent_id.as_deref()) {
|
||||
match create_task_impl(
|
||||
&project_id,
|
||||
&payload.title,
|
||||
payload.section.as_deref(),
|
||||
payload.parent_id.as_deref(),
|
||||
) {
|
||||
Ok(task) => (StatusCode::CREATED, Json(task)).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
@@ -110,9 +109,7 @@ pub async fn create_task_handler(
|
||||
pub async fn get_task_handler(project_id: String, task_id: String) -> impl IntoResponse {
|
||||
match get_task_impl(&project_id, &task_id) {
|
||||
Ok(task) => Json(task).into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to get task: {}", err),
|
||||
@@ -129,9 +126,7 @@ pub async fn update_task_content_handler(
|
||||
) -> impl IntoResponse {
|
||||
match update_task_content_impl(&project_id, &task_id, &body) {
|
||||
Ok(task) => Json(task).into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to update task: {}", err),
|
||||
@@ -144,9 +139,7 @@ pub async fn update_task_content_handler(
|
||||
pub async fn toggle_task_handler(project_id: String, task_id: String) -> impl IntoResponse {
|
||||
match toggle_task_impl(&project_id, &task_id) {
|
||||
Ok(task) => Json(task).into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to toggle task: {}", err),
|
||||
@@ -163,9 +156,7 @@ pub async fn update_task_meta_handler(
|
||||
) -> impl IntoResponse {
|
||||
match update_task_meta_impl(&project_id, &task_id, payload) {
|
||||
Ok(task) => Json(task).into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to update task metadata: {}", err),
|
||||
@@ -178,9 +169,7 @@ pub async fn update_task_meta_handler(
|
||||
pub async fn delete_task_handler(project_id: String, task_id: String) -> impl IntoResponse {
|
||||
match delete_task_impl(&project_id, &task_id) {
|
||||
Ok(()) => StatusCode::NO_CONTENT.into_response(),
|
||||
Err(err) if err.contains("not found") => {
|
||||
(StatusCode::NOT_FOUND, err).into_response()
|
||||
}
|
||||
Err(err) if err.contains("not found") => (StatusCode::NOT_FOUND, err).into_response(),
|
||||
Err(err) => (
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Failed to delete task: {}", err),
|
||||
@@ -483,11 +472,7 @@ fn toggle_task_impl(project_id: &str, task_id: &str) -> Result<Task, String> {
|
||||
);
|
||||
|
||||
// Update section based on completion status
|
||||
let new_section = if new_completed {
|
||||
"Completed"
|
||||
} else {
|
||||
"Active"
|
||||
};
|
||||
let new_section = if new_completed { "Completed" } else { "Active" };
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("section"),
|
||||
serde_yaml::Value::from(new_section),
|
||||
@@ -554,14 +539,22 @@ fn toggle_task_impl(project_id: &str, task_id: &str) -> Result<Task, String> {
|
||||
}
|
||||
|
||||
// Return updated task
|
||||
let task = parse_task_file(&fs::read_to_string(&task_path).unwrap(), &task_path, project_id)
|
||||
.ok_or_else(|| "Failed to parse updated task".to_string())?;
|
||||
let task = parse_task_file(
|
||||
&fs::read_to_string(&task_path).unwrap(),
|
||||
&task_path,
|
||||
project_id,
|
||||
)
|
||||
.ok_or_else(|| "Failed to parse updated task".to_string())?;
|
||||
|
||||
Ok(task)
|
||||
}
|
||||
|
||||
fn calculate_next_due_date(current_due: Option<&str>, recurrence: &str, interval: i64) -> Option<String> {
|
||||
use chrono::{NaiveDate, Duration, Utc, Months};
|
||||
fn calculate_next_due_date(
|
||||
current_due: Option<&str>,
|
||||
recurrence: &str,
|
||||
interval: i64,
|
||||
) -> Option<String> {
|
||||
use chrono::{Duration, Months, NaiveDate, Utc};
|
||||
|
||||
let base_date = if let Some(due_str) = current_due {
|
||||
NaiveDate::parse_from_str(due_str, "%Y-%m-%d").unwrap_or_else(|_| Utc::now().date_naive())
|
||||
@@ -600,28 +593,73 @@ fn create_recurring_task_impl(
|
||||
let id = format!("{}-{}", project_id, filename);
|
||||
|
||||
let mut fm = serde_yaml::Mapping::new();
|
||||
fm.insert(serde_yaml::Value::from("id"), serde_yaml::Value::from(id.clone()));
|
||||
fm.insert(serde_yaml::Value::from("type"), serde_yaml::Value::from("task"));
|
||||
fm.insert(serde_yaml::Value::from("title"), serde_yaml::Value::from(title));
|
||||
fm.insert(serde_yaml::Value::from("completed"), serde_yaml::Value::from(false));
|
||||
fm.insert(serde_yaml::Value::from("section"), serde_yaml::Value::from("Active"));
|
||||
fm.insert(serde_yaml::Value::from("priority"), serde_yaml::Value::from("normal"));
|
||||
fm.insert(serde_yaml::Value::from("is_active"), serde_yaml::Value::from(true));
|
||||
fm.insert(serde_yaml::Value::from("project_id"), serde_yaml::Value::from(project_id));
|
||||
fm.insert(serde_yaml::Value::from("recurrence"), serde_yaml::Value::from(recurrence));
|
||||
fm.insert(serde_yaml::Value::from("recurrence_interval"), serde_yaml::Value::from(interval as u64));
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("id"),
|
||||
serde_yaml::Value::from(id.clone()),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("type"),
|
||||
serde_yaml::Value::from("task"),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("title"),
|
||||
serde_yaml::Value::from(title),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("completed"),
|
||||
serde_yaml::Value::from(false),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("section"),
|
||||
serde_yaml::Value::from("Active"),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("priority"),
|
||||
serde_yaml::Value::from("normal"),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("is_active"),
|
||||
serde_yaml::Value::from(true),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("project_id"),
|
||||
serde_yaml::Value::from(project_id),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("recurrence"),
|
||||
serde_yaml::Value::from(recurrence),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("recurrence_interval"),
|
||||
serde_yaml::Value::from(interval as u64),
|
||||
);
|
||||
|
||||
if let Some(due) = due_date {
|
||||
fm.insert(serde_yaml::Value::from("due_date"), serde_yaml::Value::from(due));
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("due_date"),
|
||||
serde_yaml::Value::from(due),
|
||||
);
|
||||
}
|
||||
|
||||
if !tags.is_empty() {
|
||||
let yaml_tags: Vec<serde_yaml::Value> = tags.iter().map(|t| serde_yaml::Value::from(t.as_str())).collect();
|
||||
fm.insert(serde_yaml::Value::from("tags"), serde_yaml::Value::Sequence(yaml_tags));
|
||||
let yaml_tags: Vec<serde_yaml::Value> = tags
|
||||
.iter()
|
||||
.map(|t| serde_yaml::Value::from(t.as_str()))
|
||||
.collect();
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("tags"),
|
||||
serde_yaml::Value::Sequence(yaml_tags),
|
||||
);
|
||||
}
|
||||
|
||||
fm.insert(serde_yaml::Value::from("created"), serde_yaml::Value::from(now_str.clone()));
|
||||
fm.insert(serde_yaml::Value::from("updated"), serde_yaml::Value::from(now_str.clone()));
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("created"),
|
||||
serde_yaml::Value::from(now_str.clone()),
|
||||
);
|
||||
fm.insert(
|
||||
serde_yaml::Value::from("updated"),
|
||||
serde_yaml::Value::from(now_str.clone()),
|
||||
);
|
||||
|
||||
let body = format!("# {}\n\n", title);
|
||||
let content = frontmatter::serialize_frontmatter(&fm, &body)?;
|
||||
@@ -728,8 +766,12 @@ fn update_task_meta_impl(
|
||||
filesystem::atomic_write(&task_path, new_content.as_bytes())?;
|
||||
|
||||
// Return updated task
|
||||
let task = parse_task_file(&fs::read_to_string(&task_path).unwrap(), &task_path, project_id)
|
||||
.ok_or_else(|| "Failed to parse updated task".to_string())?;
|
||||
let task = parse_task_file(
|
||||
&fs::read_to_string(&task_path).unwrap(),
|
||||
&task_path,
|
||||
project_id,
|
||||
)
|
||||
.ok_or_else(|| "Failed to parse updated task".to_string())?;
|
||||
|
||||
Ok(task)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user