166 lines
4.9 KiB
Rust
166 lines
4.9 KiB
Rust
use std::path::Path;
|
|
use std::time::UNIX_EPOCH;
|
|
|
|
use sqlx::Row;
|
|
|
|
use super::JavaVersion;
|
|
|
|
/// A Java installation found by a system scan, cached together with the
|
|
/// file signature of its executable so staleness can be detected cheaply.
|
|
#[derive(Debug, Clone)]
|
|
pub struct DiscoveredJava {
|
|
pub java: JavaVersion,
|
|
pub file_size: i64,
|
|
pub file_mtime_ms: i64,
|
|
}
|
|
|
|
/// Returns (size, mtime in milliseconds) of the file at `path`, or None
|
|
/// if it does not exist or cannot be read.
|
|
pub fn java_file_signature(path: &Path) -> Option<(i64, i64)> {
|
|
let metadata = std::fs::metadata(path).ok()?;
|
|
if !metadata.is_file() {
|
|
return None;
|
|
}
|
|
let mtime_ms = metadata
|
|
.modified()
|
|
.ok()?
|
|
.duration_since(UNIX_EPOCH)
|
|
.ok()?
|
|
.as_millis() as i64;
|
|
Some((metadata.len() as i64, mtime_ms))
|
|
}
|
|
|
|
impl DiscoveredJava {
|
|
/// Builds a cache entry for a verified Java installation, stamping the
|
|
/// current signature of its executable. Returns None if the executable
|
|
/// can no longer be read.
|
|
pub fn from_java(java: JavaVersion) -> Option<Self> {
|
|
let (file_size, file_mtime_ms) =
|
|
java_file_signature(Path::new(&java.path))?;
|
|
Some(Self {
|
|
java,
|
|
file_size,
|
|
file_mtime_ms,
|
|
})
|
|
}
|
|
|
|
pub async fn get_all(
|
|
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
|
) -> crate::Result<Vec<Self>> {
|
|
let rows = sqlx::query(
|
|
"
|
|
SELECT path, major_version, full_version, architecture,
|
|
file_size, file_mtime_ms, distribution
|
|
FROM discovered_javas
|
|
ORDER BY major_version, path
|
|
",
|
|
)
|
|
.fetch_all(exec)
|
|
.await?;
|
|
|
|
Ok(rows
|
|
.into_iter()
|
|
.map(|row| Self {
|
|
java: JavaVersion {
|
|
parsed_version: row.get::<i64, _>("major_version") as u32,
|
|
version: row.get("full_version"),
|
|
architecture: row.get("architecture"),
|
|
path: row.get("path"),
|
|
distribution: row.get("distribution"),
|
|
},
|
|
file_size: row.get("file_size"),
|
|
file_mtime_ms: row.get("file_mtime_ms"),
|
|
})
|
|
.collect())
|
|
}
|
|
|
|
pub async fn upsert(
|
|
&self,
|
|
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
|
) -> crate::Result<()> {
|
|
sqlx::query(
|
|
"
|
|
INSERT INTO discovered_javas (
|
|
path, major_version, full_version, architecture,
|
|
file_size, file_mtime_ms, distribution
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
ON CONFLICT (path) DO UPDATE SET
|
|
major_version = $2,
|
|
full_version = $3,
|
|
architecture = $4,
|
|
file_size = $5,
|
|
file_mtime_ms = $6,
|
|
distribution = $7
|
|
",
|
|
)
|
|
.bind(&self.java.path)
|
|
.bind(self.java.parsed_version as i64)
|
|
.bind(&self.java.version)
|
|
.bind(&self.java.architecture)
|
|
.bind(self.file_size)
|
|
.bind(self.file_mtime_ms)
|
|
.bind(&self.java.distribution)
|
|
.execute(exec)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn remove(
|
|
path: &str,
|
|
exec: impl sqlx::Executor<'_, Database = sqlx::Sqlite>,
|
|
) -> crate::Result<()> {
|
|
sqlx::query("DELETE FROM discovered_javas WHERE path = $1")
|
|
.bind(path)
|
|
.execute(exec)
|
|
.await?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Replaces the entire cache with the results of a fresh scan.
|
|
pub async fn replace_all(
|
|
pool: &sqlx::SqlitePool,
|
|
entries: &[Self],
|
|
) -> crate::Result<()> {
|
|
let mut transaction = pool.begin().await?;
|
|
|
|
sqlx::query("DELETE FROM discovered_javas")
|
|
.execute(&mut *transaction)
|
|
.await?;
|
|
|
|
for entry in entries {
|
|
sqlx::query(
|
|
"
|
|
INSERT INTO discovered_javas (
|
|
path, major_version, full_version, architecture,
|
|
file_size, file_mtime_ms, distribution
|
|
)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
ON CONFLICT (path) DO UPDATE SET
|
|
major_version = $2,
|
|
full_version = $3,
|
|
architecture = $4,
|
|
file_size = $5,
|
|
file_mtime_ms = $6,
|
|
distribution = $7
|
|
",
|
|
)
|
|
.bind(&entry.java.path)
|
|
.bind(entry.java.parsed_version as i64)
|
|
.bind(&entry.java.version)
|
|
.bind(&entry.java.architecture)
|
|
.bind(entry.file_size)
|
|
.bind(entry.file_mtime_ms)
|
|
.bind(&entry.java.distribution)
|
|
.execute(&mut *transaction)
|
|
.await?;
|
|
}
|
|
|
|
transaction.commit().await?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|