| 1 |
|
| 2 |
|
| 3 |
|
| 4 |
|
| 5 |
|
| 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 |
|
| 14 |
pub const KINDS: &[&str] = &["project.added", "project.linked", "project.unlinked"]; |
| 15 |
|
| 16 |
|
| 17 |
pub fn default_name(path: &Path) -> Result<String> { |
| 18 |
let base = path.canonicalize()?.file_name().and_then(|n| n.to_str()).map(str::to_lowercase).context("the path has no name")?; |
| 19 |
Ok(base.chars().map(|c| if c.is_ascii_alphanumeric() || c == '-' { c } else { '-' }).collect()) |
| 20 |
} |
| 21 |
|
| 22 |
|
| 23 |
pub async fn project(conn: &mut SqliteConnection, e: &Event) -> sqlx::Result<()> { |
| 24 |
let p = e.json(); |
| 25 |
let s = |k: &str| p.get(k).and_then(Json::as_str).map(String::from); |
| 26 |
match e.kind.as_str() { |
| 27 |
"project.added" => { |
| 28 |
let Some(name) = s("name") else { return Ok(()) }; |
| 29 |
sqlx::query("INSERT OR REPLACE INTO projects (name, path, description, url, seq) VALUES (?, ?, ?, ?, ?)") |
| 30 |
.bind(&name) |
| 31 |
.bind(s("path").filter(|p| !p.is_empty())) |
| 32 |
.bind(s("description").unwrap_or_default()) |
| 33 |
.bind(s("url")) |
| 34 |
.bind(e.seq as i64) |
| 35 |
.execute(&mut *conn) |
| 36 |
.await?; |
| 37 |
} |
| 38 |
"project.linked" | "project.unlinked" => { |
| 39 |
let (Some(project), Some(on)) = (s("project"), s("depends_on")) else { return Ok(()) }; |
| 40 |
let sql = if e.kind == "project.linked" { |
| 41 |
"INSERT OR IGNORE INTO project_relations (project, depends_on) VALUES (?, ?)" |
| 42 |
} else { |
| 43 |
"DELETE FROM project_relations WHERE project = ? AND depends_on = ?" |
| 44 |
}; |
| 45 |
sqlx::query(sql).bind(project).bind(on).execute(&mut *conn).await?; |
| 46 |
} |
| 47 |
_ => {} |
| 48 |
} |
| 49 |
Ok(()) |
| 50 |
} |