返回 DeepSeek-TUI-2026
note.rs
根目录 / crates / tui / src / commands / note.rs
1 //! Note command: append to persistent notes file
2
3 use crate::tui::app::App;
4 use std::fs;
5 use std::io::Write;
6
7 use super::CommandResult;
8
9 /// Append a note to the persistent notes file
10 pub fn note(app: &mut App, content: Option<&str>) -> CommandResult {
11 let note_content = match content {
12 Some(c) => c.trim(),
13 None => {
14 return CommandResult::error("Usage: /note <text>");
15 }
16 };
17
18 if note_content.is_empty() {
19 return CommandResult::error("Note content cannot be empty");
20 }
21
22 // Determine notes path: workspace/.deepseek/notes.md
23 let notes_path = app.workspace.join(".deepseek").join("notes.md");
24
25 // Ensure parent directory exists
26 if let Some(parent) = notes_path.parent()
27 && let Err(e) = fs::create_dir_all(parent)
28 {
29 return CommandResult::error(format!("Failed to create notes directory: {e}"));
30 }
31
32 // Append to notes file
33 let mut file = match fs::OpenOptions::new()
34 .create(true)
35 .append(true)
36 .open(&notes_path)
37 {
38 Ok(f) => f,
39 Err(e) => {
40 return CommandResult::error(format!("Failed to open notes file: {e}"));
41 }
42 };
43
44 // Write separator and note content
45 if let Err(e) = writeln!(file, "\n---\n{}", note_content) {
46 return CommandResult::error(format!("Failed to write note: {e}"));
47 }
48
49 CommandResult::message(format!("Note appended to {}", notes_path.display()))
50 }
51
52 #[cfg(test)]
53 mod tests {
54 use super::*;
55 use crate::config::Config;
56 use crate::tui::app::{App, TuiOptions};
57 use tempfile::TempDir;
58
59 fn create_test_app_with_tmpdir(tmpdir: &TempDir) -> App {
60 let options = TuiOptions {
61 model: "deepseek-v4-pro".to_string(),
62 workspace: tmpdir.path().to_path_buf(),
63 config_path: None,
64 config_profile: None,
65 allow_shell: false,
66 use_alt_screen: true,
67 use_mouse_capture: false,
68 use_bracketed_paste: true,
69 max_subagents: 1,
70 skills_dir: tmpdir.path().join("skills"),
71 memory_path: tmpdir.path().join("memory.md"),
72 notes_path: tmpdir.path().join("notes.txt"),
73 mcp_config_path: tmpdir.path().join("mcp.json"),
74 use_memory: false,
75 start_in_agent_mode: false,
76 skip_onboarding: true,
77 yolo: false,
78 resume_session_id: None,
79 initial_input: None,
80 };
81 App::new(options, &Config::default())
82 }
83
84 #[test]
85 fn test_note_without_content_returns_error() {
86 let tmpdir = TempDir::new().unwrap();
87 let mut app = create_test_app_with_tmpdir(&tmpdir);
88 let result = note(&mut app, None);
89 assert!(result.message.is_some());
90 assert!(result.message.unwrap().contains("Usage: /note"));
91 }
92
93 #[test]
94 fn test_note_with_empty_content_returns_error() {
95 let tmpdir = TempDir::new().unwrap();
96 let mut app = create_test_app_with_tmpdir(&tmpdir);
97 let result = note(&mut app, Some(" "));
98 assert!(result.message.is_some());
99 assert!(result.message.unwrap().contains("cannot be empty"));
100 }
101
102 #[test]
103 fn test_note_appends_to_file() {
104 let tmpdir = TempDir::new().unwrap();
105 let mut app = create_test_app_with_tmpdir(&tmpdir);
106 let result = note(&mut app, Some("Test note content"));
107 assert!(result.message.is_some());
108 let msg = result.message.unwrap();
109 assert!(msg.contains("Note appended to"));
110
111 let notes_path = tmpdir.path().join(".deepseek").join("notes.md");
112 assert!(notes_path.exists());
113 let content = std::fs::read_to_string(&notes_path).unwrap();
114 assert!(content.contains("Test note content"));
115 }
116
117 #[test]
118 fn test_note_multiple_appends() {
119 let tmpdir = TempDir::new().unwrap();
120 let mut app = create_test_app_with_tmpdir(&tmpdir);
121 note(&mut app, Some("First note"));
122 note(&mut app, Some("Second note"));
123
124 let notes_path = tmpdir.path().join(".deepseek").join("notes.md");
125 let content = std::fs::read_to_string(&notes_path).unwrap();
126 assert!(content.contains("First note"));
127 assert!(content.contains("Second note"));
128 // Should have two separators
129 assert_eq!(content.matches("---").count(), 2);
130 }
131 }
132
132 lines RUST