OXYGENHUB

Coverage / crates/spectra-core/src/projects.rs 100.0%

32 of 32 lines run, 7 of 14 functions · run #36 on main · run missed

1
//! Projects (spec §23.1): any kind of work a group of agents shares. A
2
//! project has a name, a description, and optionally a folder and a link;
3
//! Spectra records where the folder is and reads nothing inside it. The
4
//! owner links projects (`project.linked`: one depends on another), which
5
//! is what `to: "downstream"` follows.
6
7
use crate::event::Event;
8
use anyhow::{Context, Result};
9
use serde_json::Value as Json;
10
use sqlx::SqliteConnection;
11
use std::path::Path;
12
13
/// Kinds of event the owner signs about projects.
14
pub const KINDS: &[&str] = &["project.added", "project.linked", "project.unlinked"];
15
16
/// A project's name from a folder: its name, lower-cased, made valid.
1725×
pub fn default_name(path: &Path) -> Result<String> {
1825×
    let base = path.canonicalize()?.file_name().and_then(|n| n.to_str()).map(str::to_lowercase).context("the path has no name")?;
19146×
    Ok(base.chars().map(|c| if c.is_ascii_alphanumeric() || c == '-' { c } else { '-' }).collect())
2025×
}
21
22
/// Apply a project event to the tables.
2342882×
pub async fn project(conn: &mut SqliteConnection, e: &Event) -> sqlx::Result<()> {
2442882×
    let p = e.json();
2542882×
    let s = |k: &str| p.get(k).and_then(Json::as_str).map(String::from);
2642882×
    match e.kind.as_str() {
2742882×
        "project.added" => {
283566×
            let Some(name) = s("name") else { return Ok(()) };
2954×
            sqlx::query("INSERT OR REPLACE INTO projects (name, path, description, url, seq) VALUES (?, ?, ?, ?, ?)")
3054×
                .bind(&name)
3154×
                .bind(s("path").filter(|p| !p.is_empty()))
3254×
                .bind(s("description").unwrap_or_default())
3354×
                .bind(s("url"))
3454×
                .bind(e.seq as i64)
3554×
                .execute(&mut *conn)
3654×
                .await?;
37
        }
3839316×
        "project.linked" | "project.unlinked" => {
394×
            let (Some(project), Some(on)) = (s("project"), s("depends_on")) else { return Ok(()) };
404×
            let sql = if e.kind == "project.linked" {
412×
                "INSERT OR IGNORE INTO project_relations (project, depends_on) VALUES (?, ?)"
42
            } else {
432×
                "DELETE FROM project_relations WHERE project = ? AND depends_on = ?"
44
            };
454×
            sqlx::query(sql).bind(project).bind(on).execute(&mut *conn).await?;
46
        }
4739312×
        _ => {}
48
    }
4939370×
    Ok(())
5042882×
}