mirror of
https://github.com/pesde-pkg/pesde.git
synced 2025-04-06 03:40:59 +01:00
268 lines
6.8 KiB
Rust
268 lines
6.8 KiB
Rust
use crate::AppState;
|
|
use chrono::{DateTime, Utc};
|
|
use pesde::{
|
|
manifest::{
|
|
target::{Target, TargetKind},
|
|
DependencyType,
|
|
},
|
|
names::PackageName,
|
|
source::{
|
|
git_index::{read_file, root_tree, GitBasedSource},
|
|
ids::VersionId,
|
|
pesde::{IndexFile, IndexFileEntry, PesdePackageSource, ScopeInfo, SCOPE_INFO_FILE},
|
|
specifiers::DependencySpecifiers,
|
|
},
|
|
};
|
|
use semver::Version;
|
|
use serde::Serialize;
|
|
use std::collections::{BTreeMap, BTreeSet};
|
|
use tokio::task::spawn_blocking;
|
|
|
|
#[derive(Debug, Serialize, Eq, PartialEq)]
|
|
struct TargetInfoInner {
|
|
lib: bool,
|
|
bin: bool,
|
|
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
|
|
scripts: BTreeSet<String>,
|
|
}
|
|
|
|
impl TargetInfoInner {
|
|
fn new(target: &Target) -> Self {
|
|
TargetInfoInner {
|
|
lib: target.lib_path().is_some(),
|
|
bin: target.bin_path().is_some(),
|
|
scripts: target
|
|
.scripts()
|
|
.map(|scripts| scripts.keys().cloned().collect())
|
|
.unwrap_or_default(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Eq, PartialEq)]
|
|
pub struct TargetInfo {
|
|
kind: TargetKind,
|
|
#[serde(skip_serializing_if = "std::ops::Not::not")]
|
|
yanked: bool,
|
|
#[serde(flatten)]
|
|
inner: TargetInfoInner,
|
|
}
|
|
|
|
impl TargetInfo {
|
|
fn new(target: &Target, yanked: bool) -> Self {
|
|
TargetInfo {
|
|
kind: target.kind(),
|
|
yanked,
|
|
inner: TargetInfoInner::new(target),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Ord for TargetInfo {
|
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
|
self.kind.cmp(&other.kind)
|
|
}
|
|
}
|
|
|
|
impl PartialOrd for TargetInfo {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.cmp(other))
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
|
|
#[serde(untagged)]
|
|
pub enum RegistryDocEntryKind {
|
|
Page {
|
|
name: String,
|
|
},
|
|
Category {
|
|
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
|
|
items: BTreeSet<RegistryDocEntry>,
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
collapsed: bool,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Ord, PartialOrd, Eq, PartialEq)]
|
|
pub struct RegistryDocEntry {
|
|
label: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
position: Option<usize>,
|
|
#[serde(flatten)]
|
|
kind: RegistryDocEntryKind,
|
|
}
|
|
|
|
impl From<pesde::source::pesde::DocEntry> for RegistryDocEntry {
|
|
fn from(entry: pesde::source::pesde::DocEntry) -> Self {
|
|
Self {
|
|
label: entry.label,
|
|
position: entry.position,
|
|
kind: match entry.kind {
|
|
pesde::source::pesde::DocEntryKind::Page { name, .. } => {
|
|
RegistryDocEntryKind::Page { name }
|
|
}
|
|
pesde::source::pesde::DocEntryKind::Category { items, collapsed } => {
|
|
RegistryDocEntryKind::Category {
|
|
items: items.into_iter().map(Into::into).collect(),
|
|
collapsed,
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct PackageResponseInner {
|
|
published_at: DateTime<Utc>,
|
|
#[serde(skip_serializing_if = "String::is_empty")]
|
|
license: String,
|
|
#[serde(skip_serializing_if = "Vec::is_empty")]
|
|
authors: Vec<String>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
repository: Option<String>,
|
|
#[serde(skip_serializing_if = "BTreeSet::is_empty")]
|
|
docs: BTreeSet<RegistryDocEntry>,
|
|
#[serde(skip_serializing_if = "BTreeMap::is_empty")]
|
|
dependencies: BTreeMap<String, (DependencySpecifiers, DependencyType)>,
|
|
}
|
|
|
|
impl PackageResponseInner {
|
|
pub fn new(entry: &IndexFileEntry) -> Self {
|
|
PackageResponseInner {
|
|
published_at: entry.published_at,
|
|
license: entry.license.clone().unwrap_or_default(),
|
|
authors: entry.authors.clone(),
|
|
repository: entry.repository.clone().map(|url| url.to_string()),
|
|
docs: entry.docs.iter().cloned().map(Into::into).collect(),
|
|
dependencies: entry.dependencies.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct PackageResponse {
|
|
name: String,
|
|
version: String,
|
|
targets: BTreeSet<TargetInfo>,
|
|
#[serde(skip_serializing_if = "String::is_empty")]
|
|
description: String,
|
|
#[serde(skip_serializing_if = "String::is_empty")]
|
|
deprecated: String,
|
|
#[serde(flatten)]
|
|
inner: PackageResponseInner,
|
|
}
|
|
|
|
impl PackageResponse {
|
|
pub fn new(name: &PackageName, version_id: &VersionId, file: &IndexFile) -> Self {
|
|
let entry = file.entries.get(version_id).unwrap();
|
|
|
|
PackageResponse {
|
|
name: name.to_string(),
|
|
version: version_id.version().to_string(),
|
|
targets: file
|
|
.entries
|
|
.iter()
|
|
.filter(|(ver, _)| ver.version() == version_id.version())
|
|
.map(|(_, entry)| TargetInfo::new(&entry.target, entry.yanked))
|
|
.collect(),
|
|
description: entry.description.clone().unwrap_or_default(),
|
|
deprecated: file.meta.deprecated.clone(),
|
|
inner: PackageResponseInner::new(entry),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
struct PackageVersionsResponseVersionInner {
|
|
target: TargetInfoInner,
|
|
#[serde(skip_serializing_if = "std::ops::Not::not")]
|
|
yanked: bool,
|
|
#[serde(flatten)]
|
|
inner: PackageResponseInner,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Default)]
|
|
struct PackageVersionsResponseVersion {
|
|
#[serde(skip_serializing_if = "String::is_empty")]
|
|
description: String,
|
|
targets: BTreeMap<TargetKind, PackageVersionsResponseVersionInner>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct PackageVersionsResponse {
|
|
name: String,
|
|
#[serde(skip_serializing_if = "String::is_empty")]
|
|
deprecated: String,
|
|
versions: BTreeMap<Version, PackageVersionsResponseVersion>,
|
|
}
|
|
|
|
impl PackageVersionsResponse {
|
|
pub fn new(name: &PackageName, file: &IndexFile) -> Self {
|
|
let mut versions = BTreeMap::<Version, PackageVersionsResponseVersion>::new();
|
|
|
|
for (v_id, entry) in file.entries.iter() {
|
|
let versions_resp = versions.entry(v_id.version().clone()).or_default();
|
|
|
|
versions_resp.description = entry.description.clone().unwrap_or_default();
|
|
versions_resp.targets.insert(
|
|
entry.target.kind(),
|
|
PackageVersionsResponseVersionInner {
|
|
target: TargetInfoInner::new(&entry.target),
|
|
yanked: entry.yanked,
|
|
inner: PackageResponseInner::new(entry),
|
|
},
|
|
);
|
|
}
|
|
|
|
PackageVersionsResponse {
|
|
name: name.to_string(),
|
|
deprecated: file.meta.deprecated.clone(),
|
|
versions,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn read_package(
|
|
app_state: &AppState,
|
|
package: &PackageName,
|
|
source: &PesdePackageSource,
|
|
) -> Result<Option<IndexFile>, crate::error::RegistryError> {
|
|
let path = source.path(&app_state.project);
|
|
let package = package.clone();
|
|
spawn_blocking(move || {
|
|
let (scope, name) = package.as_str();
|
|
let repo = gix::open(path)?;
|
|
let tree = root_tree(&repo)?;
|
|
|
|
let Some(versions) = read_file(&tree, [scope, name])? else {
|
|
return Ok(None);
|
|
};
|
|
|
|
toml::de::from_str(&versions).map_err(Into::into)
|
|
})
|
|
.await
|
|
.unwrap()
|
|
}
|
|
|
|
pub async fn read_scope_info(
|
|
app_state: &AppState,
|
|
scope: &str,
|
|
source: &PesdePackageSource,
|
|
) -> Result<Option<ScopeInfo>, crate::error::RegistryError> {
|
|
let path = source.path(&app_state.project);
|
|
let scope = scope.to_string();
|
|
spawn_blocking(move || {
|
|
let repo = gix::open(path)?;
|
|
let tree = root_tree(&repo)?;
|
|
|
|
let Some(versions) = read_file(&tree, [&*scope, SCOPE_INFO_FILE])? else {
|
|
return Ok(None);
|
|
};
|
|
|
|
toml::de::from_str(&versions).map_err(Into::into)
|
|
})
|
|
.await
|
|
.unwrap()
|
|
}
|