Compare commits
15 Commits
main
..
f798063f9e
| Author | SHA1 | Date | |
|---|---|---|---|
| f798063f9e | |||
| c0c6e640eb | |||
| fe5cb3fbef | |||
| 2ed1aa2e00 | |||
| 575489e3f7 | |||
| 4c77d74985 | |||
| 835b66e708 | |||
| e0088eb1c7 | |||
| 7f64d6d54d | |||
| 48b0aa8ce1 | |||
| dd91946fb2 | |||
| 0ed932a44a | |||
| 9eb53e2561 | |||
| 2cf2725df3 | |||
| 40aaaf4b49 |
+1
-18
@@ -1,18 +1 @@
|
|||||||
# ---> Rust
|
/target
|
||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
debug/
|
|
||||||
target/
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
# RustRover
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
#.idea/
|
|
||||||
|
|||||||
@@ -0,0 +1,122 @@
|
|||||||
|
# Agent Guidelines for opencode-testing
|
||||||
|
|
||||||
|
This is a Rust project using Edition 2024.
|
||||||
|
|
||||||
|
## Build/Test Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build the project
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
# Build for release
|
||||||
|
cargo build --release
|
||||||
|
|
||||||
|
# Run the application
|
||||||
|
cargo run
|
||||||
|
|
||||||
|
# Run all tests
|
||||||
|
cargo test
|
||||||
|
|
||||||
|
# Run a specific test (e.g., test named `test_calculate_area`)
|
||||||
|
cargo test test_calculate_area
|
||||||
|
|
||||||
|
# Run tests in a specific module
|
||||||
|
cargo test module_name::
|
||||||
|
|
||||||
|
# Run with output visible
|
||||||
|
cargo test -- --nocapture
|
||||||
|
|
||||||
|
# Check for compilation errors without building
|
||||||
|
cargo check
|
||||||
|
|
||||||
|
# Watch mode for development (requires cargo-watch)
|
||||||
|
cargo watch -x "run"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Quality Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Format code (rustfmt)
|
||||||
|
cargo fmt
|
||||||
|
|
||||||
|
# Check formatting without making changes
|
||||||
|
cargo fmt -- --check
|
||||||
|
|
||||||
|
# Run clippy lints
|
||||||
|
cargo clippy
|
||||||
|
|
||||||
|
# Run clippy with all features and warnings as errors
|
||||||
|
cargo clippy --all-features -- -D warnings
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style Guidelines
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
- Use `cargo fmt` with default settings
|
||||||
|
- Max line width: 100 characters (default)
|
||||||
|
- Use 4 spaces for indentation (default)
|
||||||
|
- Trailing commas in multi-line lists
|
||||||
|
|
||||||
|
### Naming Conventions
|
||||||
|
- Variables, functions, modules: `snake_case`
|
||||||
|
- Types (structs, enums, traits): `CamelCase`
|
||||||
|
- Constants, statics: `SCREAMING_SNAKE_CASE`
|
||||||
|
- Lifetimes: `'lowercase`
|
||||||
|
- Generic type parameters: `CamelCase` (single uppercase letter preferred: `T`, `K`, `V`)
|
||||||
|
- Macros: `snake_case!`
|
||||||
|
|
||||||
|
### Import Organization
|
||||||
|
Group imports in this order, separated by blank lines:
|
||||||
|
1. Standard library imports (`std::`, `core::`, `alloc::`)
|
||||||
|
2. External crate imports
|
||||||
|
3. Internal crate imports (`crate::`)
|
||||||
|
4. Super module imports (`super::`)
|
||||||
|
5. Current module imports (`self::`)
|
||||||
|
|
||||||
|
Use `use` statements not `extern crate`.
|
||||||
|
|
||||||
|
### Types and Type Safety
|
||||||
|
- Prefer explicit types over `impl Trait` in public APIs
|
||||||
|
- Use `Option<T>` and `Result<T, E>` for fallible operations
|
||||||
|
- Avoid `unwrap()` and `expect()` in production code; handle errors properly
|
||||||
|
- Use `?` operator for error propagation
|
||||||
|
- Prefer `&str` over `&String`, `&[T]` over `&Vec<T>` for function parameters
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
- Define custom error types using `thiserror` or `enum` for complex error cases
|
||||||
|
- Use `anyhow` for application code error handling
|
||||||
|
- Propagate errors with `?` operator
|
||||||
|
- Provide context with `map_err()` when needed
|
||||||
|
|
||||||
|
### Functions
|
||||||
|
- Keep functions small and focused on a single task
|
||||||
|
- Max 5-7 parameters; use structs for more complex parameter sets
|
||||||
|
- Document public functions with `///` doc comments
|
||||||
|
- Use early returns to reduce nesting
|
||||||
|
|
||||||
|
### Comments and Documentation
|
||||||
|
- Use `//` for inline comments
|
||||||
|
- Use `///` for public API documentation
|
||||||
|
- Use `//!` for module-level documentation
|
||||||
|
- Document panics, unsafe code, and complex algorithms
|
||||||
|
|
||||||
|
### Unsafe Code
|
||||||
|
- Avoid unsafe code unless absolutely necessary
|
||||||
|
- Document all safety invariants with `// SAFETY:` comments
|
||||||
|
- Keep unsafe blocks as small as possible
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- Write unit tests in the same file as the code: `#[cfg(test)]` module
|
||||||
|
- Use descriptive test names: `test_<scenario>_<expected_behavior>`
|
||||||
|
- Use `assert!`, `assert_eq!`, `assert_ne!` for assertions
|
||||||
|
- Organize tests with submodules for different scenarios
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
- Prefer ownership and borrowing over cloning when possible
|
||||||
|
- Use `.clone()` explicitly when needed
|
||||||
|
- Consider `Cow<'_, T>` for data that may be borrowed or owned
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Avoid premature optimization; write clear code first
|
||||||
|
- Use iterators instead of loops when appropriate
|
||||||
|
- Profile before optimizing
|
||||||
Generated
+249
@@ -0,0 +1,249 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# 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"
|
||||||
+19
@@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "todo_app"
|
||||||
|
version = "0.1.0"
|
||||||
|
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"] }
|
||||||
|
serde = { version = "1.0.130", features = ["derive"] }
|
||||||
|
serde_json = "1.0.130"
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2026 jakob
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
|
|
||||||
associated documentation files (the "Software"), to deal in the Software without restriction, including
|
|
||||||
without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
|
|
||||||
following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
|
||||||
portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
||||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
||||||
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
||||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
||||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Learn-Rust-through-projects
|
|
||||||
|
|
||||||
Example code from the book "Learn Rust through projects" by Julia Lornfield
|
|
||||||
+140
@@ -0,0 +1,140 @@
|
|||||||
|
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 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(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user