1use std::path::{Path, PathBuf};
2
3use anyhow::{Context, Result};
4
5use crate::discovery::find_archived_unit;
6use crate::resolve::resolve_unit;
7use crate::unit::Unit;
8
9pub struct GetResult {
11 pub unit: Unit,
12 pub path: PathBuf,
13}
14
15pub fn get(mana_dir: &Path, id: &str) -> Result<GetResult> {
17 let result = resolve_unit(mana_dir, id).or_else(|_| {
18 let unit_path =
19 find_archived_unit(mana_dir, id).with_context(|| format!("Unit not found: {}", id))?;
20 let unit =
21 Unit::from_file(&unit_path).with_context(|| format!("Failed to load unit: {}", id))?;
22 Ok::<_, anyhow::Error>(crate::resolve::ResolvedUnit {
23 unit,
24 path: unit_path,
25 })
26 })?;
27 Ok(GetResult {
28 unit: result.unit,
29 path: result.path,
30 })
31}
32
33#[cfg(test)]
34mod tests {
35 use super::*;
36 use crate::ops::create::{self, tests::minimal_params};
37 use crate::util::title_to_slug;
38 use std::fs;
39 use tempfile::TempDir;
40
41 fn setup() -> (TempDir, PathBuf) {
42 let dir = TempDir::new().unwrap();
43 let bd = dir.path().join(".mana");
44 fs::create_dir(&bd).unwrap();
45 crate::config::Config {
46 project: "test".to_string(),
47 next_id: 1,
48 auto_close_parent: true,
49 run: None,
50 plan: None,
51 max_loops: 10,
52 max_concurrent: 4,
53 poll_interval: 30,
54 extends: vec![],
55 rules_file: None,
56 file_locking: false,
57 worktree: false,
58 on_close: None,
59 on_fail: None,
60 verify_timeout: None,
61 review: None,
62 user: None,
63 user_email: None,
64 auto_commit: false,
65 commit_template: None,
66 research: None,
67 run_model: None,
68 plan_model: None,
69 review_model: None,
70 research_model: None,
71 batch_verify: false,
72 memory_reserve_mb: 0,
73 notify: None,
74 }
75 .save(&bd)
76 .unwrap();
77 (dir, bd)
78 }
79
80 #[test]
81 fn get_existing() {
82 let (_dir, bd) = setup();
83 create::create(&bd, minimal_params("My task")).unwrap();
84 let r = get(&bd, "1").unwrap();
85 assert_eq!(r.unit.title, "My task");
86 assert!(r.path.exists());
87 }
88
89 #[test]
90 fn get_archived_when_active_missing() {
91 let (_dir, bd) = setup();
92 let archive_dir = bd.join("archive/2026/04");
93 fs::create_dir_all(&archive_dir).unwrap();
94
95 let mut unit = Unit::new("1", "Archived task");
96 unit.is_archived = true;
97 let slug = title_to_slug(&unit.title);
98 let archived_path = archive_dir.join(format!("1-{}.md", slug));
99 unit.to_file(&archived_path).unwrap();
100
101 let r = get(&bd, "1").unwrap();
102 assert_eq!(r.unit.title, "Archived task");
103 assert_eq!(r.path, archived_path);
104 assert!(r.unit.is_archived);
105 }
106
107 #[test]
108 fn get_nonexistent() {
109 let (_dir, bd) = setup();
110 assert!(get(&bd, "99").is_err());
111 }
112}