From e9b224a3a40768bccbbe8be1ae24dc094630d5ea Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 17:47:36 +0700 Subject: [PATCH 1/8] refactor: return String from all methods for consistency --- mytodo/src/lib.rs | 8 +++--- mytodo/src/task.rs | 68 ++++++++++++++++++++++------------------------ mytodo/tasks.json | 6 +++- 3 files changed, 42 insertions(+), 40 deletions(-) diff --git a/mytodo/src/lib.rs b/mytodo/src/lib.rs index 9204f39..ab18249 100644 --- a/mytodo/src/lib.rs +++ b/mytodo/src/lib.rs @@ -29,18 +29,18 @@ pub fn run(cli: Cli) -> Result<()> { match cli.command { Commands::Add { description } => { - tasks.add(description); + println!("{}", tasks.add(description)?); updated = true; } Commands::List => { - tasks.print(); + println!("{}", tasks.print()); } Commands::Done { id } => { - tasks.done(id)?; + println!("{}", tasks.done(id)?); updated = true; } Commands::Remove { id } => { - tasks.remove(id)?; + println!("{}", tasks.remove(id)?); updated = true; } } diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 9ad7bbe..79e3463 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -15,58 +15,56 @@ pub struct TaskList { } impl TaskList { - const FILE_PATH: &'static str = "tasks.json"; - - pub fn load() -> Result { - if !Path::new(Self::FILE_PATH).exists() { - return Ok(Self::default()); - - } - - let data = fs::read_to_string(Self::FILE_PATH)?; - let list = serde_json::from_str(&data)?; - Ok(list) + pub fn add(&mut self, desc: String) -> Result { + self.tasks.push(Task { description: desc, done: false }); + Ok("Tugas baru ditambahkan.".to_string()) } - pub fn save(&self) -> Result<()> { - let data = serde_json::to_string_pretty(&self)?; - fs::write(Self::FILE_PATH, data)?; - Ok(()) + pub fn done(&mut self, id: usize) -> Result { + if id == 0 || id > self.tasks.len() { + bail!("Nomor tugas {} tidak ditemukan", id); + } + self.tasks[id - 1].done = true; + Ok(format!("Tugas {} ditandai selesai.", id)) } - pub fn add(&mut self, desc: String) { - self.tasks.push(Task { description: desc, done: false }); - println!("Tugas baru ditambahkan."); + pub fn remove(&mut self, id: usize) -> Result { + if id == 0 || id > self.tasks.len() { + bail!("Nomor tugas {} tidak ditemukan", id); + } + self.tasks.remove(id - 1); + Ok(format!("Tugas {} dihapus.", id)) } - pub fn print(&self) { + pub fn print(&self) -> String { if self.tasks.is_empty() { - println!("(Belum ada tugas)"); - return; + return "(Belum ada tugas)".to_string(); } - println!("Daftar Tugas:"); + let mut output = String::from("Daftar Tugas:"); for (i, task) in self.tasks.iter().enumerate() { let status = if task.done { "[x]" } else { "[ ]" }; - println!("{}. {} {}", i + 1, status, task.description); + output.push_str(&format!("\n{}. {} {}", i + 1, status, task.description)); } + output } - pub fn done(&mut self, id: usize) -> Result<()> { - if id == 0 || id > self.tasks.len() { - bail!("Nomor tugas {} tidak ditemukan", id); + const FILE_PATH: &'static str = "tasks.json"; + + pub fn load() -> Result { + if !Path::new(Self::FILE_PATH).exists() { + return Ok(Self::default()); + } - self.tasks[id - 1].done = true; - println!("Tugas {} ditandai selesai.", id); - Ok(()) + + let data = fs::read_to_string(Self::FILE_PATH)?; + let list = serde_json::from_str(&data)?; + Ok(list) } - pub fn remove(&mut self, id: usize) -> Result<()> { - if id == 0 || id > self.tasks.len() { - bail!("Nomor tugas {} tidak ditemukan", id); - } - self.tasks.remove(id - 1); - println!("Tugas {} dihapus.", id); + pub fn save(&self) -> Result<()> { + let data = serde_json::to_string_pretty(&self)?; + fs::write(Self::FILE_PATH, data)?; Ok(()) } } diff --git a/mytodo/tasks.json b/mytodo/tasks.json index d03d74d..315a153 100644 --- a/mytodo/tasks.json +++ b/mytodo/tasks.json @@ -2,7 +2,7 @@ "tasks": [ { "description": "Belajar Rust", - "done": false + "done": true }, { "description": "Belajar Kotlin", @@ -10,6 +10,10 @@ }, { "description": "Belajar Bahasa", + "done": true + }, + { + "description": "cobain", "done": false } ] From a1ee31e8131141144dc522a7b48f91c287f6ea65 Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 17:58:25 +0700 Subject: [PATCH 2/8] test: add unit test for TaskList::add --- mytodo/src/task.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 79e3463..969c6fd 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -68,3 +68,17 @@ impl TaskList { Ok(()) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_add_task() { + let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + assert_eq!(list.tasks.iter().len(), 1); + assert_eq!(list.tasks[0].description, "Belajar Rust"); + assert!(!list.tasks[0].done); + } +} \ No newline at end of file From 5bac853b309fb10a5deb4ec6783398aedfea5722 Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 18:33:32 +0700 Subject: [PATCH 3/8] test: add unit test for TaskList::done --- mytodo/src/task.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 969c6fd..52fa8fe 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -22,7 +22,7 @@ impl TaskList { pub fn done(&mut self, id: usize) -> Result { if id == 0 || id > self.tasks.len() { - bail!("Nomor tugas {} tidak ditemukan", id); + bail!("Nomor tugas {} tidak ditemukan.", id); } self.tasks[id - 1].done = true; Ok(format!("Tugas {} ditandai selesai.", id)) @@ -30,7 +30,7 @@ impl TaskList { pub fn remove(&mut self, id: usize) -> Result { if id == 0 || id > self.tasks.len() { - bail!("Nomor tugas {} tidak ditemukan", id); + bail!("Nomor tugas {} tidak ditemukan.", id); } self.tasks.remove(id - 1); Ok(format!("Tugas {} dihapus.", id)) @@ -81,4 +81,22 @@ mod tests { assert_eq!(list.tasks[0].description, "Belajar Rust"); assert!(!list.tasks[0].done); } + + #[test] + fn test_done_task_success() { + let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + let msg = list.done(1).unwrap(); + assert_eq!(msg, "Tugas 1 ditandai selesai."); + assert!(list.tasks[0].done); + } + + #[test] + fn test_done_task_failed() { + let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + let err_msg = list.done(10).unwrap_err(); + assert_eq!(err_msg.to_string(), "Nomor tugas 10 tidak ditemukan."); + assert!(!list.tasks[0].done); + } } \ No newline at end of file From 17dea78d6ea8bf8d5e3daa3361c56af9eabf0369 Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 18:34:45 +0700 Subject: [PATCH 4/8] test: use assert_eq for clarity --- mytodo/src/task.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 52fa8fe..fa96e57 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -79,7 +79,7 @@ mod tests { let _ = list.add("Belajar Rust".into()); assert_eq!(list.tasks.iter().len(), 1); assert_eq!(list.tasks[0].description, "Belajar Rust"); - assert!(!list.tasks[0].done); + assert_eq!(list.tasks[0].done, false); } #[test] @@ -88,7 +88,7 @@ mod tests { let _ = list.add("Belajar Rust".into()); let msg = list.done(1).unwrap(); assert_eq!(msg, "Tugas 1 ditandai selesai."); - assert!(list.tasks[0].done); + assert_eq!(list.tasks[0].done, true); } #[test] @@ -97,6 +97,6 @@ mod tests { let _ = list.add("Belajar Rust".into()); let err_msg = list.done(10).unwrap_err(); assert_eq!(err_msg.to_string(), "Nomor tugas 10 tidak ditemukan."); - assert!(!list.tasks[0].done); + assert_eq!(list.tasks[0].done, false); } } \ No newline at end of file From 02642aad9cadee0bced24c8aff6418509e12e635 Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 18:38:34 +0700 Subject: [PATCH 5/8] test: add unit test for TaskList::remove --- mytodo/src/task.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index fa96e57..9b891d4 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -99,4 +99,23 @@ mod tests { assert_eq!(err_msg.to_string(), "Nomor tugas 10 tidak ditemukan."); assert_eq!(list.tasks[0].done, false); } + + #[test] + fn test_remove_task_success() { + let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + let msg = list.remove(1).unwrap(); + assert_eq!(msg, "Tugas 1 dihapus."); + assert_eq!(list.tasks.is_empty(), true); + } + + #[test] + fn test_remove_task_failed() { + let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + let err_msg = list.remove(10).unwrap_err(); + assert_eq!(err_msg.to_string(), "Nomor tugas 10 tidak ditemukan."); + assert_eq!(list.tasks.is_empty(), false); + } + } \ No newline at end of file From d98fbd5dced2e2ad13bd022ac800e70d03e59e86 Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 18:44:04 +0700 Subject: [PATCH 6/8] test: add unit test for TaskList::print --- mytodo/src/task.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 9b891d4..57ca8b2 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -118,4 +118,18 @@ mod tests { assert_eq!(list.tasks.is_empty(), false); } + #[test] + fn test_print_empty() { + let list = TaskList::default(); + assert_eq!(list.print(), "(Belum ada tugas)"); + } + + #[test] + fn test_print_tasks() { + let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + let _ = list.add("Belajar Unit Test".into()); + let output = list.print(); + assert_eq!(output, "Daftar Tugas:\n1. [ ] Belajar Rust\n2. [ ] Belajar Unit Test"); + } } \ No newline at end of file From 336f00139fc737a9130b592b83adc5fcaa71d89a Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 18:49:17 +0700 Subject: [PATCH 7/8] fix: tidy up test code by implementing given-when-then and remove unnecessary task item --- mytodo/src/task.rs | 17 ++++++++++++++++- mytodo/tasks.json | 4 ---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 57ca8b2..23f0155 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -76,7 +76,9 @@ mod tests { #[test] fn test_add_task() { let mut list = TaskList::default(); + let _ = list.add("Belajar Rust".into()); + assert_eq!(list.tasks.iter().len(), 1); assert_eq!(list.tasks[0].description, "Belajar Rust"); assert_eq!(list.tasks[0].done, false); @@ -86,7 +88,9 @@ mod tests { fn test_done_task_success() { let mut list = TaskList::default(); let _ = list.add("Belajar Rust".into()); + let msg = list.done(1).unwrap(); + assert_eq!(msg, "Tugas 1 ditandai selesai."); assert_eq!(list.tasks[0].done, true); } @@ -95,7 +99,9 @@ mod tests { fn test_done_task_failed() { let mut list = TaskList::default(); let _ = list.add("Belajar Rust".into()); + let err_msg = list.done(10).unwrap_err(); + assert_eq!(err_msg.to_string(), "Nomor tugas 10 tidak ditemukan."); assert_eq!(list.tasks[0].done, false); } @@ -104,7 +110,9 @@ mod tests { fn test_remove_task_success() { let mut list = TaskList::default(); let _ = list.add("Belajar Rust".into()); + let msg = list.remove(1).unwrap(); + assert_eq!(msg, "Tugas 1 dihapus."); assert_eq!(list.tasks.is_empty(), true); } @@ -113,7 +121,9 @@ mod tests { fn test_remove_task_failed() { let mut list = TaskList::default(); let _ = list.add("Belajar Rust".into()); + let err_msg = list.remove(10).unwrap_err(); + assert_eq!(err_msg.to_string(), "Nomor tugas 10 tidak ditemukan."); assert_eq!(list.tasks.is_empty(), false); } @@ -121,7 +131,10 @@ mod tests { #[test] fn test_print_empty() { let list = TaskList::default(); - assert_eq!(list.print(), "(Belum ada tugas)"); + + let output = list.print(); + + assert_eq!(output, "(Belum ada tugas)"); } #[test] @@ -129,7 +142,9 @@ mod tests { let mut list = TaskList::default(); let _ = list.add("Belajar Rust".into()); let _ = list.add("Belajar Unit Test".into()); + let output = list.print(); + assert_eq!(output, "Daftar Tugas:\n1. [ ] Belajar Rust\n2. [ ] Belajar Unit Test"); } } \ No newline at end of file diff --git a/mytodo/tasks.json b/mytodo/tasks.json index 315a153..f789957 100644 --- a/mytodo/tasks.json +++ b/mytodo/tasks.json @@ -11,10 +11,6 @@ { "description": "Belajar Bahasa", "done": true - }, - { - "description": "cobain", - "done": false } ] } \ No newline at end of file From 817efbe044832487a2f39cca0a79353f6050b22b Mon Sep 17 00:00:00 2001 From: Nazar Alwi Date: Thu, 24 Jul 2025 19:58:16 +0700 Subject: [PATCH 8/8] fix: make TaskList::print returns Result --- mytodo/src/lib.rs | 2 +- mytodo/src/task.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/mytodo/src/lib.rs b/mytodo/src/lib.rs index ab18249..4d1f70e 100644 --- a/mytodo/src/lib.rs +++ b/mytodo/src/lib.rs @@ -33,7 +33,7 @@ pub fn run(cli: Cli) -> Result<()> { updated = true; } Commands::List => { - println!("{}", tasks.print()); + println!("{}", tasks.print()?); } Commands::Done { id } => { println!("{}", tasks.done(id)?); diff --git a/mytodo/src/task.rs b/mytodo/src/task.rs index 23f0155..ef81747 100644 --- a/mytodo/src/task.rs +++ b/mytodo/src/task.rs @@ -36,9 +36,9 @@ impl TaskList { Ok(format!("Tugas {} dihapus.", id)) } - pub fn print(&self) -> String { + pub fn print(&self) -> Result { if self.tasks.is_empty() { - return "(Belum ada tugas)".to_string(); + return Ok("(Belum ada tugas)".to_string()); } let mut output = String::from("Daftar Tugas:"); @@ -46,7 +46,7 @@ impl TaskList { let status = if task.done { "[x]" } else { "[ ]" }; output.push_str(&format!("\n{}. {} {}", i + 1, status, task.description)); } - output + Ok(output) } const FILE_PATH: &'static str = "tasks.json"; @@ -132,7 +132,7 @@ mod tests { fn test_print_empty() { let list = TaskList::default(); - let output = list.print(); + let output = list.print().unwrap(); assert_eq!(output, "(Belum ada tugas)"); } @@ -143,8 +143,8 @@ mod tests { let _ = list.add("Belajar Rust".into()); let _ = list.add("Belajar Unit Test".into()); - let output = list.print(); - + let output = list.print().unwrap(); + assert_eq!(output, "Daftar Tugas:\n1. [ ] Belajar Rust\n2. [ ] Belajar Unit Test"); } } \ No newline at end of file