1use crate::cli::Cli;
2use crate::config::Config;
3use crate::config_manager::ConfigManager;
4use crate::errors::JourneyError;
5use crate::vault::Vault;
6use chrono::{Local, NaiveDate};
7use std::env;
8use std::process::Command;
9
10#[derive(Debug, Clone)]
11struct DailyNotesConfig {
12 enabled: bool,
13 format: String,
14 folder: String,
15 template: Option<String>,
16}
17
18#[derive(Debug, Clone)]
19struct PeriodicNotesConfig {
20 enabled: bool,
21}
22
23#[derive(Debug, Clone)]
24struct JournalsConfig {
25 name: String,
26}
27
28#[derive(Debug, Clone)]
29struct ObsidianPluginConfigs {
30 daily_notes: Option<DailyNotesConfig>,
31 periodic_notes: Option<PeriodicNotesConfig>,
32 journals: Vec<JournalsConfig>,
33}
34
35impl ObsidianPluginConfigs {
36 fn new() -> Self {
37 Self {
38 daily_notes: None,
39 periodic_notes: None,
40 journals: Vec::new(),
41 }
42 }
43}
44
45#[derive(Clone)]
46pub struct CliArgs {
47 pub vault: Option<String>,
48 pub date: Option<String>,
49 pub relative_date: Option<i64>,
50 pub time: Option<String>,
51 pub time_format: Option<String>,
52}
53
54pub struct App {
55 config_manager: ConfigManager,
56 config: Config,
57}
58
59impl App {
60 pub fn new() -> Result<Self, JourneyError> {
61 let config_manager = ConfigManager::new()?;
62
63 if !config_manager.config_exists() {
65 return Err(JourneyError::Config(
66 "No configuration file found. Please run 'journey --init --path <path>' to create your first vault.".to_string()
67 ));
68 }
69 let config = config_manager.load_config()?;
70
71 Ok(Self {
72 config_manager,
73 config,
74 })
75 }
76
77 pub fn new_for_init() -> Result<Self, JourneyError> {
78 let config_manager = ConfigManager::new()?;
79 let config = config_manager.load_config()?;
80
81 Ok(Self {
82 config_manager,
83 config,
84 })
85 }
86
87 pub fn run(&mut self, cli: Cli) -> Result<(), JourneyError> {
88 match cli.command {
89 Some(cmd) => {
90 let vault = cli.vault.clone();
92 let date = cli.date.clone();
93 let relative_date = cli.relative_date;
94 let time = cli.time.clone();
95 let time_format = cli.time_format.clone();
96 self.handle_command_with_args(cmd, vault, date, relative_date, time, time_format)
97 }
98 None => self.handle_default_behavior(&cli),
99 }
100 }
101
102 pub fn run_journeyctl(&mut self, cli: crate::journeyctl::JourneyCtlCli) -> Result<(), JourneyError> {
103 match cli.command {
104 Some(cmd) => self.run_journeyctl_command(cmd),
105 None => Err(JourneyError::Config("No command provided".to_string()))
106 }
107 }
108
109 pub fn run_journeyctl_command(&mut self, command: crate::journeyctl::Commands) -> Result<(), JourneyError> {
110 match command {
111 crate::journeyctl::Commands::Init { path, name, vault_type: _, obsidian } => {
112 self.init_vault(path, name, obsidian)
113 }
114 crate::journeyctl::Commands::List => {
115 self.list_vaults()
116 }
117 crate::journeyctl::Commands::SetDefault { vault_name } => {
118 self.set_default_vault(&vault_name)
119 }
120 crate::journeyctl::Commands::UnsetDefault => {
121 self.unset_default_vault()
122 }
123 crate::journeyctl::Commands::ShowDefault => {
124 self.show_default_vault()
125 }
126 crate::journeyctl::Commands::UnlistVault { vault_name } => {
127 self.unlist_vault(&vault_name)
128 }
129 crate::journeyctl::Commands::Today { vault, verbose } => {
130 self.show_today_file(vault, verbose)
131 }
132 }
133 }
134
135 fn handle_command_with_args(&mut self, cmd: crate::cli::Commands, vault: Option<String>, date: Option<String>, relative_date: Option<i64>, time: Option<String>, time_format: Option<String>) -> Result<(), JourneyError> {
136 match cmd {
137 crate::cli::Commands::Add { content } => {
138 let cli_args = CliArgs { vault, date, relative_date, time, time_format };
139 self.add_note(&content, &cli_args, None)
140 }
141 crate::cli::Commands::List => {
142 let cli_args = CliArgs { vault, date, relative_date, time, time_format };
143 self.list_notes(&cli_args, false, None)
144 }
145 crate::cli::Commands::Edit => {
146 let cli_args = CliArgs { vault, date, relative_date, time, time_format };
147 self.edit_notes(&cli_args)
148 }
149 }
150 }
151
152 fn handle_default_behavior(&mut self, cli: &Cli) -> Result<(), JourneyError> {
153 let cli_args = CliArgs {
154 vault: cli.vault.clone(),
155 date: cli.date.clone(),
156 relative_date: cli.relative_date,
157 time: cli.time.clone(),
158 time_format: cli.time_format.clone(),
159 };
160
161 if cli.list {
162 self.list_notes(&cli_args, cli.header, cli.category.as_deref())
163 } else if cli.edit {
164 self.edit_notes(&cli_args)
165 } else if cli.stdin {
166 self.handle_stdin_input(&cli_args, cli.category.as_deref())
167 } else if let Some(note) = &cli.add_note {
168 self.add_note(note, &cli_args, cli.category.as_deref())
169 } else if !cli.note_content.is_empty() {
170 let content = cli.note_content.join(" ");
172 self.add_note(&content, &cli_args, cli.category.as_deref())
173 } else {
174 self.list_notes(&cli_args, cli.header, cli.category.as_deref())
176 }
177 }
178
179 pub fn init_vault(&mut self, path: std::path::PathBuf, name: Option<String>, obsidian: bool) -> Result<(), JourneyError> {
180 if obsidian {
181 self.init_obsidian_vault(path, name)
182 } else {
183 self.init_regular_vault(path, name)
184 }
185 }
186
187 fn init_regular_vault(&mut self, path: std::path::PathBuf, name: Option<String>) -> Result<(), JourneyError> {
188 std::fs::create_dir_all(&path)?;
190
191 let vault_name = if let Some(name) = name {
193 name
194 } else {
195 match path.file_name().and_then(|n| n.to_str()) {
196 Some(s) if !s.is_empty() => s.to_string(),
197 _ => return Err(JourneyError::Config("Invalid path: cannot extract basename".to_string())),
198 }
199 };
200
201 let locale = self.get_system_locale();
203
204 let vault_config = crate::config::VaultConfig {
206 name: vault_name.clone(),
207 path,
208 locale,
209 phrases: std::collections::HashMap::new(),
210 section_header: None,
211 section_header_work: None,
212 section_header_personal: None,
213 section_header_health: None,
214 section_header_meetings: None,
215 table_headers: None,
216 date_format: None,
217 template_file: None,
218 file_path_format: None,
219 list_type: None,
220 section_name: None,
221 weekly_format: None,
222 monthly_format: None,
223 quarterly_format: None,
224 yearly_format: None,
225 note_format: None,
226 };
227
228 self.config.add_vault(vault_config);
230 self.config_manager.save_config(&self.config)?;
231
232 println!("Vault '{}' initialized successfully!", vault_name);
233 Ok(())
234 }
235
236 fn init_obsidian_vault(&mut self, path: std::path::PathBuf, name: Option<String>) -> Result<(), JourneyError> {
237 if !path.exists() {
239 return Err(JourneyError::Config(format!("Obsidian vault path does not exist: {}", path.display())));
240 }
241
242 let obsidian_dir = path.join(".obsidian");
244 if !obsidian_dir.exists() {
245 return Err(JourneyError::Config(format!("Path is not a valid Obsidian vault (missing .obsidian directory): {}", path.display())));
246 }
247
248 let vault_name = if let Some(name) = name {
250 name
251 } else {
252 path.file_name()
253 .and_then(|n| n.to_str())
254 .ok_or_else(|| JourneyError::Config("Invalid path: cannot extract basename".to_string()))?
255 .to_string()
256 };
257
258 let plugin_configs = self.check_obsidian_plugins(&obsidian_dir)?;
260
261 println!("🔍 Detected Obsidian plugins:");
263 if let Some(_) = &plugin_configs.daily_notes {
264 println!(" ✅ Daily Notes (core plugin) - enabled");
265 } else {
266 println!(" ❌ Daily Notes (core plugin) - not enabled");
267 }
268
269 if let Some(_) = &plugin_configs.periodic_notes {
270 println!(" ✅ Periodic Notes plugin - enabled");
271 } else {
272 println!(" ❌ Periodic Notes plugin - not enabled");
273 }
274
275 if !plugin_configs.journals.is_empty() {
276 println!(" ✅ Journals plugin - enabled ({} journal(s) configured)", plugin_configs.journals.len());
277 for journal in &plugin_configs.journals {
278 println!(" 📝 Journal: '{}'", journal.name);
279 }
280 } else {
281 println!(" ❌ Journals plugin - not configured");
282 }
283
284 let locale = self.get_system_locale();
286
287 let mut vault_config = crate::config::VaultConfig {
289 name: vault_name.clone(),
290 path,
291 locale,
292 phrases: std::collections::HashMap::new(),
293 section_header: None,
294 section_header_work: None,
295 section_header_personal: None,
296 section_header_health: None,
297 section_header_meetings: None,
298 table_headers: None,
299 date_format: None,
300 template_file: None,
301 file_path_format: None,
302 list_type: None,
303 section_name: None,
304 weekly_format: None,
305 monthly_format: None,
306 quarterly_format: None,
307 yearly_format: None,
308 note_format: None,
309 };
310
311 self.apply_obsidian_config(&mut vault_config, &plugin_configs)?;
313
314 let mut vault_count = 0;
316 for journal in &plugin_configs.journals {
317 let mut journal_vault_config = vault_config.clone();
318
319 journal_vault_config.name = format!("{}-{}", vault_name, journal.name);
321
322 self.config.add_vault(journal_vault_config);
326 vault_count += 1;
327 }
328
329 if vault_count == 0 {
331 self.config.add_vault(vault_config);
332 vault_count = 1;
333 }
334
335 self.config_manager.save_config(&self.config)?;
337
338 println!();
339 println!("🎉 Obsidian vault '{}' initialized successfully with {} vault(s)!", vault_name, vault_count);
340
341 println!();
343 println!("⚠️ IMPORTANT: Please review your configuration and add the missing essential information:");
344 println!(" • section_header: The default section header in the daily note where journey will put your notes (e.g., '## Todays notes')");
345 println!(" • list_type: The format for your notes ('bullet' or 'table')");
346 println!(" • file_path_format: template string journey uses to determine the location of the daily note");
347 println!(" • It can contain variables like {{year}}, {{month}}, {{day}}, {{weekday}}, {{weekday_short}}, {{month_name}}, {{month_short}}, etc");
348 println!(" • The variables are replaced with the actual values when the note is added");
349 println!();
350 println!();
351 println!(" You can edit the configuration file at: {}", self.config_manager.config_path.display());
352 println!();
353
354 if let Some(daily_notes) = &plugin_configs.daily_notes {
356 println!("📅 Daily Notes: format='{}', folder='{}'", daily_notes.format, daily_notes.folder);
357 }
358
359 if let Some(periodic_notes) = &plugin_configs.periodic_notes {
360 println!("📊 Periodic Notes: enabled={}", periodic_notes.enabled);
361 }
362
363 if !plugin_configs.journals.is_empty() {
364 println!("📚 Journals: {} configured", plugin_configs.journals.len());
365 }
366
367 Ok(())
368 }
369
370 fn check_obsidian_plugins(&self, obsidian_dir: &std::path::Path) -> Result<ObsidianPluginConfigs, JourneyError> {
371 let mut configs = ObsidianPluginConfigs::new();
372
373 if let Ok(daily_notes_config) = self.check_daily_notes_plugin(obsidian_dir) {
375 configs.daily_notes = Some(daily_notes_config);
376 }
377
378 if let Ok(periodic_notes_config) = self.check_periodic_notes_plugin(obsidian_dir) {
380 configs.periodic_notes = Some(periodic_notes_config);
381 }
382
383 if let Ok(journals_configs) = self.check_journals_plugin(obsidian_dir) {
385 configs.journals = journals_configs;
386 }
387
388 Ok(configs)
389 }
390
391 fn check_daily_notes_plugin(&self, obsidian_dir: &std::path::Path) -> Result<DailyNotesConfig, JourneyError> {
392 let app_json_path = obsidian_dir.join("app.json");
393 if !app_json_path.exists() {
394 return Err(JourneyError::Config("Obsidian app.json not found".to_string()));
395 }
396
397 let app_json_content = std::fs::read_to_string(&app_json_path)?;
398 let app_config: serde_json::Value = serde_json::from_str(&app_json_content)?;
399
400 if let Some(daily_notes) = app_config.get("dailyNotes") {
402 if let Some(enabled) = daily_notes.get("enabled") {
403 if enabled.as_bool().unwrap_or(false) {
404 let format = daily_notes.get("format").and_then(|v| v.as_str()).unwrap_or("YYYY-MM-DD").to_string();
405 let folder = daily_notes.get("folder").and_then(|v| v.as_str()).unwrap_or("/").to_string();
406 let template = daily_notes.get("template").and_then(|v| v.as_str()).map(|s| s.to_string());
407
408 return Ok(DailyNotesConfig {
409 enabled: true,
410 format,
411 folder,
412 template,
413 });
414 }
415 } else {
416 return Err(JourneyError::Config("Daily Notes plugin not enabled".to_string()));
418 }
419 }
420
421 Err(JourneyError::Config("Daily Notes plugin not enabled".to_string()))
422 }
423
424 fn check_periodic_notes_plugin(&self, obsidian_dir: &std::path::Path) -> Result<PeriodicNotesConfig, JourneyError> {
425 let plugins_dir = obsidian_dir.join("plugins");
426 let periodic_notes_dir = plugins_dir.join("periodic-notes");
427
428 if !periodic_notes_dir.exists() {
429 return Err(JourneyError::Config("Periodic Notes plugin not installed".to_string()));
430 }
431
432 let data_json_path = periodic_notes_dir.join("data.json");
433 if !data_json_path.exists() {
434 return Err(JourneyError::Config("Periodic Notes plugin data not found".to_string()));
435 }
436
437 let data_content = std::fs::read_to_string(&data_json_path)?;
438 let plugin_data: serde_json::Value = serde_json::from_str(&data_content)?;
439
440 if let Some(enabled) = plugin_data.get("enabled") {
442 if !enabled.as_bool().unwrap_or(false) {
443 return Err(JourneyError::Config("Periodic Notes plugin not enabled".to_string()));
444 }
445 } else {
446 return Err(JourneyError::Config("Periodic Notes plugin not enabled".to_string()));
448 }
449
450 Ok(PeriodicNotesConfig {
452 enabled: true,
453 })
454 }
455
456 fn check_journals_plugin(&self, obsidian_dir: &std::path::Path) -> Result<Vec<JournalsConfig>, JourneyError> {
457 let plugins_dir = obsidian_dir.join("plugins");
458 let journals_dir = plugins_dir.join("journals");
459
460 if !journals_dir.exists() {
461 return Err(JourneyError::Config("Journals plugin not installed".to_string()));
462 }
463
464 let data_json_path = journals_dir.join("data.json");
465 if !data_json_path.exists() {
466 return Err(JourneyError::Config("Journals plugin data not found".to_string()));
467 }
468
469 let data_content = std::fs::read_to_string(&data_json_path)?;
470 let plugin_data: serde_json::Value = serde_json::from_str(&data_content)?;
471
472 if !plugin_data.get("journals").is_some() {
475 return Err(JourneyError::Config("Journals plugin not configured".to_string()));
476 }
477
478 let journals = plugin_data.get("journals")
480 .ok_or_else(|| JourneyError::Config("No journals configured".to_string()))?;
481
482 let mut journal_configs = Vec::new();
483
484 if let Some(journals_obj) = journals.as_object() {
485 for (journal_name, _journal_data) in journals_obj {
486 journal_configs.push(JournalsConfig {
488 name: journal_name.clone(),
489 });
490 }
491 } else {
492 return Err(JourneyError::Config("Invalid journals configuration".to_string()));
493 }
494
495 if journal_configs.is_empty() {
496 return Err(JourneyError::Config("No journal entries found".to_string()));
497 }
498
499 Ok(journal_configs)
500 }
501
502 fn apply_obsidian_config(&mut self, vault_config: &mut crate::config::VaultConfig, plugin_configs: &ObsidianPluginConfigs) -> Result<(), JourneyError> {
503 if let Some(daily_notes) = &plugin_configs.daily_notes {
505 if daily_notes.enabled {
506 vault_config.date_format = Some(daily_notes.format.clone());
507 vault_config.file_path_format = Some(daily_notes.folder.clone());
508
509 if let Some(template) = &daily_notes.template {
510 vault_config.template_file = Some(template.clone());
511 }
512 }
513 }
514
515 if let Some(periodic_notes) = &plugin_configs.periodic_notes {
517 if periodic_notes.enabled {
518 }
520 }
521
522 Ok(())
525 }
526
527 fn add_note(&mut self, content: &str, cli: &CliArgs, category: Option<&str>) -> Result<(), JourneyError> {
528 let vault = self.get_vault(cli.vault.as_deref())?;
529 let date = self.parse_date(cli)?;
530 let time = self.parse_time(cli)?;
531
532 let timestamp = if let Some(time) = time {
533 vault.date_handler.combine_date_time(date, time)
534 } else {
535 let current_time = vault.date_handler.get_current_datetime().time();
537 vault.date_handler.combine_date_time(date, current_time)
538 };
539
540 vault.add_note_with_category(content, Some(timestamp), category)?;
541 println!("Note added successfully!");
542 Ok(())
543 }
544
545 fn list_notes(&self, cli: &CliArgs, header: bool, category: Option<&str>) -> Result<(), JourneyError> {
546 let vault = self.get_vault(cli.vault.as_deref())?;
547 let date = self.parse_date(cli)?;
548
549 let notes = vault.list_notes_with_category(date, category)?;
550
551 if notes.is_empty() {
552 println!("No notes found for {}", vault.date_handler.format_date(date));
553 } else {
554 let has_table_format = notes.iter().any(|note| note.trim().starts_with("|"));
555
556 if !header && !has_table_format {
558 println!("Notes for {}:", vault.date_handler.format_date(date));
559 }
560
561 if header && has_table_format {
563 let (time_header, content_header) = vault.get_table_headers();
564 println!("| {} | {} |", time_header, content_header);
565 println!("|------|----------|");
566 }
567
568 for note in notes {
569 println!("{}", note);
570 }
571 }
572
573 Ok(())
574 }
575
576 fn edit_notes(&self, cli: &CliArgs) -> Result<(), JourneyError> {
577 let vault = self.get_vault(cli.vault.as_deref())?;
578 let date = self.parse_date(cli)?;
579 let note_path = vault.get_editor_path(date);
580
581 let editor = env::var("EDITOR").unwrap_or_else(|_| "nano".to_string());
583
584 let status = Command::new(&editor)
586 .arg(¬e_path)
587 .status()
588 .map_err(|_| JourneyError::EditorNotFound(editor))?;
589
590 if !status.success() {
591 eprintln!("Editor exited with non-zero status");
592 }
593
594 Ok(())
595 }
596
597
598 pub fn get_vault(&self, vault_name: Option<&str>) -> Result<Vault, JourneyError> {
599 if self.config.vaults.is_empty() {
601 return Err(JourneyError::VaultNotFound("No vaults configured. Use 'journey init' to create one.".to_string()));
602 }
603
604 let vault_config = if let Some(name) = vault_name {
605 self.config.get_vault(name)
607 .ok_or_else(|| JourneyError::VaultNotFound(name.to_string()))?
608 } else {
609 if let Some(default_name) = &self.config.default_vault {
611 self.config.get_vault(default_name)
613 .ok_or_else(|| JourneyError::VaultNotFound(format!("Default vault '{}' not found", default_name)))?
614 } else if self.config.vaults.len() == 1 {
615 self.config.vaults.values().next()
617 .ok_or_else(|| JourneyError::VaultNotFound("No vaults configured".to_string()))?
618 } else if self.config.vaults.len() > 1 {
619 return Err(JourneyError::VaultNotFound(
621 format!("Multiple vaults available: {}. Please specify --vault or set a default vault",
622 self.config.vaults.keys().map(|s| s.as_str()).collect::<Vec<_>>().join(", "))
623 ));
624 } else {
625 return Err(JourneyError::VaultNotFound("No vaults configured. Use 'journey init' to create one.".to_string()));
627 }
628 };
629
630 Ok(Vault::new(vault_config.clone()))
631 }
632
633 pub fn parse_date(&self, cli: &CliArgs) -> Result<NaiveDate, JourneyError> {
634 if let Some(date_str) = &cli.date {
635 let vault = self.get_vault(cli.vault.as_deref())?;
636 vault.date_handler.parse_date_with_format_override(date_str, vault.config.date_format.as_deref())
637 } else if let Some(days_ago) = cli.relative_date {
638 let vault = self.get_vault(cli.vault.as_deref())?;
639 Ok(vault.date_handler.parse_relative_date(days_ago))
640 } else {
641 Ok(Local::now().date_naive())
642 }
643 }
644
645 fn parse_time(&self, cli: &CliArgs) -> Result<Option<chrono::NaiveTime>, JourneyError> {
646 if let Some(time_str) = &cli.time {
647 let vault = self.get_vault(cli.vault.as_deref())?;
648 Ok(Some(vault.date_handler.parse_time_with_format_override(time_str, cli.time_format.as_deref())?))
649 } else {
650 Ok(None)
651 }
652 }
653
654 fn handle_stdin_input(&mut self, cli: &CliArgs, category: Option<&str>) -> Result<(), JourneyError> {
655 use std::io::{self, BufRead};
656
657 let stdin = io::stdin();
658 let lines = stdin.lock().lines();
659
660 let mut note_count = 0;
661
662 for line in lines {
663 let line = line?;
664 let trimmed = line.trim();
665
666 if trimmed.is_empty() {
668 continue;
669 }
670
671 self.add_note(trimmed, cli, category)?;
673 note_count += 1;
674 }
675
676 if note_count > 0 {
677 println!("Added {} notes from stdin", note_count);
678 } else {
679 println!("No content received from stdin");
680 }
681
682 Ok(())
683 }
684
685 fn get_system_locale(&self) -> String {
686 env::var("LANG")
688 .or_else(|_| env::var("LC_ALL"))
689 .unwrap_or_else(|_| "en-US".to_string())
690 }
691
692 fn set_default_vault(&mut self, vault_name: &str) -> Result<(), JourneyError> {
693 match self.config.set_default_vault(vault_name) {
694 Ok(()) => {
695 self.config_manager.save_config(&self.config)?;
696 println!("Default vault set to '{}'", vault_name);
697 Ok(())
698 }
699 Err(error) => Err(JourneyError::Config(error))
700 }
701 }
702
703 fn unset_default_vault(&mut self) -> Result<(), JourneyError> {
704 self.config.unset_default_vault();
705 self.config_manager.save_config(&self.config)?;
706 println!("Default vault unset");
707 Ok(())
708 }
709
710 fn show_default_vault(&self) -> Result<(), JourneyError> {
711 if let Some(default_name) = &self.config.default_vault {
712 if let Some(vault) = self.config.get_vault(default_name) {
713 println!("Default vault: {} ({})", default_name, vault.path.display());
714 } else {
715 println!("Default vault '{}' is set but vault not found", default_name);
716 }
717 } else {
718 println!("No default vault set");
719 if self.config.vaults.len() > 1 {
720 println!("Available vaults: {}",
721 self.config.vaults.keys().map(|s| s.as_str()).collect::<Vec<_>>().join(", "));
722 }
723 }
724 Ok(())
725 }
726
727 pub fn unlist_vault(&mut self, vault_name: &str) -> Result<(), JourneyError> {
728 if !self.config.vaults.contains_key(vault_name) {
730 return Err(JourneyError::VaultNotFound(format!("Vault '{}' not found", vault_name)));
731 }
732
733 self.config.remove_vault(vault_name)
735 .map_err(|e| JourneyError::Config(e))?;
736
737 self.config_manager.save_config(&self.config)?;
739
740 println!("Vault '{}' unlisted successfully!", vault_name);
741 Ok(())
742 }
743
744 #[doc(hidden)]
746 pub fn get_config(&self) -> &Config {
747 &self.config
748 }
749
750 fn list_vaults(&self) -> Result<(), JourneyError> {
751 if self.config.vaults.is_empty() {
752 println!("No vaults configured.");
753 println!("Use 'journeyctl init --path <path> --name <name>' to create your first vault.");
754 return Ok(());
755 }
756
757 println!("Configured vaults:");
758 println!();
759
760 for (name, vault) in &self.config.vaults {
761 let is_default = self.config.default_vault.as_ref() == Some(name);
762 let default_marker = if is_default { " (default)" } else { "" };
763
764 println!(" {}: {}{}", name, vault.path.display(), default_marker);
765
766 if let Some(section_header) = &vault.section_header {
768 println!(" Section: {}", section_header);
769 }
770 if let Some(template_file) = &vault.template_file {
771 println!(" Template: {}", template_file);
772 }
773 if let Some(file_path_format) = &vault.file_path_format {
774 println!(" Path format: {}", file_path_format);
775 }
776 if !vault.phrases.is_empty() {
777 println!(" Phrases: {} configured", vault.phrases.len());
778 }
779 println!();
780 }
781
782 if let Some(default_name) = &self.config.default_vault {
783 println!("Default vault: {}", default_name);
784 } else {
785 println!("No default vault set");
786 }
787
788 Ok(())
789 }
790
791 fn show_today_file(&self, vault_name: Option<String>, verbose: bool) -> Result<(), JourneyError> {
792 let vault = self.get_vault(vault_name.as_deref())?;
793 let today = vault.date_handler.get_current_datetime().date_naive();
794 let file_path = vault.get_note_path(today);
795
796 if verbose {
797 println!("Today's file location: {}", file_path.display());
798
799 if file_path.exists() {
800 println!("File exists: Yes");
801 } else {
802 println!("File exists: No");
803 }
804 } else {
805 println!("{}", file_path.display());
806 }
807
808 Ok(())
809 }
810}