1use crate::build::BuildResult;
24use anyhow::{Context, Result};
25use indexmap::IndexMap;
26use pkgsrc::{PkgName, PkgPath, ScanIndex};
27use rusqlite::{Connection, params};
28use std::path::Path;
29use tracing::debug;
30
31pub struct Database {
33 conn: Connection,
34}
35
36impl Database {
37 pub fn open(path: &Path) -> Result<Self> {
39 if let Some(parent) = path.parent() {
40 std::fs::create_dir_all(parent)
41 .context("Failed to create database directory")?;
42 }
43 let conn = Connection::open(path).context("Failed to open database")?;
44 let db = Self { conn };
45 db.init()?;
46 Ok(db)
47 }
48
49 fn init(&self) -> Result<()> {
50 self.conn.execute_batch(
51 "CREATE TABLE IF NOT EXISTS scan (
52 pkgpath TEXT PRIMARY KEY,
53 data TEXT NOT NULL
54 );
55 CREATE TABLE IF NOT EXISTS build (
56 pkgname TEXT PRIMARY KEY,
57 data TEXT NOT NULL
58 )",
59 )?;
60 Ok(())
61 }
62
63 pub fn store_scan_pkgpath(
65 &self,
66 pkgpath: &str,
67 indexes: &[ScanIndex],
68 ) -> Result<()> {
69 let json = serde_json::to_string(indexes)?;
70 self.conn.execute(
71 "INSERT OR REPLACE INTO scan (pkgpath, data) VALUES (?1, ?2)",
72 params![pkgpath, json],
73 )?;
74 debug!(pkgpath, "Stored scan result");
75 Ok(())
76 }
77
78 pub fn get_all_scan(&self) -> Result<IndexMap<PkgPath, Vec<ScanIndex>>> {
80 let mut stmt = self
81 .conn
82 .prepare("SELECT pkgpath, data FROM scan ORDER BY rowid")?;
83 let mut result = IndexMap::new();
84
85 let rows = stmt.query_map([], |row| {
86 let pkgpath: String = row.get(0)?;
87 let json: String = row.get(1)?;
88 Ok((pkgpath, json))
89 })?;
90
91 for row in rows {
92 let (pkgpath_str, json) = row?;
93 let pkgpath = PkgPath::new(&pkgpath_str)
94 .context("Invalid pkgpath in database")?;
95 let indexes: Vec<ScanIndex> = serde_json::from_str(&json)
96 .context("Failed to deserialize scan data")?;
97 result.insert(pkgpath, indexes);
98 }
99
100 Ok(result)
101 }
102
103 pub fn count_scan(&self) -> Result<i64> {
105 self.conn
106 .query_row("SELECT COUNT(*) FROM scan", [], |row| row.get(0))
107 .context("Failed to count scan")
108 }
109
110 pub fn clear_scan(&self) -> Result<()> {
112 self.conn.execute("DELETE FROM scan", [])?;
113 Ok(())
114 }
115
116 pub fn store_build_pkgname(
118 &self,
119 pkgname: &str,
120 result: &BuildResult,
121 ) -> Result<()> {
122 let json = serde_json::to_string(result)?;
123 self.conn.execute(
124 "INSERT OR REPLACE INTO build (pkgname, data) VALUES (?1, ?2)",
125 params![pkgname, json],
126 )?;
127 debug!(pkgname, "Stored build result");
128 Ok(())
129 }
130
131 pub fn get_all_build(&self) -> Result<IndexMap<PkgName, BuildResult>> {
133 let mut stmt = self
134 .conn
135 .prepare("SELECT pkgname, data FROM build ORDER BY rowid")?;
136 let mut result = IndexMap::new();
137
138 let rows = stmt.query_map([], |row| {
139 let pkgname: String = row.get(0)?;
140 let json: String = row.get(1)?;
141 Ok((pkgname, json))
142 })?;
143
144 for row in rows {
145 let (pkgname_str, json) = row?;
146 let pkgname = PkgName::new(&pkgname_str);
147 let build_result: BuildResult = serde_json::from_str(&json)
148 .context("Failed to deserialize build data")?;
149 result.insert(pkgname, build_result);
150 }
151
152 Ok(result)
153 }
154
155 pub fn count_build(&self) -> Result<i64> {
157 self.conn
158 .query_row("SELECT COUNT(*) FROM build", [], |row| row.get(0))
159 .context("Failed to count build")
160 }
161
162 pub fn clear_build(&self) -> Result<()> {
164 self.conn.execute("DELETE FROM build", [])?;
165 Ok(())
166 }
167}