169 lines
4.6 KiB
Rust
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(())
|
|
}
|