| 274 | |
| 275 | impl Command for New { |
| 276 | fn run(&self) -> CliResult<()> { |
| 277 | // Get the view name |
| 278 | let name = self |
| 279 | .name |
| 280 | .as_ref() |
| 281 | .ok_or_else(|| CliError::InvalidArgument { |
| 282 | message: "View name is required".to_string(), |
| 283 | })?; |
| 284 | |
| 285 | // Validate the view name |
| 286 | validate_view_name(name).map_err(|msg| CliError::InvalidArgument { message: msg })?; |
| 287 | |
| 288 | // Find the repository |
| 289 | let repo_root = find_repository_root()?; |
| 290 | let mut repo = Repository::open(&repo_root).map_err(|e| match e { |
| 291 | atomic_repository::RepositoryError::NotFound { path } => CliError::RepositoryNotFound { |
| 292 | searched_path: path.into(), |
| 293 | }, |
| 294 | other => CliError::Repository(other), |
| 295 | })?; |
| 296 | |
| 297 | // Check if the view already exists |
| 298 | if repo.view_exists(name).map_err(CliError::Repository)? { |
| 299 | return Err(CliError::ViewAlreadyExists { |
| 300 | name: name.to_string(), |
| 301 | }); |
| 302 | } |
| 303 | |
| 304 | // ONE creation concept: a Draft overlay whose filter is anchored on a |
| 305 | // parent and may be SEEDED from a source view's change-set |
| 306 | // membership. The graph holds every node a view exposes; --from only |
| 307 | // selects which EXISTING changes the new view's filter starts with. |
| 308 | // |
| 309 | // --from S → anchor on S, seed from S |
| 310 | // --from S --parent P → anchor on P, seed from S |
| 311 | // --parent P | --draft → anchor on P, empty membership |
| 312 | // (default) → anchor on nearest Shared, empty membership |
| 313 | // (bring files in with |
| 314 | // `atomic insert from-view dev`) |
| 315 | if let Some(ref source) = self.from { |
| 316 | if !repo.view_exists(source).map_err(CliError::Repository)? { |
| 317 | return Err(CliError::ViewNotFound { |
| 318 | name: source.to_string(), |
| 319 | }); |
| 320 | } |
| 321 | } |
| 322 | |
| 323 | let (anchor, seed) = resolve_overlay_creation(self.from.as_deref(), self.parent.as_deref()); |
| 324 | |
| 325 | let source_info = if let Some(seed_view) = seed.as_deref() { |
| 326 | Some( |
| 327 | repo.get_view_info(seed_view) |
| 328 | .map_err(CliError::Repository)?, |
| 329 | ) |
| 330 | } else { |
| 331 | None |
| 332 | }; |
| 333 | |