feat: add main features
Agents for developpement added + frontend add + backend added. Git viewer created + agent and template creator + layout and project creator
This commit is contained in:
115
crates/application/src/project/open.rs
Normal file
115
crates/application/src/project/open.rs
Normal file
@ -0,0 +1,115 @@
|
||||
//! [`OpenProject`] and [`ListProjects`] (ARCHITECTURE §6).
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use domain::ports::{FileSystem, ProjectStore, RemotePath};
|
||||
use domain::{AgentManifest, Project, ProjectId};
|
||||
|
||||
use crate::error::AppError;
|
||||
|
||||
use super::meta::{
|
||||
from_json_bytes, join_root, ProjectMeta, AGENTS_FILE, IDEAI_DIR, PROJECT_FILE,
|
||||
};
|
||||
|
||||
/// Input for [`OpenProject::execute`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OpenProjectInput {
|
||||
/// Id of the project to open (as known by the registry).
|
||||
pub project_id: ProjectId,
|
||||
}
|
||||
|
||||
/// Output of [`OpenProject::execute`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct OpenProjectOutput {
|
||||
/// The opened project (registry source of truth for id/name/root/remote).
|
||||
pub project: Project,
|
||||
/// The project-local meta read from `.ideai/project.json`, if present and
|
||||
/// readable. Read tolerantly: a missing/corrupt file does not fail the open.
|
||||
pub meta: Option<ProjectMeta>,
|
||||
/// The agent manifest read from `.ideai/agents.json`, if present. Tolerant:
|
||||
/// absent in a brand-new project.
|
||||
pub manifest: Option<AgentManifest>,
|
||||
}
|
||||
|
||||
/// Loads a project, its `.ideai/project.json` meta and (tolerantly) its manifest.
|
||||
pub struct OpenProject {
|
||||
store: Arc<dyn ProjectStore>,
|
||||
fs: Arc<dyn FileSystem>,
|
||||
}
|
||||
|
||||
impl OpenProject {
|
||||
/// Builds the use case from its injected ports.
|
||||
#[must_use]
|
||||
pub fn new(store: Arc<dyn ProjectStore>, fs: Arc<dyn FileSystem>) -> Self {
|
||||
Self { store, fs }
|
||||
}
|
||||
|
||||
/// Executes the open.
|
||||
///
|
||||
/// # Errors
|
||||
/// - [`AppError::NotFound`] if the registry has no such project,
|
||||
/// - [`AppError::Store`] on registry I/O failure.
|
||||
///
|
||||
/// Reading the `.ideai/` files is **tolerant**: their absence or a parse
|
||||
/// failure yields `None` rather than an error, so a project whose `.ideai/`
|
||||
/// was deleted can still be opened.
|
||||
pub async fn execute(&self, input: OpenProjectInput) -> Result<OpenProjectOutput, AppError> {
|
||||
let project = self.store.load_project(input.project_id).await?;
|
||||
|
||||
let meta = self
|
||||
.read_optional_json::<ProjectMeta>(&project, PROJECT_FILE)
|
||||
.await;
|
||||
let manifest = self
|
||||
.read_optional_json::<AgentManifest>(&project, AGENTS_FILE)
|
||||
.await;
|
||||
|
||||
Ok(OpenProjectOutput {
|
||||
project,
|
||||
meta,
|
||||
manifest,
|
||||
})
|
||||
}
|
||||
|
||||
/// Reads and parses a JSON file inside the project's `.ideai/`, tolerating
|
||||
/// any failure (missing file, I/O error, parse error) by returning `None`.
|
||||
async fn read_optional_json<T: for<'de> serde::Deserialize<'de>>(
|
||||
&self,
|
||||
project: &Project,
|
||||
file: &str,
|
||||
) -> Option<T> {
|
||||
let path = RemotePath::new(join_root(&project.root, &format!("{IDEAI_DIR}/{file}")));
|
||||
match self.fs.read(&path).await {
|
||||
Ok(bytes) => from_json_bytes::<T>(&bytes).ok(),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Output of [`ListProjects::execute`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ListProjectsOutput {
|
||||
/// All projects known to the registry.
|
||||
pub projects: Vec<Project>,
|
||||
}
|
||||
|
||||
/// Lists the projects known to the registry.
|
||||
pub struct ListProjects {
|
||||
store: Arc<dyn ProjectStore>,
|
||||
}
|
||||
|
||||
impl ListProjects {
|
||||
/// Builds the use case from its injected port.
|
||||
#[must_use]
|
||||
pub fn new(store: Arc<dyn ProjectStore>) -> Self {
|
||||
Self { store }
|
||||
}
|
||||
|
||||
/// Executes the listing.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`AppError::Store`] on registry I/O failure.
|
||||
pub async fn execute(&self) -> Result<ListProjectsOutput, AppError> {
|
||||
let projects = self.store.list_projects().await?;
|
||||
Ok(ListProjectsOutput { projects })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user