feat: add support for abstract packages
Signed-off-by: sisungo <[email protected]>
This commit is contained in:
@@ -76,7 +76,9 @@ pub fn main(cli: Cli) -> anyhow::Result<()> {
|
||||
},
|
||||
)?;
|
||||
package_files.push(path);
|
||||
progress_bar.set_position(init_pos + package.repo_pkg.download_size);
|
||||
}
|
||||
drop(progress_bar);
|
||||
|
||||
let progress_bar = ProgressBar::new(package_files.len() as _);
|
||||
for package in package_files.iter() {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#[macro_export]
|
||||
macro_rules! impl_serde_str {
|
||||
($t:ty) => {
|
||||
impl serde::Serialize for $t {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for $t {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer)?
|
||||
.parse()
|
||||
.map_err(|err| serde::de::Error::custom(err))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
pub mod checksum;
|
||||
pub mod fs;
|
||||
pub mod macros;
|
||||
|
||||
pub use fs::*;
|
||||
|
||||
|
||||
@@ -128,6 +128,13 @@ impl super::Packie {
|
||||
.insert_installation(&installation)
|
||||
.map_err(InstallError::Database)?;
|
||||
|
||||
// Record abstract package provision in the database
|
||||
for i in installation.pkg_manifest.provides.iter() {
|
||||
self.local_db
|
||||
.insert_abspkg(&i, &installation.pkg_manifest.pkg_ident())
|
||||
.map_err(InstallError::Database)?;
|
||||
}
|
||||
|
||||
// Enable default links
|
||||
for link in pkg_manifest.links.iter() {
|
||||
if !link.default {
|
||||
|
||||
+84
-18
@@ -1,4 +1,7 @@
|
||||
use crate::package::{PkgIdent, PkgManifest, PkgSpec};
|
||||
use crate::{
|
||||
package::{AbsPkgIdent, PkgIdent, PkgManifest, PkgSpec},
|
||||
version::VersionFilter,
|
||||
};
|
||||
use rusqlite::{OpenFlags, Row, params};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
@@ -7,12 +10,25 @@ impl super::Packie {
|
||||
&mut self,
|
||||
pkgspec: &PkgSpec,
|
||||
) -> rusqlite::Result<Vec<Installation>> {
|
||||
Ok(self
|
||||
let mut real = self
|
||||
.local_db
|
||||
.select_installation_by_pkgname(&pkgspec.name)?
|
||||
.select_installation_by_name(&pkgspec.name)?
|
||||
.into_iter()
|
||||
.filter(|x| pkgspec.matches(&x.pkg_manifest.pkg_ident()))
|
||||
.collect())
|
||||
.collect::<Vec<_>>();
|
||||
if !matches!(pkgspec.version, VersionFilter::Any) {
|
||||
return Ok(real);
|
||||
}
|
||||
let abspkg = AbsPkgIdent {
|
||||
name: pkgspec.name.clone(),
|
||||
arch: pkgspec.arch.clone(),
|
||||
};
|
||||
let abs = self.local_db.select_abspkg_by_ident(&abspkg)?;
|
||||
for (_, provider) in abs {
|
||||
real.push(self.local_db.select_installation_by_ident(&provider)?);
|
||||
}
|
||||
real.dedup_by_key(|x| x.pkg_manifest.pkg_ident());
|
||||
Ok(real)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,17 +61,22 @@ impl LocalDb {
|
||||
pub const FILENAME: &str = "local.db";
|
||||
|
||||
pub const SCHEMA: &str = r#"
|
||||
CREATE TABLE IF NOT EXISTS "installation"(
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"pkg_name" TEXT NOT NULL,
|
||||
"pkg_version" TEXT NOT NULL,
|
||||
"pkg_arch" TEXT NOT NULL,
|
||||
"pkg_manifest" TEXT NOT NULL,
|
||||
"install_pkgspec" TEXT NOT NULL,
|
||||
"install_date" INTEGER NOT NULL,
|
||||
"update_date" INTEGER NOT NULL,
|
||||
"installed_size" INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "installation"(
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"pkg_name" TEXT NOT NULL,
|
||||
"pkg_version" TEXT NOT NULL,
|
||||
"pkg_arch" TEXT NOT NULL,
|
||||
"pkg_manifest" TEXT NOT NULL,
|
||||
"install_pkgspec" TEXT NOT NULL,
|
||||
"install_date" INTEGER NOT NULL,
|
||||
"update_date" INTEGER NOT NULL,
|
||||
"installed_size" INTEGER NOT NULL
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "abspkg"(
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"abspkg_ident" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL
|
||||
);
|
||||
"#;
|
||||
|
||||
pub fn open_ro<P: AsRef<Path>>(path: P) -> rusqlite::Result<Self> {
|
||||
@@ -94,7 +115,18 @@ impl LocalDb {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_installation_by_pkgname(
|
||||
pub fn insert_abspkg(
|
||||
&mut self,
|
||||
abspkg: &AbsPkgIdent,
|
||||
provider: &PkgIdent,
|
||||
) -> rusqlite::Result<()> {
|
||||
self.0
|
||||
.prepare_cached(r#"INSERT INTO "abspkg"("abspkg_ident", "provider") VALUES(?1, ?2)"#)?
|
||||
.execute(params![abspkg.to_string(), provider.to_string(),])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn select_installation_by_name(
|
||||
&mut self,
|
||||
pkg_name: &str,
|
||||
) -> rusqlite::Result<Vec<Installation>> {
|
||||
@@ -109,7 +141,7 @@ impl LocalDb {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn select_installation_unique(
|
||||
pub fn select_installation_by_ident(
|
||||
&mut self,
|
||||
pkg_ident: &PkgIdent,
|
||||
) -> rusqlite::Result<Installation> {
|
||||
@@ -118,13 +150,35 @@ impl LocalDb {
|
||||
"#)?.query_one(params![pkg_ident.name, pkg_ident.version.to_string(), pkg_ident.arch], map_installation)
|
||||
}
|
||||
|
||||
pub fn select_abspkg_by_ident(
|
||||
&mut self,
|
||||
ident: &AbsPkgIdent,
|
||||
) -> rusqlite::Result<Vec<(AbsPkgIdent, PkgIdent)>> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare_cached(r#"SELECT * FROM "abspkg" WHERE "abspkg_ident" = ?1"#)?;
|
||||
let rows = stmt.query_map(params![ident.to_string()], map_abspkg)?;
|
||||
let mut abspkg = Vec::new();
|
||||
for i in rows {
|
||||
abspkg.push(i?);
|
||||
}
|
||||
Ok(abspkg)
|
||||
}
|
||||
|
||||
pub fn remove_installation(&mut self, pkg_ident: &PkgIdent) -> rusqlite::Result<bool> {
|
||||
self
|
||||
.0
|
||||
.prepare_cached(r#"DELETE FROM "installation" WHERE pkg_name = ?1 AND pkg_version = ?2 AND pkg_arch = ?3"#)?
|
||||
.prepare_cached(r#"DELETE FROM "installation" WHERE "pkg_name" = ?1 AND "pkg_version" = ?2 AND "pkg_arch" = ?3"#)?
|
||||
.execute(params![pkg_ident.name, pkg_ident.version.to_string(), pkg_ident.arch])
|
||||
.map(|x| x > 0)
|
||||
}
|
||||
|
||||
pub fn remove_abspkg_by_provider(&mut self, provider: &PkgIdent) -> rusqlite::Result<bool> {
|
||||
self.0
|
||||
.prepare_cached(r#"DELETE FROM "abspkg" WHERE "provider" = ?1"#)?
|
||||
.execute(params![provider.to_string()])
|
||||
.map(|x| x > 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -150,6 +204,18 @@ fn map_installation(row: &Row) -> rusqlite::Result<Installation> {
|
||||
})
|
||||
}
|
||||
|
||||
fn map_abspkg(row: &Row) -> rusqlite::Result<(AbsPkgIdent, PkgIdent)> {
|
||||
let provider = row
|
||||
.get::<_, String>("provider")?
|
||||
.parse()
|
||||
.map_err(from_sql_error)?;
|
||||
let abspkg_name = row
|
||||
.get::<_, String>("abspkg_ident")?
|
||||
.parse()
|
||||
.map_err(from_sql_error)?;
|
||||
Ok((abspkg_name, provider))
|
||||
}
|
||||
|
||||
fn from_sql_error<E: std::error::Error + Send + Sync + 'static>(error: E) -> rusqlite::Error {
|
||||
rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(error))
|
||||
}
|
||||
|
||||
+62
-21
@@ -83,9 +83,9 @@ pub struct PkgManifest {
|
||||
#[serde(default)]
|
||||
pub dependencies: Vec<PkgSpec>,
|
||||
|
||||
/// Package provisions.
|
||||
/// Provided abstract packages.
|
||||
#[serde(default)]
|
||||
pub provides: Vec<String>,
|
||||
pub provides: Vec<AbsPkgIdent>,
|
||||
|
||||
/// Links.
|
||||
#[serde(default)]
|
||||
@@ -205,27 +205,10 @@ impl Display for PkgSpec {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Serialize for PkgSpec {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
self.to_string().serialize(serializer)
|
||||
}
|
||||
}
|
||||
impl<'de> Deserialize<'de> for PkgSpec {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer)?
|
||||
.parse::<Self>()
|
||||
.map_err(|e| serde::de::Error::custom(e))
|
||||
}
|
||||
}
|
||||
crate::impl_serde_str!(PkgSpec);
|
||||
|
||||
/// A package identifier that specifies a unique package.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PkgIdent {
|
||||
pub name: String,
|
||||
pub version: Version,
|
||||
@@ -236,3 +219,61 @@ impl Display for PkgIdent {
|
||||
write!(f, "{}={}@{}", self.name, self.version, self.arch)
|
||||
}
|
||||
}
|
||||
impl FromStr for PkgIdent {
|
||||
type Err = crate::version::VersionError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let mut name = String::new();
|
||||
let mut version = String::new();
|
||||
let mut arch = String::new();
|
||||
let mut handling = "name";
|
||||
for c in s.chars() {
|
||||
if c == '=' {
|
||||
handling = "version";
|
||||
} else if c == '@' {
|
||||
handling = "arch";
|
||||
} else if handling == "name" {
|
||||
name.push(c);
|
||||
} else if handling == "version" {
|
||||
version.push(c);
|
||||
} else if handling == "arch" {
|
||||
arch.push(c);
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
name: name.trim().into(),
|
||||
version: version.trim().parse()?,
|
||||
arch: arch.trim().into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
crate::impl_serde_str!(PkgIdent);
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct AbsPkgIdent {
|
||||
pub name: String,
|
||||
pub arch: Option<String>,
|
||||
}
|
||||
impl Display for AbsPkgIdent {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.arch {
|
||||
Some(arch) => write!(f, "{}@{}", self.name, arch),
|
||||
None => write!(f, "{}", self.name),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for AbsPkgIdent {
|
||||
type Err = std::convert::Infallible;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (name, arch) = s
|
||||
.split_once('@')
|
||||
.map(|(a, b)| (a, Some(b)))
|
||||
.unwrap_or((s, None));
|
||||
Ok(Self {
|
||||
name: name.into(),
|
||||
arch: arch.map(Into::into),
|
||||
})
|
||||
}
|
||||
}
|
||||
crate::impl_serde_str!(AbsPkgIdent);
|
||||
|
||||
@@ -32,6 +32,9 @@ impl super::Packie {
|
||||
self.local_db
|
||||
.remove_installation(&found.pkg_manifest.pkg_ident())
|
||||
.map_err(RemoveError::Database)?;
|
||||
self.local_db
|
||||
.remove_abspkg_by_provider(&found.pkg_manifest.pkg_ident())
|
||||
.map_err(RemoveError::Database)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
+72
-26
@@ -10,7 +10,7 @@ pub use serve::RepoServeDir;
|
||||
|
||||
use crate::{
|
||||
Packie,
|
||||
package::{PkgIdent, PkgManifest, PkgSpec},
|
||||
package::{AbsPkgIdent, PkgIdent, PkgManifest, PkgSpec},
|
||||
};
|
||||
use download::Download;
|
||||
use rusqlite::{OpenFlags, Row, params};
|
||||
@@ -43,7 +43,7 @@ impl super::Packie {
|
||||
let repo_db = repo_cache_dir.join(RepoDb::FILENAME);
|
||||
let mut repo_db = RepoDb::open_ro(repo_db).map_err(RepoError::Database)?;
|
||||
let repo_package = repo_db
|
||||
.select_package_by_pkgident(pkg_ident)
|
||||
.select_package_by_ident(pkg_ident)
|
||||
.map_err(|_| RepoError::NoSuchPackage)?;
|
||||
|
||||
let standard_filename = format!("{pkg_ident}.pkg");
|
||||
@@ -96,17 +96,22 @@ impl RepoDb {
|
||||
const FILENAME: &str = "repo.db";
|
||||
|
||||
const SCHEMA: &str = r#"
|
||||
CREATE TABLE IF NOT EXISTS "package"(
|
||||
"pkg_name" TEXT NOT NULL,
|
||||
"pkg_version" TEXT NOT NULL,
|
||||
"pkg_arch" TEXT NOT NULL,
|
||||
"pkg_manifest" TEXT NOT NULL,
|
||||
"download_size" INTEGER NOT NULL,
|
||||
"installed_size" INTEGER NOT NULL,
|
||||
"checksum" TEXT NOT NULL,
|
||||
PRIMARY KEY ("pkg_name", "pkg_version", "pkg_arch")
|
||||
);
|
||||
"#;
|
||||
CREATE TABLE IF NOT EXISTS "package"(
|
||||
"pkg_name" TEXT NOT NULL,
|
||||
"pkg_version" TEXT NOT NULL,
|
||||
"pkg_arch" TEXT NOT NULL,
|
||||
"pkg_manifest" TEXT NOT NULL,
|
||||
"download_size" INTEGER NOT NULL,
|
||||
"installed_size" INTEGER NOT NULL,
|
||||
"checksum" TEXT NOT NULL,
|
||||
PRIMARY KEY ("pkg_name", "pkg_version", "pkg_arch")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "abspkg"(
|
||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
"abspkg_ident" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL
|
||||
);
|
||||
"#;
|
||||
|
||||
pub fn open_rw<P: AsRef<Path>>(path: P) -> rusqlite::Result<Self> {
|
||||
let conn = rusqlite::Connection::open(path)?;
|
||||
@@ -118,7 +123,7 @@ impl RepoDb {
|
||||
rusqlite::Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_ONLY).map(Self)
|
||||
}
|
||||
|
||||
pub fn select_package_by_pkgident(
|
||||
pub fn select_package_by_ident(
|
||||
&mut self,
|
||||
pkg_ident: &PkgIdent,
|
||||
) -> rusqlite::Result<RepoPackage> {
|
||||
@@ -127,10 +132,7 @@ impl RepoDb {
|
||||
)?.query_one(params![pkg_ident.name, pkg_ident.version.to_string(), pkg_ident.arch], map_repo_package)
|
||||
}
|
||||
|
||||
pub fn select_package_by_pkgname(
|
||||
&mut self,
|
||||
pkgname: &str,
|
||||
) -> rusqlite::Result<Vec<RepoPackage>> {
|
||||
pub fn select_package_by_name(&mut self, pkgname: &str) -> rusqlite::Result<Vec<RepoPackage>> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare_cached(r#"SELECT * FROM "package" WHERE "pkg_name" = ?1"#)?;
|
||||
@@ -142,6 +144,21 @@ impl RepoDb {
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn select_abspkg_by_ident(
|
||||
&mut self,
|
||||
ident: &AbsPkgIdent,
|
||||
) -> rusqlite::Result<Vec<(AbsPkgIdent, PkgIdent)>> {
|
||||
let mut stmt = self
|
||||
.0
|
||||
.prepare_cached(r#"SELECT * FROM "abspkg" WHERE "abspkg_ident" = ?1"#)?;
|
||||
let rows = stmt.query_map(params![ident.to_string()], map_abspkg)?;
|
||||
let mut ret = Vec::with_capacity(16);
|
||||
for row in rows {
|
||||
ret.push(row?);
|
||||
}
|
||||
Ok(ret)
|
||||
}
|
||||
|
||||
pub fn for_each_package<E: From<rusqlite::Error>>(
|
||||
&mut self,
|
||||
mut f: impl FnMut(RepoPackage) -> Result<(), E>,
|
||||
@@ -178,13 +195,18 @@ impl RepoDb {
|
||||
])
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
pub fn insert_abspkg(&mut self, abs: &AbsPkgIdent, real: &PkgIdent) -> rusqlite::Result<()> {
|
||||
self.0
|
||||
.prepare_cached(r#"INSERT INTO "abspkg"("abspkg_ident", "provider") VALUES(?1, ?2)"#)?
|
||||
.execute(params![abs.to_string(), real.to_string()])?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn map_repo_package(row: &Row) -> rusqlite::Result<RepoPackage> {
|
||||
let pkg_manifest: String = row.get("pkg_manifest")?;
|
||||
let pkg_manifest = serde_json::from_str(&pkg_manifest).map_err(|error| {
|
||||
rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(error))
|
||||
})?;
|
||||
let pkg_manifest = serde_json::from_str(&pkg_manifest).map_err(from_sql_error)?;
|
||||
Ok(RepoPackage {
|
||||
pkg_manifest,
|
||||
download_size: row.get::<_, i64>("download_size")? as u64,
|
||||
@@ -193,6 +215,22 @@ fn map_repo_package(row: &Row) -> rusqlite::Result<RepoPackage> {
|
||||
})
|
||||
}
|
||||
|
||||
fn map_abspkg(row: &Row) -> rusqlite::Result<(AbsPkgIdent, PkgIdent)> {
|
||||
let provider = row
|
||||
.get::<_, String>("provider")?
|
||||
.parse()
|
||||
.map_err(from_sql_error)?;
|
||||
let abspkg_name = row
|
||||
.get::<_, String>("abspkg_ident")?
|
||||
.parse()
|
||||
.map_err(from_sql_error)?;
|
||||
Ok((abspkg_name, provider))
|
||||
}
|
||||
|
||||
fn from_sql_error<E: std::error::Error + Send + Sync + 'static>(error: E) -> rusqlite::Error {
|
||||
rusqlite::Error::FromSqlConversionFailure(0, rusqlite::types::Type::Text, Box::new(error))
|
||||
}
|
||||
|
||||
/// A record representing to a package in a repository.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RepoPackage {
|
||||
@@ -333,17 +371,25 @@ impl QuerySession {
|
||||
}
|
||||
|
||||
pub fn search_package(&mut self, pkgspec: &PkgSpec) -> Vec<(String, RepoPackage)> {
|
||||
let mut all = Vec::new();
|
||||
let mut all: Vec<(String, RepoPackage)> = Vec::new();
|
||||
for (name, db) in self.repos.iter_mut() {
|
||||
for pkg in db
|
||||
.select_package_by_pkgname(&pkgspec.name)
|
||||
.unwrap_or_default()
|
||||
{
|
||||
for pkg in db.select_package_by_name(&pkgspec.name).unwrap_or_default() {
|
||||
if pkgspec.matches(&pkg.pkg_manifest.pkg_ident()) {
|
||||
all.push((name.into(), pkg));
|
||||
}
|
||||
}
|
||||
let abspkg_ident = AbsPkgIdent {
|
||||
name: pkgspec.name.clone(),
|
||||
arch: pkgspec.arch.clone(),
|
||||
};
|
||||
for (_, provider) in db.select_abspkg_by_ident(&abspkg_ident).unwrap_or_default() {
|
||||
let Ok(real) = db.select_package_by_ident(&provider) else {
|
||||
continue;
|
||||
};
|
||||
all.push((name.into(), real));
|
||||
}
|
||||
}
|
||||
all.dedup_by_key(|(repo, pkg)| (repo.to_string(), pkg.pkg_manifest.pkg_ident()));
|
||||
all
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,10 @@ impl RepoServeDir {
|
||||
std::fs::copy(path, self.path.join(standard_filename))?;
|
||||
|
||||
self.db.insert_package(&repo_package)?;
|
||||
for abs in repo_package.pkg_manifest.provides.iter() {
|
||||
self.db
|
||||
.insert_abspkg(abs, &repo_package.pkg_manifest.pkg_ident())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
+3
-26
@@ -2,30 +2,7 @@
|
||||
|
||||
use std::{cmp::Ordering, fmt::Display, str::FromStr};
|
||||
|
||||
macro_rules! impl_serde_str {
|
||||
($t:ty) => {
|
||||
impl serde::Serialize for $t {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serializer.serialize_str(&self.to_string())
|
||||
}
|
||||
}
|
||||
impl<'de> serde::Deserialize<'de> for $t {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
String::deserialize(deserializer)?
|
||||
.parse()
|
||||
.map_err(|err| serde::de::Error::custom(err))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Version {
|
||||
/// Version namespace. By default, the value is `0`. The value needs to be changed if the versioning
|
||||
/// rule has an incompatible change.
|
||||
@@ -121,7 +98,7 @@ impl Display for Version {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl_serde_str!(Version);
|
||||
crate::impl_serde_str!(Version);
|
||||
impl Version {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
@@ -229,7 +206,7 @@ impl Display for VersionFilter {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl_serde_str!(VersionFilter);
|
||||
crate::impl_serde_str!(VersionFilter);
|
||||
|
||||
/// An error caused by dealing with version or version filter strings.
|
||||
#[derive(Debug, Clone, thiserror::Error)]
|
||||
|
||||
Reference in New Issue
Block a user