Compare commits

..

10 Commits

3 changed files with 391 additions and 5 deletions

242
Cargo.lock generated
View File

@@ -2,6 +2,248 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "anstream"
version = "0.6.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"is_terminal_polyfill",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
[[package]]
name = "anstyle-parse"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
"windows-sys",
]
[[package]]
name = "clap"
version = "4.5.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
[[package]]
name = "colorchoice"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
[[package]]
name = "itoa"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
[[package]]
name = "memchr"
version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
[[package]]
name = "once_cell_polyfill"
version = "1.70.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "opencode-testing"
version = "0.1.0"
dependencies = [
"clap",
"serde",
"serde_json",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
dependencies = [
"serde_core",
"serde_derive",
]
[[package]]
name = "serde_core"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.228"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
dependencies = [
"itoa",
"memchr",
"serde",
"serde_core",
"zmij",
]
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "utf8parse"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
dependencies = [
"windows-link",
]
[[package]]
name = "zmij"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ff05f8caa9038894637571ae6b9e29466c1f4f829d26c9b28f869a29cbe3445"

View File

@@ -1,7 +1,17 @@
[package]
name = "opencode-testing"
name = "todo_app"
version = "0.1.0"
edition = "2024"
edition = "2026"
authors = ["Jakob Rönnbäck <jakob@rowanbrook.net>"]
description = "A simple command-line todo list manager"
license = "MIT"
repository = "https://gitea.rowanbrook.net/jakob/Learn-Rust-through-projects.git"
keywords = ["cli", "todo", "productivity"]
categories = ["command-line-utilities"]
[[bin]]
name = "todo"
path = "src/main.rs"
[dependencies]
clap = { version = "4.0.0", features = ["derive"] }

View File

@@ -1,6 +1,140 @@
use std::env;
use clap::{Parser, Subcommand};
use serde::{Deserialize, Serialize};
use std::fmt;
use std::fs;
use std::path::Path;
#[derive(Parser)]
#[command(name = "todo")]
#[command(about = "A simple todo list manager")]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
Add { task: String },
List,
Complete { id: usize },
Remove { id: usize },
}
#[derive(Serialize, Deserialize, Debug)]
struct Task {
id: usize,
description: String,
completed: bool,
}
#[derive(Serialize, Deserialize, Debug)]
struct TodoList {
tasks: Vec<Task>,
next_id: usize,
}
#[derive(Debug)]
enum TodoError {
FileError(String),
ParseError(String),
TaskNotFound(usize),
InvalidInput(String),
}
impl fmt::Display for TodoError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TodoError::FileError(msg) => write!(f, "File error: {}", msg),
TodoError::ParseError(msg) => write!(f, "Parse error: {}", msg),
TodoError::TaskNotFound(id) => write!(f, "Task {} not found", id),
TodoError::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
}
}
}
impl std::error::Error for TodoError {}
impl TodoList {
fn new() -> Self {
TodoList {
tasks: Vec::new(),
next_id: 1,
}
}
fn load_from_file(filename: &str) -> Result<Self, Box<dyn std::error::Error>> {
if Path::new(filename).exists() {
let contents = fs::read_to_string(filename)?;
let todo_list = serde_json::from_str(&contents)?;
Ok(todo_list)
} else {
Ok(TodoList::new())
}
}
fn save_to_file(&self, filename: &str) -> Result<(), Box<dyn std::error::Error>> {
let json = serde_json::to_string_pretty(self)?;
fs::write(filename, json)?;
Ok(())
}
fn add_task(&mut self, description: String) -> Result<(), TodoError> {
if description.trim().is_empty() {
return Err(TodoError::InvalidInput(
"Task description cannot be empty".to_string(),
));
}
let task = Task {
id: self.next_id,
description,
completed: false,
};
self.tasks.push(task);
self.next_id += 1;
Ok(())
}
fn complete_task(&mut self, id: usize) -> Result<(), TodoError> {
match self.tasks.iter_mut().find(|task| task.id == id) {
Some(task) => {
task.completed = true;
Ok(())
}
None => Err(TodoError::TaskNotFound(id)),
}
}
}
fn main() {
let args: Vec<String> = env::args().collect();
println!("Arguments: {:?}", args);
let cli = Cli::parse();
if let Err(e) = run_command(cli) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
fn run_command(cli: Cli) -> Result<(), TodoError> {
let mut todo_list =
TodoList::load_from_file("tasks.json").map_err(|e| TodoError::FileError(e.to_string()))?;
match cli.command {
Commands::Add { task } => {
todo_list.add_task(task)?;
println!("Task added successfully");
}
Commands::Complete { id } => {
todo_list.complete_task(id)?;
println!("Task {} marked as complete", id);
}
_ => {} // Handle other commands
}
todo_list
.save_to_file("todo.json")
.map_err(|e| TodoError::FileError(e.to_string()))?;
Ok(())
}