warehouse/src/index.rs
2022-09-09 18:34:17 -05:00

169 lines
4.6 KiB
Rust

use std::{collections::HashMap, process::Stdio};
use futures_util::StreamExt;
use semver::{Version, VersionReq};
use serde::{Deserialize, Serialize};
use sqlx::{query_as, Type};
use tokio::{
fs::{self, OpenOptions},
io::AsyncWriteExt,
process::Command,
};
use crate::{
db::{DbDep, DbVersion, PgU32},
db_error, get_crate_prefix, internal_error, Errors, State, INDEX_LOCK,
};
#[derive(Serialize, Deserialize)]
pub struct CrateVersion {
pub name: String,
pub vers: Version,
pub deps: Vec<Dependency>,
pub cksum: String,
pub features: HashMap<String, Vec<String>>,
pub yanked: bool,
pub links: Option<String>,
/// Should always be `1` for maximum compatability
pub v: u32,
// `features2` exists, but for out purposes, it can be ignored. See https://doc.rust-lang.org/cargo/reference/registries.html
}
#[derive(Serialize, Deserialize)]
pub struct Dependency {
#[serde(flatten)]
pub base: BaseDependency,
pub package: Option<String>,
}
#[derive(Serialize, Deserialize)]
pub struct BaseDependency {
pub name: String,
pub version_req: VersionReq,
pub features: Vec<String>,
pub optional: bool,
pub default_features: bool,
pub target: Option<String>,
pub kind: DependencyKind,
pub registry: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Copy, Type)]
#[serde(rename_all = "snake_case")]
#[sqlx(rename_all = "snake_case")]
#[sqlx(type_name = "dependency_kind")]
pub enum DependencyKind {
Dev,
Build,
Normal,
}
pub async fn update_crate_from_db(
crate_id: PgU32,
state: &State,
message: &str,
) -> Result<(), Errors> {
let lock = INDEX_LOCK.lock().await;
let mut db = state.db.acquire().await.map_err(db_error)?;
let (mut crate_name,): (String,) = query_as("SELECT name FROM crates WHERE id = $1")
.bind(crate_id)
.fetch_one(&mut db)
.await
.map_err(db_error)?;
// we use `fetch_all` here since we cant use `fetch` because of conflicting mutable borrows
let versions = query_as::<_, DbVersion>("SELECT * FROM versions WHERE crate_id = $1")
.bind(crate_id)
.fetch_all(&mut db)
.await
.map_err(db_error)?;
let mut versions2 = Vec::new();
for version in versions {
let mut deps = query_as::<_, DbDep>("SELECT * FROM deps WHERE version_id = $1")
.bind(version.id)
.fetch(&mut db);
let mut deps2 = Vec::new();
while let Some(dep) = deps.next().await.transpose().map_err(db_error)? {
deps2.push(Dependency {
base: BaseDependency {
name: dep.name,
version_req: dep.version_req.parse().unwrap(),
features: dep.features,
optional: dep.optional,
default_features: dep.default_features,
target: dep.target,
kind: dep.kind,
registry: dep.registry,
},
package: dep.package,
});
}
versions2.push(CrateVersion {
name: crate_name.clone(),
vers: version.vers.parse().unwrap(),
deps: deps2,
cksum: version.cksum,
features: version
.features
.into_iter()
.map(|v| (v.feature, v.enables))
.collect(),
yanked: version.yanked,
links: version.links,
v: 1,
});
}
crate_name.make_ascii_lowercase();
let mut path = state.index_dir.clone();
path.push(&get_crate_prefix(&crate_name).unwrap());
fs::create_dir_all(&path).await.map_err(internal_error)?;
path.push(&crate_name);
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(&path)
.await
.map_err(internal_error)?;
for version in versions2 {
let mut buf = serde_json::to_vec(&version).map_err(internal_error)?;
buf.push(b'\n');
file.write_all(&buf).await.map_err(internal_error)?;
}
Command::new("git")
.arg("add")
.arg(&path)
.current_dir(&state.index_dir)
.stdin(Stdio::null())
.stderr(Stdio::null())
.stdout(Stdio::null())
.status()
.await
.map_err(internal_error)?;
Command::new("git")
.arg("commit")
.arg("-m")
.arg(message)
.current_dir(&state.index_dir)
.stdin(Stdio::null())
.stderr(Stdio::null())
.stdout(Stdio::null())
.status()
.await
.map_err(internal_error)?;
drop(lock);
Ok(())
}