diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index f5c2bbe..e544b38 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -50,8 +50,8 @@ jobs: # 8️⃣ Make x.sh executable - name: Make script executable - run: chmod +x CoTask/x.sh + run: chmod +x x.sh # 9️⃣ Build using your custom script - name: Build Project - run: bash CoTask/x.sh build + run: bash ./x.sh build diff --git a/src/bin/cli.rs b/src/bin/cli.rs index 5523e02..12ca34a 100644 --- a/src/bin/cli.rs +++ b/src/bin/cli.rs @@ -1,5 +1,8 @@ +use cotask::logic::{ + add_task, branch, checkout, diff, export, gc, import, init_repo, list_task, merge, rebase, + resolve, revert, show_help, show_log, stash, tag, +}; use std::env; -use cotask::logic::{add_task, branch, checkout, diff, gc, init_repo,import,export,list_task, merge, revert, show_help, show_log,resolve, rebase,stash,tag}; fn main() { let args: Vec = env::args().collect(); @@ -38,18 +41,16 @@ fn main() { println!("Please provide task id."); return; } - let id:usize = match args[2].parse() { + let id: usize = match args[2].parse() { Ok(n) => n, - Err(_) =>{ + Err(_) => { println!("Invalid task ID"); return; } - }; add_task::mark_done(id); - } - + "log" => show_log::show_log(), "checkout" => { @@ -99,7 +100,6 @@ fn main() { merge::merge_branch(&args[2]); } - "branch" => { if args.len() < 3 { println!("Provide branch name."); @@ -125,7 +125,6 @@ fn main() { } "--help" => { show_help::show_help(); - return; } "tag" => { @@ -140,7 +139,6 @@ fn main() { tag::list_tags(); } - "resolve" => { if args.len() < 4 { println!("Usage: resolve done|undone"); @@ -160,18 +158,17 @@ fn main() { resolve::resolve(id, done); } "rebase" => { - if args.len() < 3 { - println!("Provide target branch."); - return; - } + if args.len() < 3 { + println!("Provide target branch."); + return; + } - rebase::rebase_onto(&args[2]); - } + rebase::rebase_onto(&args[2]); + } _ => { println!("Unknown command.\n"); - show_help::show_help(); + show_help::show_help(); } - } } diff --git a/src/lib.rs b/src/lib.rs index fb5cbb8..fe024ea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,3 @@ pub mod logic; +pub mod models; pub mod storage; -pub mod models; \ No newline at end of file diff --git a/src/logic/add_task.rs b/src/logic/add_task.rs index 535ffe6..ec554dd 100644 --- a/src/logic/add_task.rs +++ b/src/logic/add_task.rs @@ -1,7 +1,7 @@ use crate::models::{commit_model::Commit, task_model::Task}; use crate::storage::{ commit::{load_commit, save_commit}, - head::{read_head_branch, read_branch_commit, write_branch_commit}, + head::{read_branch_commit, read_head_branch, write_branch_commit}, }; pub fn add_task(text: &str) { @@ -41,7 +41,11 @@ pub fn add_task(text: &str) { let message = format!("Added task '{}'", text); let new_commit = Commit { - parents: if head_commit == 0 { vec![] } else { vec![head_commit] }, + parents: if head_commit == 0 { + vec![] + } else { + vec![head_commit] + }, message, tasks, }; diff --git a/src/logic/branch.rs b/src/logic/branch.rs index 490a547..17c11e7 100644 --- a/src/logic/branch.rs +++ b/src/logic/branch.rs @@ -1,5 +1,5 @@ +use crate::storage::head::{read_branch_commit, read_head_branch}; use std::fs; -use crate::storage::{commit, head::{read_branch_commit, read_head_branch}}; pub fn create_branch(name: &str) { let current_branch = match read_head_branch() { @@ -62,7 +62,6 @@ pub fn delete_branch(name: &str) { println!("Repository not initialized."); return; } - }; // Prevent deleting current branch if name == current_branch { @@ -70,7 +69,7 @@ pub fn delete_branch(name: &str) { return; } - let path = format!(".cotask/refs/{}",name); + let path = format!(".cotask/refs/{}", name); // Check branch exists if fs::metadata(&path).is_err() { println!("Branch '{}' does not exist.", name); @@ -83,5 +82,5 @@ pub fn delete_branch(name: &str) { return; } - println!("Branch '{}' deleted.",name); -} \ No newline at end of file + println!("Branch '{}' deleted.", name); +} diff --git a/src/logic/checkout.rs b/src/logic/checkout.rs index 61d56a5..4c3e33b 100644 --- a/src/logic/checkout.rs +++ b/src/logic/checkout.rs @@ -1,9 +1,9 @@ -use std::fs; +use crate::logic::tag::read_tag_commit; use crate::storage::{ commit::load_commit, - head::{write_head_branch, read_head_branch}, + head::{read_head_branch, write_head_branch}, }; -use crate::logic::tag::read_tag_commit; +use std::fs; pub fn checkout_commit(commit_number: usize) { if commit_number == 0 { @@ -28,8 +28,10 @@ pub fn checkout_ref(name: &str) { // 1) Branch check let branch_path = format!(".cotask/refs/{}", name); if fs::metadata(&branch_path).is_ok() { - if let Ok(current) = read_head_branch() { - if current == name { + if let Ok(current) = read_head_branch() + && current == name + { + { println!("Already on branch '{}'", name); return; } diff --git a/src/logic/diff.rs b/src/logic/diff.rs index 54177be..81d1bf0 100644 --- a/src/logic/diff.rs +++ b/src/logic/diff.rs @@ -1,6 +1,6 @@ -use std::collections::HashMap; -use crate::storage::commit::load_commit; use crate::models::task_model::Task; +use crate::storage::commit::load_commit; +use std::collections::HashMap; pub fn diff(c1: usize, c2: usize) { let commit1 = match load_commit(c1) { @@ -48,13 +48,13 @@ pub fn diff(c1: usize, c2: usize) { // Status changes for (id, task1) in &map1 { - if let Some(task2) = map2.get(id) { - if task1.completed != task2.completed { - if task2.completed { - println!("✓ Completed: {}. {}", task2.id, task2.text); - } else { - println!("↺ Marked incomplete: {}. {}", task2.id, task2.text); - } + if let Some(task2) = map2.get(id) + && task1.completed != task2.completed + { + if task2.completed { + println!("✓ Completed: {}. {}", task2.id, task2.text); + } else { + println!("↺ Marked incomplete: {}. {}", task2.id, task2.text); } } } diff --git a/src/logic/export.rs b/src/logic/export.rs index 4529037..54cabba 100644 --- a/src/logic/export.rs +++ b/src/logic/export.rs @@ -1,8 +1,5 @@ +use crate::{models::export_model::RepoExport, storage::commit::load_commit}; use std::{collections::HashMap, fs}; -use crate::{ - models::export_model::RepoExport, - storage::commit::load_commit, -}; pub fn export_repo() { let mut commits = HashMap::new(); @@ -13,10 +10,10 @@ pub fn export_repo() { if let Ok(entries) = fs::read_dir(".cotask/commits") { for e in entries.flatten() { let name = e.file_name().into_string().unwrap(); - if let Ok(num) = name.trim_end_matches(".json").parse::() { - if let Ok(commit) = load_commit(num) { - commits.insert(num, commit); - } + if let Ok(num) = name.trim_end_matches(".json").parse::() + && let Ok(commit) = load_commit(num) + { + commits.insert(num, commit); } } } @@ -43,7 +40,12 @@ pub fn export_repo() { let head = fs::read_to_string(".cotask/HEAD").unwrap(); - let export = RepoExport { commits, branches, tags, head }; + let export = RepoExport { + commits, + branches, + tags, + head, + }; let json = serde_json::to_string_pretty(&export).unwrap(); fs::write("cotask_backup.json", json).unwrap(); diff --git a/src/logic/gc.rs b/src/logic/gc.rs index d845512..dd24ba7 100644 --- a/src/logic/gc.rs +++ b/src/logic/gc.rs @@ -2,8 +2,8 @@ use std::collections::HashSet; use std::fs; use crate::storage::{ - head::{read_head_branch, read_branch_commit}, commit::load_commit, + head::{read_branch_commit, read_head_branch}, }; fn collect_reachable(commit_number: usize, reachable: &mut HashSet) { @@ -58,14 +58,12 @@ pub fn run_gc() { let mut deleted = 0; for entry in paths.flatten() { - if let Some(name) = entry.path().file_stem() { - if let Ok(num) = name.to_string_lossy().parse::() { - if !reachable.contains(&num) { - if fs::remove_file(entry.path()).is_ok() { - deleted += 1; - } - } - } + if let Some(name) = entry.path().file_stem() + && let Ok(num) = name.to_string_lossy().parse::() + && !reachable.contains(&num) + && fs::remove_file(entry.path()).is_ok() + { + deleted += 1; } } diff --git a/src/logic/import.rs b/src/logic/import.rs index 74c1bc8..18f2abd 100644 --- a/src/logic/import.rs +++ b/src/logic/import.rs @@ -1,6 +1,6 @@ -use std::fs; use crate::models::export_model::RepoExport; use crate::storage::commit::save_commit; +use std::fs; pub fn import_repo(file: &str) { let data = fs::read_to_string(file).unwrap(); diff --git a/src/logic/init_repo.rs b/src/logic/init_repo.rs index 9358050..130632c 100644 --- a/src/logic/init_repo.rs +++ b/src/logic/init_repo.rs @@ -12,29 +12,22 @@ pub fn init_repo() { fs::create_dir(repo_path).expect("Failed to create .cotask directory"); // Create commits folder - fs::create_dir(repo_path.join("commits")) - .expect("Failed to create commits directory"); + fs::create_dir(repo_path.join("commits")).expect("Failed to create commits directory"); // Create refs folder - fs::create_dir(repo_path.join("refs")) - .expect("Failed to create refs directory"); + fs::create_dir(repo_path.join("refs")).expect("Failed to create refs directory"); // Create tags folder - fs::create_dir(repo_path.join("tags")) - .expect("Failed to create tags directory"); + fs::create_dir(repo_path.join("tags")).expect("Failed to create tags directory"); // Create stash folder - fs::create_dir(repo_path.join("stash")) - .expect("Failed to create stash directory"); - + fs::create_dir(repo_path.join("stash")).expect("Failed to create stash directory"); // HEAD now stores branch name - fs::write(repo_path.join("HEAD"), "main") - .expect("Failed to create HEAD"); + fs::write(repo_path.join("HEAD"), "main").expect("Failed to create HEAD"); // Create main branch pointing to commit 0 - fs::write(repo_path.join("refs/main"), "0") - .expect("Failed to create main branch"); + fs::write(repo_path.join("refs/main"), "0").expect("Failed to create main branch"); println!("Initialized empty cotask repository with branch 'main'."); } diff --git a/src/logic/list_task.rs b/src/logic/list_task.rs index 367637d..3598452 100644 --- a/src/logic/list_task.rs +++ b/src/logic/list_task.rs @@ -1,6 +1,6 @@ use crate::storage::{ - head::{read_head_branch, read_branch_commit}, commit::load_commit, + head::{read_branch_commit, read_head_branch}, }; pub fn list_tasks() { diff --git a/src/logic/merge.rs b/src/logic/merge.rs index abf6f2e..3916e81 100644 --- a/src/logic/merge.rs +++ b/src/logic/merge.rs @@ -1,10 +1,10 @@ -use std::fs; -use std::collections::HashMap; +use crate::models::{commit_model::Commit, task_model::Task}; use crate::storage::{ - head::{read_head_branch, read_branch_commit, write_branch_commit}, commit::{load_commit, save_commit}, + head::{read_branch_commit, read_head_branch, write_branch_commit}, }; -use crate::models::{commit_model::Commit, task_model::Task}; +use std::collections::HashMap; +use std::fs; pub fn merge_branch(target_branch: &str) { // Current branch (OURS) @@ -43,11 +43,14 @@ pub fn merge_branch(target_branch: &str) { let target = load_commit(theirs).unwrap(); let mut task_map: HashMap = HashMap::new(); + let mut conflict_found = false; + // Insert OUR tasks for t in current.tasks { task_map.insert(t.id, t); } + // Merge THEIR tasks for t in target.tasks { task_map .entry(t.id) @@ -56,19 +59,24 @@ pub fn merge_branch(target_branch: &str) { if existing.completed != t.completed || existing.text != t.text { let conflict_info = format!( "TASK {}\nOURS: completed={}, text={}\nTHEIRS: completed={}, text={}\n", - existing.id, existing.completed, existing.text, - t.completed, t.text + existing.id, existing.completed, existing.text, t.completed, t.text ); fs::write(".cotask/MERGE_CONFLICT", conflict_info).unwrap(); println!("⚠ Conflict in task {}!", existing.id); println!("Run: cotask resolve {} done|undone", existing.id); - return; // stop merge safely + + conflict_found = true; } }) .or_insert(t); } + // Stop merge safely if conflict happened + if conflict_found { + return; + } + let merged_tasks: Vec = task_map.into_values().collect(); let new_commit_number = ours + 1; diff --git a/src/logic/mod.rs b/src/logic/mod.rs index 89bd2c5..e652120 100644 --- a/src/logic/mod.rs +++ b/src/logic/mod.rs @@ -1,17 +1,17 @@ -pub mod init_repo; pub mod add_task; -pub mod list_task; -pub mod show_log; +pub mod branch; pub mod checkout; -pub mod revert; +pub mod diff; +pub mod export; pub mod gc; +pub mod import; +pub mod init_repo; +pub mod list_task; pub mod merge; -pub mod branch; -pub mod diff; -pub mod show_help; -pub mod resolve; pub mod rebase; -pub mod tag; +pub mod resolve; +pub mod revert; +pub mod show_help; +pub mod show_log; pub mod stash; -pub mod export; -pub mod import; \ No newline at end of file +pub mod tag; diff --git a/src/logic/rebase.rs b/src/logic/rebase.rs index c0d4874..c540877 100644 --- a/src/logic/rebase.rs +++ b/src/logic/rebase.rs @@ -1,8 +1,8 @@ +use crate::models::commit_model::Commit; use crate::storage::{ - head::{read_head_branch, read_branch_commit, write_branch_commit}, commit::{load_commit, save_commit}, + head::{read_branch_commit, read_head_branch, write_branch_commit}, }; -use crate::models::commit_model::Commit; pub fn rebase_onto(target_branch: &str) { let current_branch = read_head_branch().unwrap(); diff --git a/src/logic/resolve.rs b/src/logic/resolve.rs index c961346..84592cf 100644 --- a/src/logic/resolve.rs +++ b/src/logic/resolve.rs @@ -1,18 +1,15 @@ -use std::fs; +use crate::models::commit_model::Commit; use crate::storage::{ - head::{read_head_branch, read_branch_commit, write_branch_commit}, commit::{load_commit, save_commit}, + head::{read_branch_commit, read_head_branch, write_branch_commit}, }; -use crate::models::commit_model::Commit; +use std::fs; pub fn resolve(task_id: usize, done: bool) { - let conflict = match fs::read_to_string(".cotask/MERGE_CONFLICT") { - Ok(c) => c, - Err(_) => { - println!("No conflict to resolve."); - return; - } - }; + if fs::read_to_string(".cotask/MERGE_CONFLICT").is_err() { + println!("No merge conflict to resolve"); + return; + } let branch = read_head_branch().unwrap(); let head = read_branch_commit(&branch).unwrap(); diff --git a/src/logic/revert.rs b/src/logic/revert.rs index 66d8de7..f94a430 100644 --- a/src/logic/revert.rs +++ b/src/logic/revert.rs @@ -1,8 +1,8 @@ +use crate::models::commit_model::Commit; use crate::storage::{ - head::{read_head_branch, read_branch_commit, write_branch_commit}, commit::{load_commit, save_commit}, + head::{read_branch_commit, read_head_branch, write_branch_commit}, }; -use crate::models::commit_model::Commit; pub fn revert(commit_number: usize) { // Read current branch diff --git a/src/logic/show_log.rs b/src/logic/show_log.rs index 05feba3..f42f702 100644 --- a/src/logic/show_log.rs +++ b/src/logic/show_log.rs @@ -2,7 +2,7 @@ use std::collections::HashSet; use crate::storage::{ commit::load_commit, - head::{read_head_branch, read_branch_commit}, + head::{read_branch_commit, read_head_branch}, }; pub fn show_log() { @@ -36,7 +36,7 @@ pub fn show_log() { match load_commit(commit_number) { Ok(commit) => { println!("Commit {}", commit_number); - println!("Message: {}", commit.message); + println!("Message: {}", commit.message); for task in &commit.tasks { let status = if task.completed { "✓" } else { " " }; diff --git a/src/logic/stash.rs b/src/logic/stash.rs index 02d5e49..1407d3c 100644 --- a/src/logic/stash.rs +++ b/src/logic/stash.rs @@ -1,10 +1,10 @@ -use std::fs; +use crate::models::commit_model::Commit; use crate::storage::{ commit::load_commit, - head::{read_head_branch, read_branch_commit, write_branch_commit}, commit::save_commit, + head::{read_branch_commit, read_head_branch, write_branch_commit}, }; -use crate::models::commit_model::Commit; +use std::fs; pub fn stash() { let branch = read_head_branch().unwrap(); @@ -41,17 +41,21 @@ pub fn stash_pop() { fs::remove_file(path).unwrap(); - println!("Applied stash {} → new commit {}", stash_id, new_commit_number); + println!( + "Applied stash {} → new commit {}", + stash_id, new_commit_number + ); } fn next_stash_id() -> usize { let mut max = 0; if let Ok(entries) = fs::read_dir(".cotask/stash") { for e in entries.flatten() { - if let Some(name) = e.file_name().to_str() { - if let Ok(n) = name.trim_end_matches(".json").parse::() { - if n > max { max = n; } - } + if let Some(name) = e.file_name().to_str() + && let Ok(n) = name.trim_end_matches(".json").parse::() + && n > max + { + max = n; } } } @@ -62,10 +66,11 @@ fn latest_stash_id() -> usize { let mut max = 0; if let Ok(entries) = fs::read_dir(".cotask/stash") { for e in entries.flatten() { - if let Some(name) = e.file_name().to_str() { - if let Ok(n) = name.trim_end_matches(".json").parse::() { - if n > max { max = n; } - } + if let Some(name) = e.file_name().to_str() + && let Ok(n) = name.trim_end_matches(".json").parse::() + && n > max + { + max = n; } } } diff --git a/src/logic/tag.rs b/src/logic/tag.rs index d197dd8..a78776e 100644 --- a/src/logic/tag.rs +++ b/src/logic/tag.rs @@ -1,5 +1,5 @@ +use crate::storage::head::{read_branch_commit, read_head_branch}; use std::fs; -use crate::storage::head::{read_head_branch, read_branch_commit}; pub fn create_tag(name: &str) { let branch = read_head_branch().unwrap(); @@ -26,7 +26,6 @@ pub fn read_tag_commit(name: &str) -> Option { } } - pub fn list_tags() { match fs::read_dir(".cotask/tags") { Ok(entries) => { diff --git a/src/models/commit_model.rs b/src/models/commit_model.rs index 87677cf..ecb2756 100644 --- a/src/models/commit_model.rs +++ b/src/models/commit_model.rs @@ -1,8 +1,8 @@ -use serde::{Deserialize, Serialize}; use crate::models::task_model::Task; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Commit { - pub parents: Vec, - pub message: String, + pub parents: Vec, + pub message: String, pub tasks: Vec, } diff --git a/src/models/export_model.rs b/src/models/export_model.rs index 8c9e2c2..ee05615 100644 --- a/src/models/export_model.rs +++ b/src/models/export_model.rs @@ -1,6 +1,6 @@ -use serde::{Serialize, Deserialize}; -use std::collections::HashMap; use crate::models::commit_model::Commit; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; #[derive(Serialize, Deserialize)] pub struct RepoExport { diff --git a/src/models/mod.rs b/src/models/mod.rs index ba9e5d0..75ba72a 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,3 @@ -pub mod task_model; pub mod commit_model; -pub mod export_model; \ No newline at end of file +pub mod export_model; +pub mod task_model; diff --git a/src/models/task_model.rs b/src/models/task_model.rs index 4dacf58..2ed3730 100644 --- a/src/models/task_model.rs +++ b/src/models/task_model.rs @@ -1,8 +1,8 @@ use serde::{Deserialize, Serialize}; -#[derive(Serialize,Deserialize,Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct Task { pub id: usize, pub text: String, - pub completed: bool -} \ No newline at end of file + pub completed: bool, +} diff --git a/src/storage/commit.rs b/src/storage/commit.rs index 239e4af..31e7e39 100644 --- a/src/storage/commit.rs +++ b/src/storage/commit.rs @@ -1,6 +1,6 @@ +use crate::models::commit_model::Commit; use std::fs; use std::io; -use crate::models::commit_model::Commit; pub fn save_commit(commit_number: usize, commit: &Commit) -> io::Result<()> { let path = format!(".cotask/commits/{}.json", commit_number); diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 6aad703..4776328 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1,2 +1,2 @@ +pub mod commit; pub mod head; -pub mod commit; \ No newline at end of file