| 74 | |
| 75 | impl Command for List { |
| 76 | fn run(&self) -> CliResult<()> { |
| 77 | // Find the repository |
| 78 | let repo_root = find_repository_root()?; |
| 79 | let repo = Repository::open(&repo_root).map_err(|e| match e { |
| 80 | atomic_repository::RepositoryError::NotFound { path } => CliError::RepositoryNotFound { |
| 81 | searched_path: path.into(), |
| 82 | }, |
| 83 | other => CliError::Repository(other), |
| 84 | })?; |
| 85 | |
| 86 | // Get list of views |
| 87 | let views = repo.list_views().map_err(CliError::Repository)?; |
| 88 | let current = repo.current_view(); |
| 89 | |
| 90 | if views.is_empty() { |
| 91 | println!( |
| 92 | "{}", |
| 93 | hint("No views found. Use 'atomic view create <name>' to create one.") |
| 94 | ); |
| 95 | return Ok(()); |
| 96 | } |
| 97 | |
| 98 | // Sort views alphabetically, but keep current view considerations |
| 99 | let mut sorted_views = views; |
| 100 | sorted_views.sort(); |
| 101 | |
| 102 | // Calculate padding for alignment in verbose mode |
| 103 | let max_name_len = sorted_views.iter().map(|s| s.len()).max().unwrap_or(0); |
| 104 | |
| 105 | for view in sorted_views { |
| 106 | let is_current = view == current; |
| 107 | let marker = if is_current { "*" } else { " " }; |
| 108 | |
| 109 | if self.verbose { |
| 110 | // Get view info for verbose output |
| 111 | match repo.get_view_info(&view) { |
| 112 | Ok(info) => { |
| 113 | let change_word = if info.change_count == 1 { |
| 114 | "change" |
| 115 | } else { |
| 116 | "changes" |
| 117 | }; |
| 118 | let kind_tag = match info.kind_label() { |
| 119 | "draft" => "[draft]", |
| 120 | _ => "[shared]", |
| 121 | }; |
| 122 | let parent_info = match &info.parent_name { |
| 123 | Some(p) => format!(" parent: {}", style_view(p)), |
| 124 | None => String::new(), |
| 125 | }; |
| 126 | println!( |
| 127 | "{} {:<width$} {:<10} ({} {}) state: {}{}", |
| 128 | marker, |
| 129 | style_view(&view), |
| 130 | kind_tag, |
| 131 | info.change_count, |
| 132 | change_word, |
| 133 | info.state_short(), |