mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
chore(fmt): format with cargo fmt
This commit is contained in:
parent
f5b80a7d12
commit
a33a3246b6
17 changed files with 1012 additions and 644 deletions
|
|
@ -2,94 +2,110 @@ use diff::Diff;
|
||||||
use libips::actions::File;
|
use libips::actions::File;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use libips::payload::Payload;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Diff)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Diff)]
|
||||||
#[diff(attr(
|
#[diff(attr(
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
))]
|
))]
|
||||||
struct Manifest {
|
struct Manifest {
|
||||||
files: HashMap<String, File>
|
files: HashMap<String, File>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let base = Manifest{files: HashMap::from([
|
let base = Manifest {
|
||||||
("0dh5".to_string(), File{
|
files: HashMap::from([
|
||||||
payload: None,
|
(
|
||||||
path: "var/file".to_string(),
|
"0dh5".to_string(),
|
||||||
group: "bin".to_string(),
|
File {
|
||||||
owner: "root".to_string(),
|
payload: None,
|
||||||
mode: "0755".to_string(),
|
path: "var/file".to_string(),
|
||||||
preserve: false,
|
group: "bin".to_string(),
|
||||||
overlay: false,
|
owner: "root".to_string(),
|
||||||
original_name: "".to_string(),
|
mode: "0755".to_string(),
|
||||||
revert_tag: "".to_string(),
|
preserve: false,
|
||||||
sys_attr: "".to_string(),
|
overlay: false,
|
||||||
properties: vec![],
|
original_name: "".to_string(),
|
||||||
facets: Default::default(),
|
revert_tag: "".to_string(),
|
||||||
}),
|
sys_attr: "".to_string(),
|
||||||
("12ds3".to_string(), File{
|
properties: vec![],
|
||||||
payload: None,
|
facets: Default::default(),
|
||||||
path: "var/file1".to_string(),
|
},
|
||||||
group: "bin".to_string(),
|
),
|
||||||
owner: "root".to_string(),
|
(
|
||||||
mode: "0755".to_string(),
|
"12ds3".to_string(),
|
||||||
preserve: false,
|
File {
|
||||||
overlay: false,
|
payload: None,
|
||||||
original_name: "".to_string(),
|
path: "var/file1".to_string(),
|
||||||
revert_tag: "".to_string(),
|
group: "bin".to_string(),
|
||||||
sys_attr: "".to_string(),
|
owner: "root".to_string(),
|
||||||
properties: vec![],
|
mode: "0755".to_string(),
|
||||||
facets: Default::default(),
|
preserve: false,
|
||||||
}),
|
overlay: false,
|
||||||
("654".to_string(), File{
|
original_name: "".to_string(),
|
||||||
payload: None,
|
revert_tag: "".to_string(),
|
||||||
path: "var/file1".to_string(),
|
sys_attr: "".to_string(),
|
||||||
group: "bin".to_string(),
|
properties: vec![],
|
||||||
owner: "root".to_string(),
|
facets: Default::default(),
|
||||||
mode: "0755".to_string(),
|
},
|
||||||
preserve: false,
|
),
|
||||||
overlay: false,
|
(
|
||||||
original_name: "".to_string(),
|
"654".to_string(),
|
||||||
revert_tag: "".to_string(),
|
File {
|
||||||
sys_attr: "".to_string(),
|
payload: None,
|
||||||
properties: vec![],
|
path: "var/file1".to_string(),
|
||||||
facets: Default::default(),
|
group: "bin".to_string(),
|
||||||
})
|
owner: "root".to_string(),
|
||||||
])};
|
mode: "0755".to_string(),
|
||||||
|
preserve: false,
|
||||||
|
overlay: false,
|
||||||
|
original_name: "".to_string(),
|
||||||
|
revert_tag: "".to_string(),
|
||||||
|
sys_attr: "".to_string(),
|
||||||
|
properties: vec![],
|
||||||
|
facets: Default::default(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
|
||||||
let new_set = Manifest{files: HashMap::from([
|
let new_set = Manifest {
|
||||||
("0dh5".to_string(), File{
|
files: HashMap::from([
|
||||||
payload: None,
|
(
|
||||||
path: "var/file".to_string(),
|
"0dh5".to_string(),
|
||||||
group: "bin".to_string(),
|
File {
|
||||||
owner: "root".to_string(),
|
payload: None,
|
||||||
mode: "0755".to_string(),
|
path: "var/file".to_string(),
|
||||||
preserve: false,
|
group: "bin".to_string(),
|
||||||
overlay: false,
|
owner: "root".to_string(),
|
||||||
original_name: "".to_string(),
|
mode: "0755".to_string(),
|
||||||
revert_tag: "".to_string(),
|
preserve: false,
|
||||||
sys_attr: "".to_string(),
|
overlay: false,
|
||||||
properties: vec![],
|
original_name: "".to_string(),
|
||||||
facets: Default::default(),
|
revert_tag: "".to_string(),
|
||||||
}),
|
sys_attr: "".to_string(),
|
||||||
("654".to_string(), File{
|
properties: vec![],
|
||||||
payload: None,
|
facets: Default::default(),
|
||||||
path: "var/file1".to_string(),
|
},
|
||||||
group: "bin".to_string(),
|
),
|
||||||
owner: "root".to_string(),
|
(
|
||||||
mode: "0755".to_string(),
|
"654".to_string(),
|
||||||
preserve: false,
|
File {
|
||||||
overlay: false,
|
payload: None,
|
||||||
original_name: "".to_string(),
|
path: "var/file1".to_string(),
|
||||||
revert_tag: "".to_string(),
|
group: "bin".to_string(),
|
||||||
sys_attr: "".to_string(),
|
owner: "root".to_string(),
|
||||||
properties: vec![],
|
mode: "0755".to_string(),
|
||||||
facets: Default::default(),
|
preserve: false,
|
||||||
})
|
overlay: false,
|
||||||
])};
|
original_name: "".to_string(),
|
||||||
|
revert_tag: "".to_string(),
|
||||||
|
sys_attr: "".to_string(),
|
||||||
|
properties: vec![],
|
||||||
|
facets: Default::default(),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
};
|
||||||
let d = base.diff(&new_set);
|
let d = base.diff(&new_set);
|
||||||
println!("{:#?}", d);
|
println!("{:#?}", d);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,16 +8,16 @@
|
||||||
use crate::digest::Digest;
|
use crate::digest::Digest;
|
||||||
use crate::fmri::Fmri;
|
use crate::fmri::Fmri;
|
||||||
use crate::payload::{Payload, PayloadError};
|
use crate::payload::{Payload, PayloadError};
|
||||||
|
use diff::Diff;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use pest_derive::Parser;
|
use pest_derive::Parser;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::clone::Clone;
|
use std::clone::Clone;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::read_to_string;
|
use std::fs::read_to_string;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use diff::Diff;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
type Result<T> = StdResult<T, ActionError>;
|
type Result<T> = StdResult<T, ActionError>;
|
||||||
|
|
@ -289,23 +289,19 @@ impl From<Action> for Dependency {
|
||||||
}
|
}
|
||||||
for prop in props {
|
for prop in props {
|
||||||
match prop.key.as_str() {
|
match prop.key.as_str() {
|
||||||
"fmri" => {
|
"fmri" => match Fmri::parse(&prop.value) {
|
||||||
match Fmri::parse(&prop.value) {
|
Ok(fmri) => dep.fmri = Some(fmri),
|
||||||
Ok(fmri) => dep.fmri = Some(fmri),
|
Err(err) => {
|
||||||
Err(err) => {
|
eprintln!("Error parsing FMRI '{}': {}", prop.value, err);
|
||||||
eprintln!("Error parsing FMRI '{}': {}", prop.value, err);
|
dep.fmri = None;
|
||||||
dep.fmri = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"type" => dep.dependency_type = prop.value,
|
"type" => dep.dependency_type = prop.value,
|
||||||
"predicate" => {
|
"predicate" => match Fmri::parse(&prop.value) {
|
||||||
match Fmri::parse(&prop.value) {
|
Ok(fmri) => dep.predicate = Some(fmri),
|
||||||
Ok(fmri) => dep.predicate = Some(fmri),
|
Err(err) => {
|
||||||
Err(err) => {
|
eprintln!("Error parsing predicate FMRI '{}': {}", prop.value, err);
|
||||||
eprintln!("Error parsing predicate FMRI '{}': {}", prop.value, err);
|
dep.predicate = None;
|
||||||
dep.predicate = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root-image" => dep.root_image = prop.value,
|
"root-image" => dep.root_image = prop.value,
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,14 @@
|
||||||
// MPL was not distributed with this file, You can
|
// MPL was not distributed with this file, You can
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
use diff::Diff;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use sha2::Digest as Sha2Digest;
|
use sha2::Digest as Sha2Digest;
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use sha3::Digest as Sha3Digest;
|
use sha3::Digest as Sha3Digest;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::{convert::TryInto, result::Result as StdResult};
|
use std::{convert::TryInto, result::Result as StdResult};
|
||||||
use diff::Diff;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use strum::{Display as StrumDisplay, EnumString};
|
use strum::{Display as StrumDisplay, EnumString};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
|
@ -19,7 +19,9 @@ type Result<T> = StdResult<T, DigestError>;
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
static DEFAULT_ALGORITHM: DigestAlgorithm = DigestAlgorithm::SHA512;
|
static DEFAULT_ALGORITHM: DigestAlgorithm = DigestAlgorithm::SHA512;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, StrumDisplay, EnumString, Default, Deserialize, Serialize, Diff)]
|
#[derive(
|
||||||
|
Debug, PartialEq, Clone, StrumDisplay, EnumString, Default, Deserialize, Serialize, Diff,
|
||||||
|
)]
|
||||||
#[diff(attr(
|
#[diff(attr(
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
))]
|
))]
|
||||||
|
|
@ -41,7 +43,9 @@ pub enum DigestAlgorithm {
|
||||||
SHA3512, // Sha3 version of sha512t
|
SHA3512, // Sha3 version of sha512t
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, StrumDisplay, EnumString, Default, Deserialize, Serialize, Diff)]
|
#[derive(
|
||||||
|
Debug, PartialEq, Clone, StrumDisplay, EnumString, Default, Deserialize, Serialize, Diff,
|
||||||
|
)]
|
||||||
#[diff(attr(
|
#[diff(attr(
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
))]
|
))]
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,11 @@
|
||||||
//! assert_eq!(version.timestamp, Some("20200421T195136Z".to_string()));
|
//! assert_eq!(version.timestamp, Some("20200421T195136Z".to_string()));
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use diff::Diff;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use diff::Diff;
|
|
||||||
|
|
||||||
/// Errors that can occur when parsing an FMRI
|
/// Errors that can occur when parsing an FMRI
|
||||||
#[derive(Debug, Error, PartialEq)]
|
#[derive(Debug, Error, PartialEq)]
|
||||||
|
|
@ -146,7 +146,7 @@ impl Version {
|
||||||
timestamp: None,
|
timestamp: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper method to pad a version string to ensure it has at least MAJOR.MINOR.PATCH components
|
/// Helper method to pad a version string to ensure it has at least MAJOR.MINOR.PATCH components
|
||||||
///
|
///
|
||||||
/// This method takes a dot-separated version string and ensures it has at least three components
|
/// This method takes a dot-separated version string and ensures it has at least three components
|
||||||
|
|
@ -161,7 +161,7 @@ impl Version {
|
||||||
_ => format!("{}.{}.{}", parts[0], parts[1], parts[2]), // Use only the first three parts
|
_ => format!("{}.{}.{}", parts[0], parts[1], parts[2]), // Use only the first three parts
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the release component to a semver::Version
|
/// Convert the release component to a semver::Version
|
||||||
///
|
///
|
||||||
/// This method attempts to parse the release component as a semver::Version.
|
/// This method attempts to parse the release component as a semver::Version.
|
||||||
|
|
@ -172,7 +172,7 @@ impl Version {
|
||||||
let version_str = Self::pad_version_string(&self.release);
|
let version_str = Self::pad_version_string(&self.release);
|
||||||
version_str.parse()
|
version_str.parse()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the branch component to a semver::Version
|
/// Convert the branch component to a semver::Version
|
||||||
///
|
///
|
||||||
/// This method attempts to parse the branch component as a semver::Version.
|
/// This method attempts to parse the branch component as a semver::Version.
|
||||||
|
|
@ -186,7 +186,7 @@ impl Version {
|
||||||
version_str.parse()
|
version_str.parse()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the build component to a semver::Version
|
/// Convert the build component to a semver::Version
|
||||||
///
|
///
|
||||||
/// This method attempts to parse the build component as a semver::Version.
|
/// This method attempts to parse the build component as a semver::Version.
|
||||||
|
|
@ -200,7 +200,7 @@ impl Version {
|
||||||
version_str.parse()
|
version_str.parse()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Version with the given semver::Version as release
|
/// Create a new Version with the given semver::Version as release
|
||||||
///
|
///
|
||||||
/// This method creates a new Version with the given semver::Version as release.
|
/// This method creates a new Version with the given semver::Version as release.
|
||||||
|
|
@ -213,7 +213,7 @@ impl Version {
|
||||||
timestamp: None,
|
timestamp: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a Version from semver::Version components
|
/// Create a Version from semver::Version components
|
||||||
///
|
///
|
||||||
/// This method creates a Version from semver::Version components.
|
/// This method creates a Version from semver::Version components.
|
||||||
|
|
@ -231,7 +231,7 @@ impl Version {
|
||||||
timestamp,
|
timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Version with the given semver::Version as release and branch
|
/// Create a new Version with the given semver::Version as release and branch
|
||||||
///
|
///
|
||||||
/// This method creates a new Version with the given semver::Version as release and branch.
|
/// This method creates a new Version with the given semver::Version as release and branch.
|
||||||
|
|
@ -244,7 +244,7 @@ impl Version {
|
||||||
timestamp: None,
|
timestamp: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Version with the given semver::Version as release, branch, and build
|
/// Create a new Version with the given semver::Version as release, branch, and build
|
||||||
///
|
///
|
||||||
/// This method creates a new Version with the given semver::Version as release, branch, and build.
|
/// This method creates a new Version with the given semver::Version as release, branch, and build.
|
||||||
|
|
@ -261,7 +261,7 @@ impl Version {
|
||||||
timestamp: None,
|
timestamp: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new Version with the given semver::Version as release, branch, build, and timestamp
|
/// Create a new Version with the given semver::Version as release, branch, build, and timestamp
|
||||||
///
|
///
|
||||||
/// This method creates a new Version with the given semver::Version as release, branch, build, and timestamp.
|
/// This method creates a new Version with the given semver::Version as release, branch, build, and timestamp.
|
||||||
|
|
@ -279,29 +279,35 @@ impl Version {
|
||||||
timestamp: Some(timestamp.to_string()),
|
timestamp: Some(timestamp.to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all version components as semver::Version objects
|
/// Get all version components as semver::Version objects
|
||||||
///
|
///
|
||||||
/// This method returns all version components as semver::Version objects.
|
/// This method returns all version components as semver::Version objects.
|
||||||
/// If a component is not present or cannot be parsed, it will be None.
|
/// If a component is not present or cannot be parsed, it will be None.
|
||||||
pub fn to_semver(&self) -> (Result<semver::Version, semver::Error>, Option<Result<semver::Version, semver::Error>>, Option<Result<semver::Version, semver::Error>>) {
|
pub fn to_semver(
|
||||||
|
&self,
|
||||||
|
) -> (
|
||||||
|
Result<semver::Version, semver::Error>,
|
||||||
|
Option<Result<semver::Version, semver::Error>>,
|
||||||
|
Option<Result<semver::Version, semver::Error>>,
|
||||||
|
) {
|
||||||
let release = self.release_to_semver();
|
let release = self.release_to_semver();
|
||||||
let branch = self.branch_to_semver();
|
let branch = self.branch_to_semver();
|
||||||
let build = self.build_to_semver();
|
let build = self.build_to_semver();
|
||||||
|
|
||||||
(release, branch, build)
|
(release, branch, build)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if this version is compatible with semver
|
/// Check if this version is compatible with semver
|
||||||
///
|
///
|
||||||
/// This method checks if all components of this version can be parsed as semver::Version objects.
|
/// This method checks if all components of this version can be parsed as semver::Version objects.
|
||||||
pub fn is_semver_compatible(&self) -> bool {
|
pub fn is_semver_compatible(&self) -> bool {
|
||||||
let (release, branch, build) = self.to_semver();
|
let (release, branch, build) = self.to_semver();
|
||||||
|
|
||||||
let release_ok = release.is_ok();
|
let release_ok = release.is_ok();
|
||||||
let branch_ok = branch.map_or(true, |r| r.is_ok());
|
let branch_ok = branch.map_or(true, |r| r.is_ok());
|
||||||
let build_ok = build.map_or(true, |r| r.is_ok());
|
let build_ok = build.map_or(true, |r| r.is_ok());
|
||||||
|
|
||||||
release_ok && branch_ok && build_ok
|
release_ok && branch_ok && build_ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -364,7 +370,10 @@ impl Version {
|
||||||
if timestamp.is_empty() {
|
if timestamp.is_empty() {
|
||||||
return Err(FmriError::InvalidTimestampFormat);
|
return Err(FmriError::InvalidTimestampFormat);
|
||||||
}
|
}
|
||||||
if !timestamp.chars().all(|c| c.is_ascii_hexdigit() || c == 'T' || c == 'Z') {
|
if !timestamp
|
||||||
|
.chars()
|
||||||
|
.all(|c| c.is_ascii_hexdigit() || c == 'T' || c == 'Z')
|
||||||
|
{
|
||||||
return Err(FmriError::InvalidTimestampFormat);
|
return Err(FmriError::InvalidTimestampFormat);
|
||||||
}
|
}
|
||||||
version.timestamp = Some(timestamp.to_string());
|
version.timestamp = Some(timestamp.to_string());
|
||||||
|
|
@ -431,7 +440,7 @@ impl Version {
|
||||||
if parts.len() >= 3 {
|
if parts.len() >= 3 {
|
||||||
// Create a version string with exactly MAJOR.MINOR.PATCH
|
// Create a version string with exactly MAJOR.MINOR.PATCH
|
||||||
let version_str = format!("{}.{}.{}", parts[0], parts[1], parts[2]);
|
let version_str = format!("{}.{}.{}", parts[0], parts[1], parts[2]);
|
||||||
|
|
||||||
// Try to parse it as a semver version
|
// Try to parse it as a semver version
|
||||||
if let Err(_) = semver::Version::parse(&version_str) {
|
if let Err(_) = semver::Version::parse(&version_str) {
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -519,12 +528,12 @@ impl Fmri {
|
||||||
version,
|
version,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the stem of the FMRI (the package name without version)
|
/// Get the stem of the FMRI (the package name without version)
|
||||||
pub fn stem(&self) -> &str {
|
pub fn stem(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the version of the FMRI as a string
|
/// Get the version of the FMRI as a string
|
||||||
pub fn version(&self) -> String {
|
pub fn version(&self) -> String {
|
||||||
match &self.version {
|
match &self.version {
|
||||||
|
|
@ -562,35 +571,34 @@ impl Fmri {
|
||||||
// Check if there's a scheme with a publisher (pkg://publisher/name)
|
// Check if there's a scheme with a publisher (pkg://publisher/name)
|
||||||
if let Some(scheme_end) = name_part.find("://") {
|
if let Some(scheme_end) = name_part.find("://") {
|
||||||
fmri.scheme = name_part[0..scheme_end].to_string();
|
fmri.scheme = name_part[0..scheme_end].to_string();
|
||||||
|
|
||||||
// Extract the rest after the scheme
|
// Extract the rest after the scheme
|
||||||
let rest = &name_part[scheme_end + 3..];
|
let rest = &name_part[scheme_end + 3..];
|
||||||
|
|
||||||
// Check if there's a publisher
|
// Check if there's a publisher
|
||||||
if let Some(publisher_end) = rest.find('/') {
|
if let Some(publisher_end) = rest.find('/') {
|
||||||
// If there's a non-empty publisher, set it
|
// If there's a non-empty publisher, set it
|
||||||
if publisher_end > 0 {
|
if publisher_end > 0 {
|
||||||
fmri.publisher = Some(rest[0..publisher_end].to_string());
|
fmri.publisher = Some(rest[0..publisher_end].to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the name
|
// Set the name
|
||||||
fmri.name = rest[publisher_end + 1..].to_string();
|
fmri.name = rest[publisher_end + 1..].to_string();
|
||||||
} else {
|
} else {
|
||||||
// No publisher, just a name
|
// No publisher, just a name
|
||||||
fmri.name = rest.to_string();
|
fmri.name = rest.to_string();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Check if there's a scheme without a publisher (pkg:/name)
|
// Check if there's a scheme without a publisher (pkg:/name)
|
||||||
else if let Some(scheme_end) = name_part.find(":/") {
|
else if let Some(scheme_end) = name_part.find(":/") {
|
||||||
fmri.scheme = name_part[0..scheme_end].to_string();
|
fmri.scheme = name_part[0..scheme_end].to_string();
|
||||||
|
|
||||||
// Extract the rest after the scheme
|
// Extract the rest after the scheme
|
||||||
let rest = &name_part[scheme_end + 2..];
|
let rest = &name_part[scheme_end + 2..];
|
||||||
|
|
||||||
// Set the name
|
// Set the name
|
||||||
fmri.name = rest.to_string();
|
fmri.name = rest.to_string();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// No scheme, just a name
|
// No scheme, just a name
|
||||||
fmri.name = name_part.to_string();
|
fmri.name = name_part.to_string();
|
||||||
}
|
}
|
||||||
|
|
@ -635,7 +643,7 @@ impl FromStr for Fmri {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_semver_conversion() {
|
fn test_semver_conversion() {
|
||||||
// Test release_to_semver
|
// Test release_to_semver
|
||||||
|
|
@ -644,21 +652,21 @@ mod tests {
|
||||||
assert_eq!(semver.major, 5);
|
assert_eq!(semver.major, 5);
|
||||||
assert_eq!(semver.minor, 11);
|
assert_eq!(semver.minor, 11);
|
||||||
assert_eq!(semver.patch, 0);
|
assert_eq!(semver.patch, 0);
|
||||||
|
|
||||||
// Test with a full semver version
|
// Test with a full semver version
|
||||||
let version = Version::new("1.2.3");
|
let version = Version::new("1.2.3");
|
||||||
let semver = version.release_to_semver().unwrap();
|
let semver = version.release_to_semver().unwrap();
|
||||||
assert_eq!(semver.major, 1);
|
assert_eq!(semver.major, 1);
|
||||||
assert_eq!(semver.minor, 2);
|
assert_eq!(semver.minor, 2);
|
||||||
assert_eq!(semver.patch, 3);
|
assert_eq!(semver.patch, 3);
|
||||||
|
|
||||||
// Test branch_to_semver
|
// Test branch_to_semver
|
||||||
let version = Version::with_branch("5.11", "1");
|
let version = Version::with_branch("5.11", "1");
|
||||||
let semver = version.branch_to_semver().unwrap().unwrap();
|
let semver = version.branch_to_semver().unwrap().unwrap();
|
||||||
assert_eq!(semver.major, 1);
|
assert_eq!(semver.major, 1);
|
||||||
assert_eq!(semver.minor, 0);
|
assert_eq!(semver.minor, 0);
|
||||||
assert_eq!(semver.patch, 0);
|
assert_eq!(semver.patch, 0);
|
||||||
|
|
||||||
// Test with a full semver version
|
// Test with a full semver version
|
||||||
let mut version = Version::new("5.11");
|
let mut version = Version::new("5.11");
|
||||||
version.branch = Some("1.2.3".to_string());
|
version.branch = Some("1.2.3".to_string());
|
||||||
|
|
@ -666,7 +674,7 @@ mod tests {
|
||||||
assert_eq!(semver.major, 1);
|
assert_eq!(semver.major, 1);
|
||||||
assert_eq!(semver.minor, 2);
|
assert_eq!(semver.minor, 2);
|
||||||
assert_eq!(semver.patch, 3);
|
assert_eq!(semver.patch, 3);
|
||||||
|
|
||||||
// Test build_to_semver
|
// Test build_to_semver
|
||||||
let mut version = Version::new("5.11");
|
let mut version = Version::new("5.11");
|
||||||
version.build = Some("2020.0.1.0".to_string());
|
version.build = Some("2020.0.1.0".to_string());
|
||||||
|
|
@ -674,20 +682,20 @@ mod tests {
|
||||||
assert_eq!(semver.major, 2020);
|
assert_eq!(semver.major, 2020);
|
||||||
assert_eq!(semver.minor, 0);
|
assert_eq!(semver.minor, 0);
|
||||||
assert_eq!(semver.patch, 1);
|
assert_eq!(semver.patch, 1);
|
||||||
|
|
||||||
// Test from_semver
|
// Test from_semver
|
||||||
let release = semver::Version::new(5, 11, 0);
|
let release = semver::Version::new(5, 11, 0);
|
||||||
let branch = Some(semver::Version::new(1, 0, 0));
|
let branch = Some(semver::Version::new(1, 0, 0));
|
||||||
let build = Some(semver::Version::new(2020, 0, 1));
|
let build = Some(semver::Version::new(2020, 0, 1));
|
||||||
let timestamp = Some("20200421T195136Z".to_string());
|
let timestamp = Some("20200421T195136Z".to_string());
|
||||||
|
|
||||||
let version = Version::from_semver(release, branch, build, timestamp);
|
let version = Version::from_semver(release, branch, build, timestamp);
|
||||||
assert_eq!(version.release, "5.11.0");
|
assert_eq!(version.release, "5.11.0");
|
||||||
assert_eq!(version.branch, Some("1.0.0".to_string()));
|
assert_eq!(version.branch, Some("1.0.0".to_string()));
|
||||||
assert_eq!(version.build, Some("2020.0.1".to_string()));
|
assert_eq!(version.build, Some("2020.0.1".to_string()));
|
||||||
assert_eq!(version.timestamp, Some("20200421T195136Z".to_string()));
|
assert_eq!(version.timestamp, Some("20200421T195136Z".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_new_semver_constructors() {
|
fn test_new_semver_constructors() {
|
||||||
// Test new_semver
|
// Test new_semver
|
||||||
|
|
@ -697,7 +705,7 @@ mod tests {
|
||||||
assert_eq!(version.branch, None);
|
assert_eq!(version.branch, None);
|
||||||
assert_eq!(version.build, None);
|
assert_eq!(version.build, None);
|
||||||
assert_eq!(version.timestamp, None);
|
assert_eq!(version.timestamp, None);
|
||||||
|
|
||||||
// Test with_branch_semver
|
// Test with_branch_semver
|
||||||
let release = semver::Version::new(5, 11, 0);
|
let release = semver::Version::new(5, 11, 0);
|
||||||
let branch = semver::Version::new(1, 0, 0);
|
let branch = semver::Version::new(1, 0, 0);
|
||||||
|
|
@ -706,7 +714,7 @@ mod tests {
|
||||||
assert_eq!(version.branch, Some("1.0.0".to_string()));
|
assert_eq!(version.branch, Some("1.0.0".to_string()));
|
||||||
assert_eq!(version.build, None);
|
assert_eq!(version.build, None);
|
||||||
assert_eq!(version.timestamp, None);
|
assert_eq!(version.timestamp, None);
|
||||||
|
|
||||||
// Test with_build_semver
|
// Test with_build_semver
|
||||||
let release = semver::Version::new(5, 11, 0);
|
let release = semver::Version::new(5, 11, 0);
|
||||||
let branch = Some(semver::Version::new(1, 0, 0));
|
let branch = Some(semver::Version::new(1, 0, 0));
|
||||||
|
|
@ -716,7 +724,7 @@ mod tests {
|
||||||
assert_eq!(version.branch, Some("1.0.0".to_string()));
|
assert_eq!(version.branch, Some("1.0.0".to_string()));
|
||||||
assert_eq!(version.build, Some("2020.0.1".to_string()));
|
assert_eq!(version.build, Some("2020.0.1".to_string()));
|
||||||
assert_eq!(version.timestamp, None);
|
assert_eq!(version.timestamp, None);
|
||||||
|
|
||||||
// Test with_timestamp_semver
|
// Test with_timestamp_semver
|
||||||
let release = semver::Version::new(5, 11, 0);
|
let release = semver::Version::new(5, 11, 0);
|
||||||
let branch = Some(semver::Version::new(1, 0, 0));
|
let branch = Some(semver::Version::new(1, 0, 0));
|
||||||
|
|
@ -728,43 +736,43 @@ mod tests {
|
||||||
assert_eq!(version.build, Some("2020.0.1".to_string()));
|
assert_eq!(version.build, Some("2020.0.1".to_string()));
|
||||||
assert_eq!(version.timestamp, Some("20200421T195136Z".to_string()));
|
assert_eq!(version.timestamp, Some("20200421T195136Z".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_semver() {
|
fn test_to_semver() {
|
||||||
// Test to_semver with all components
|
// Test to_semver with all components
|
||||||
let mut version = Version::new("5.11");
|
let mut version = Version::new("5.11");
|
||||||
version.branch = Some("1.2.3".to_string());
|
version.branch = Some("1.2.3".to_string());
|
||||||
version.build = Some("2020.0.1".to_string());
|
version.build = Some("2020.0.1".to_string());
|
||||||
|
|
||||||
let (release, branch, build) = version.to_semver();
|
let (release, branch, build) = version.to_semver();
|
||||||
|
|
||||||
assert!(release.is_ok());
|
assert!(release.is_ok());
|
||||||
let release = release.unwrap();
|
let release = release.unwrap();
|
||||||
assert_eq!(release.major, 5);
|
assert_eq!(release.major, 5);
|
||||||
assert_eq!(release.minor, 11);
|
assert_eq!(release.minor, 11);
|
||||||
assert_eq!(release.patch, 0);
|
assert_eq!(release.patch, 0);
|
||||||
|
|
||||||
assert!(branch.is_some());
|
assert!(branch.is_some());
|
||||||
let branch = branch.unwrap().unwrap();
|
let branch = branch.unwrap().unwrap();
|
||||||
assert_eq!(branch.major, 1);
|
assert_eq!(branch.major, 1);
|
||||||
assert_eq!(branch.minor, 2);
|
assert_eq!(branch.minor, 2);
|
||||||
assert_eq!(branch.patch, 3);
|
assert_eq!(branch.patch, 3);
|
||||||
|
|
||||||
assert!(build.is_some());
|
assert!(build.is_some());
|
||||||
let build = build.unwrap().unwrap();
|
let build = build.unwrap().unwrap();
|
||||||
assert_eq!(build.major, 2020);
|
assert_eq!(build.major, 2020);
|
||||||
assert_eq!(build.minor, 0);
|
assert_eq!(build.minor, 0);
|
||||||
assert_eq!(build.patch, 1);
|
assert_eq!(build.patch, 1);
|
||||||
|
|
||||||
// Test is_semver_compatible
|
// Test is_semver_compatible
|
||||||
assert!(version.is_semver_compatible());
|
assert!(version.is_semver_compatible());
|
||||||
|
|
||||||
// Test with invalid semver
|
// Test with invalid semver
|
||||||
let mut version = Version::new("5.11");
|
let mut version = Version::new("5.11");
|
||||||
version.branch = Some("invalid".to_string());
|
version.branch = Some("invalid".to_string());
|
||||||
assert!(!version.is_semver_compatible());
|
assert!(!version.is_semver_compatible());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_semver_validation() {
|
fn test_semver_validation() {
|
||||||
// Test valid dot-separated vectors
|
// Test valid dot-separated vectors
|
||||||
|
|
@ -772,14 +780,14 @@ mod tests {
|
||||||
assert!(Version::is_valid_dot_vector("5.11"));
|
assert!(Version::is_valid_dot_vector("5.11"));
|
||||||
assert!(Version::is_valid_dot_vector("5.11.0"));
|
assert!(Version::is_valid_dot_vector("5.11.0"));
|
||||||
assert!(Version::is_valid_dot_vector("2020.0.1.0"));
|
assert!(Version::is_valid_dot_vector("2020.0.1.0"));
|
||||||
|
|
||||||
// Test invalid dot-separated vectors
|
// Test invalid dot-separated vectors
|
||||||
assert!(!Version::is_valid_dot_vector(""));
|
assert!(!Version::is_valid_dot_vector(""));
|
||||||
assert!(!Version::is_valid_dot_vector(".11"));
|
assert!(!Version::is_valid_dot_vector(".11"));
|
||||||
assert!(!Version::is_valid_dot_vector("5."));
|
assert!(!Version::is_valid_dot_vector("5."));
|
||||||
assert!(!Version::is_valid_dot_vector("5..11"));
|
assert!(!Version::is_valid_dot_vector("5..11"));
|
||||||
assert!(!Version::is_valid_dot_vector("5a.11"));
|
assert!(!Version::is_valid_dot_vector("5a.11"));
|
||||||
|
|
||||||
// Test semver validation
|
// Test semver validation
|
||||||
assert!(Version::is_valid_dot_vector("1.2.3"));
|
assert!(Version::is_valid_dot_vector("1.2.3"));
|
||||||
assert!(Version::is_valid_dot_vector("0.0.0"));
|
assert!(Version::is_valid_dot_vector("0.0.0"));
|
||||||
|
|
@ -857,7 +865,8 @@ mod tests {
|
||||||
assert_eq!(version.to_string(), "5.11-2020.0.1.0");
|
assert_eq!(version.to_string(), "5.11-2020.0.1.0");
|
||||||
|
|
||||||
// Test displaying a release, branch, build, and timestamp
|
// Test displaying a release, branch, build, and timestamp
|
||||||
let version = Version::with_timestamp("5.11", Some("1"), Some("2020.0.1.0"), "20200421T195136Z");
|
let version =
|
||||||
|
Version::with_timestamp("5.11", Some("1"), Some("2020.0.1.0"), "20200421T195136Z");
|
||||||
assert_eq!(version.to_string(), "5.11,1-2020.0.1.0:20200421T195136Z");
|
assert_eq!(version.to_string(), "5.11,1-2020.0.1.0:20200421T195136Z");
|
||||||
|
|
||||||
// Test displaying a release and timestamp (no branch or build)
|
// Test displaying a release and timestamp (no branch or build)
|
||||||
|
|
@ -908,7 +917,10 @@ mod tests {
|
||||||
assert_eq!(fmri.version, None);
|
assert_eq!(fmri.version, None);
|
||||||
|
|
||||||
// Test parsing with scheme, publisher, and version
|
// Test parsing with scheme, publisher, and version
|
||||||
let fmri = Fmri::parse("pkg://openindiana.org/web/server/nginx@1.18.0,5.11-2020.0.1.0:20200421T195136Z").unwrap();
|
let fmri = Fmri::parse(
|
||||||
|
"pkg://openindiana.org/web/server/nginx@1.18.0,5.11-2020.0.1.0:20200421T195136Z",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
assert_eq!(fmri.scheme, "pkg");
|
assert_eq!(fmri.scheme, "pkg");
|
||||||
assert_eq!(fmri.publisher, Some("openindiana.org".to_string()));
|
assert_eq!(fmri.publisher, Some("openindiana.org".to_string()));
|
||||||
assert_eq!(fmri.name, "web/server/nginx");
|
assert_eq!(fmri.name, "web/server/nginx");
|
||||||
|
|
@ -947,14 +959,22 @@ mod tests {
|
||||||
// Test displaying a name and version
|
// Test displaying a name and version
|
||||||
let version = Version::with_timestamp("5.11", Some("1"), None, "20200421T195136Z");
|
let version = Version::with_timestamp("5.11", Some("1"), None, "20200421T195136Z");
|
||||||
let fmri = Fmri::with_version("sunos/coreutils", version);
|
let fmri = Fmri::with_version("sunos/coreutils", version);
|
||||||
assert_eq!(fmri.to_string(), "pkg:///sunos/coreutils@5.11,1:20200421T195136Z");
|
assert_eq!(
|
||||||
|
fmri.to_string(),
|
||||||
|
"pkg:///sunos/coreutils@5.11,1:20200421T195136Z"
|
||||||
|
);
|
||||||
|
|
||||||
// Test displaying with publisher
|
// Test displaying with publisher
|
||||||
let fmri = Fmri::with_publisher("openindiana.org", "web/server/nginx", None);
|
let fmri = Fmri::with_publisher("openindiana.org", "web/server/nginx", None);
|
||||||
assert_eq!(fmri.to_string(), "pkg://openindiana.org/web/server/nginx");
|
assert_eq!(fmri.to_string(), "pkg://openindiana.org/web/server/nginx");
|
||||||
|
|
||||||
// Test displaying with publisher and version
|
// Test displaying with publisher and version
|
||||||
let version = Version::with_timestamp("1.18.0", Some("5.11"), Some("2020.0.1.0"), "20200421T195136Z");
|
let version = Version::with_timestamp(
|
||||||
|
"1.18.0",
|
||||||
|
Some("5.11"),
|
||||||
|
Some("2020.0.1.0"),
|
||||||
|
"20200421T195136Z",
|
||||||
|
);
|
||||||
let fmri = Fmri::with_publisher("openindiana.org", "web/server/nginx", Some(version));
|
let fmri = Fmri::with_publisher("openindiana.org", "web/server/nginx", Some(version));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
fmri.to_string(),
|
fmri.to_string(),
|
||||||
|
|
@ -968,31 +988,76 @@ mod tests {
|
||||||
assert_eq!(Version::parse(""), Err(FmriError::InvalidReleaseFormat));
|
assert_eq!(Version::parse(""), Err(FmriError::InvalidReleaseFormat));
|
||||||
assert_eq!(Version::parse(".11"), Err(FmriError::InvalidReleaseFormat));
|
assert_eq!(Version::parse(".11"), Err(FmriError::InvalidReleaseFormat));
|
||||||
assert_eq!(Version::parse("5."), Err(FmriError::InvalidReleaseFormat));
|
assert_eq!(Version::parse("5."), Err(FmriError::InvalidReleaseFormat));
|
||||||
assert_eq!(Version::parse("5..11"), Err(FmriError::InvalidReleaseFormat));
|
assert_eq!(
|
||||||
assert_eq!(Version::parse("5a.11"), Err(FmriError::InvalidReleaseFormat));
|
Version::parse("5..11"),
|
||||||
|
Err(FmriError::InvalidReleaseFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5a.11"),
|
||||||
|
Err(FmriError::InvalidReleaseFormat)
|
||||||
|
);
|
||||||
|
|
||||||
// Test invalid branch format
|
// Test invalid branch format
|
||||||
assert_eq!(Version::parse("5.11,"), Err(FmriError::InvalidBranchFormat));
|
assert_eq!(Version::parse("5.11,"), Err(FmriError::InvalidBranchFormat));
|
||||||
assert_eq!(Version::parse("5.11,.1"), Err(FmriError::InvalidBranchFormat));
|
assert_eq!(
|
||||||
assert_eq!(Version::parse("5.11,1."), Err(FmriError::InvalidBranchFormat));
|
Version::parse("5.11,.1"),
|
||||||
assert_eq!(Version::parse("5.11,1..2"), Err(FmriError::InvalidBranchFormat));
|
Err(FmriError::InvalidBranchFormat)
|
||||||
assert_eq!(Version::parse("5.11,1a.2"), Err(FmriError::InvalidBranchFormat));
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11,1."),
|
||||||
|
Err(FmriError::InvalidBranchFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11,1..2"),
|
||||||
|
Err(FmriError::InvalidBranchFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11,1a.2"),
|
||||||
|
Err(FmriError::InvalidBranchFormat)
|
||||||
|
);
|
||||||
|
|
||||||
// Test invalid build format
|
// Test invalid build format
|
||||||
assert_eq!(Version::parse("5.11-"), Err(FmriError::InvalidBuildFormat));
|
assert_eq!(Version::parse("5.11-"), Err(FmriError::InvalidBuildFormat));
|
||||||
assert_eq!(Version::parse("5.11-.1"), Err(FmriError::InvalidBuildFormat));
|
assert_eq!(
|
||||||
assert_eq!(Version::parse("5.11-1."), Err(FmriError::InvalidBuildFormat));
|
Version::parse("5.11-.1"),
|
||||||
assert_eq!(Version::parse("5.11-1..2"), Err(FmriError::InvalidBuildFormat));
|
Err(FmriError::InvalidBuildFormat)
|
||||||
assert_eq!(Version::parse("5.11-1a.2"), Err(FmriError::InvalidBuildFormat));
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11-1."),
|
||||||
|
Err(FmriError::InvalidBuildFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11-1..2"),
|
||||||
|
Err(FmriError::InvalidBuildFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11-1a.2"),
|
||||||
|
Err(FmriError::InvalidBuildFormat)
|
||||||
|
);
|
||||||
|
|
||||||
// Test invalid timestamp format
|
// Test invalid timestamp format
|
||||||
assert_eq!(Version::parse("5.11:"), Err(FmriError::InvalidTimestampFormat));
|
assert_eq!(
|
||||||
assert_eq!(Version::parse("5.11:xyz"), Err(FmriError::InvalidTimestampFormat));
|
Version::parse("5.11:"),
|
||||||
|
Err(FmriError::InvalidTimestampFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11:xyz"),
|
||||||
|
Err(FmriError::InvalidTimestampFormat)
|
||||||
|
);
|
||||||
|
|
||||||
// Test invalid version format
|
// Test invalid version format
|
||||||
assert_eq!(Version::parse("5.11,1,2"), Err(FmriError::InvalidVersionFormat));
|
assert_eq!(
|
||||||
assert_eq!(Version::parse("5.11-1-2"), Err(FmriError::InvalidVersionFormat));
|
Version::parse("5.11,1,2"),
|
||||||
assert_eq!(Version::parse("5.11:1:2"), Err(FmriError::InvalidVersionFormat));
|
Err(FmriError::InvalidVersionFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11-1-2"),
|
||||||
|
Err(FmriError::InvalidVersionFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Version::parse("5.11:1:2"),
|
||||||
|
Err(FmriError::InvalidVersionFormat)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1001,14 +1066,29 @@ mod tests {
|
||||||
assert_eq!(Fmri::parse(""), Err(FmriError::InvalidFormat));
|
assert_eq!(Fmri::parse(""), Err(FmriError::InvalidFormat));
|
||||||
assert_eq!(Fmri::parse("pkg://"), Err(FmriError::InvalidFormat));
|
assert_eq!(Fmri::parse("pkg://"), Err(FmriError::InvalidFormat));
|
||||||
assert_eq!(Fmri::parse("pkg:///"), Err(FmriError::InvalidFormat));
|
assert_eq!(Fmri::parse("pkg:///"), Err(FmriError::InvalidFormat));
|
||||||
assert_eq!(Fmri::parse("pkg://publisher/"), Err(FmriError::InvalidFormat));
|
assert_eq!(
|
||||||
|
Fmri::parse("pkg://publisher/"),
|
||||||
|
Err(FmriError::InvalidFormat)
|
||||||
|
);
|
||||||
assert_eq!(Fmri::parse("@5.11"), Err(FmriError::InvalidFormat));
|
assert_eq!(Fmri::parse("@5.11"), Err(FmriError::InvalidFormat));
|
||||||
assert_eq!(Fmri::parse("name@version@extra"), Err(FmriError::InvalidFormat));
|
assert_eq!(
|
||||||
|
Fmri::parse("name@version@extra"),
|
||||||
|
Err(FmriError::InvalidFormat)
|
||||||
|
);
|
||||||
|
|
||||||
// Test invalid version
|
// Test invalid version
|
||||||
assert_eq!(Fmri::parse("name@"), Err(FmriError::InvalidReleaseFormat));
|
assert_eq!(Fmri::parse("name@"), Err(FmriError::InvalidReleaseFormat));
|
||||||
assert_eq!(Fmri::parse("name@5.11,"), Err(FmriError::InvalidBranchFormat));
|
assert_eq!(
|
||||||
assert_eq!(Fmri::parse("name@5.11-"), Err(FmriError::InvalidBuildFormat));
|
Fmri::parse("name@5.11,"),
|
||||||
assert_eq!(Fmri::parse("name@5.11:"), Err(FmriError::InvalidTimestampFormat));
|
Err(FmriError::InvalidBranchFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Fmri::parse("name@5.11-"),
|
||||||
|
Err(FmriError::InvalidBuildFormat)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Fmri::parse("name@5.11:"),
|
||||||
|
Err(FmriError::InvalidTimestampFormat)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
mod properties;
|
mod properties;
|
||||||
|
|
||||||
|
use properties::*;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use properties::*;
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
|
@ -29,7 +29,7 @@ pub struct Image {
|
||||||
|
|
||||||
impl Image {
|
impl Image {
|
||||||
pub fn new<P: Into<PathBuf>>(path: P) -> Image {
|
pub fn new<P: Into<PathBuf>>(path: P) -> Image {
|
||||||
Image{
|
Image {
|
||||||
path: path.into(),
|
path: path.into(),
|
||||||
version: 5,
|
version: 5,
|
||||||
variants: HashMap::new(),
|
variants: HashMap::new(),
|
||||||
|
|
@ -55,4 +55,4 @@ impl Image {
|
||||||
Image::new(path.as_ref())
|
Image::new(path.as_ref())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@ pub enum ImageProperty {
|
||||||
None,
|
None,
|
||||||
Array(Vec<ImageProperty>),
|
Array(Vec<ImageProperty>),
|
||||||
Integer(i32),
|
Integer(i32),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@
|
||||||
pub mod actions;
|
pub mod actions;
|
||||||
pub mod digest;
|
pub mod digest;
|
||||||
pub mod fmri;
|
pub mod fmri;
|
||||||
pub mod payload;
|
|
||||||
pub mod image;
|
pub mod image;
|
||||||
|
pub mod payload;
|
||||||
pub mod repository;
|
pub mod repository;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
@ -987,12 +987,17 @@ depend facet.version-lock.system/mozilla-nss=true fmri=system/mozilla-nss@3.51.1
|
||||||
..Dependency::default()
|
..Dependency::default()
|
||||||
},
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
fmri: Some(Fmri::parse("pkg:/system/file-system/nfs@0.5.11,5.11-2020.0.1.19951").unwrap()),
|
fmri: Some(
|
||||||
|
Fmri::parse("pkg:/system/file-system/nfs@0.5.11,5.11-2020.0.1.19951").unwrap(),
|
||||||
|
),
|
||||||
dependency_type: "incorporate".to_string(),
|
dependency_type: "incorporate".to_string(),
|
||||||
..Dependency::default()
|
..Dependency::default()
|
||||||
},
|
},
|
||||||
Dependency {
|
Dependency {
|
||||||
fmri: Some(Fmri::parse("pkg:/system/data/hardware-registry@2020.2.22,5.11-2020.0.1.19951").unwrap()),
|
fmri: Some(
|
||||||
|
Fmri::parse("pkg:/system/data/hardware-registry@2020.2.22,5.11-2020.0.1.19951")
|
||||||
|
.unwrap(),
|
||||||
|
),
|
||||||
dependency_type: "incorporate".to_string(),
|
dependency_type: "incorporate".to_string(),
|
||||||
facets: hashmap! {
|
facets: hashmap! {
|
||||||
"version-lock.system/data/hardware-registry".to_string() => Facet{
|
"version-lock.system/data/hardware-registry".to_string() => Facet{
|
||||||
|
|
@ -1038,7 +1043,7 @@ depend facet.version-lock.system/mozilla-nss=true fmri=system/mozilla-nss@3.51.1
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(dependency.fmri.is_none(), test_results[pos].fmri.is_none());
|
assert_eq!(dependency.fmri.is_none(), test_results[pos].fmri.is_none());
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
dependency.dependency_type,
|
dependency.dependency_type,
|
||||||
test_results[pos].dependency_type
|
test_results[pos].dependency_type
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use crate::digest::{Digest, DigestAlgorithm, DigestError, DigestSource};
|
use crate::digest::{Digest, DigestAlgorithm, DigestError, DigestSource};
|
||||||
|
use diff::Diff;
|
||||||
use object::Object;
|
use object::Object;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::io::Error as IOError;
|
use std::io::Error as IOError;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use diff::Diff;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
type Result<T> = StdResult<T, PayloadError>;
|
type Result<T> = StdResult<T, PayloadError>;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
@ -27,11 +27,12 @@ fn convert_system_time_to_datetime(time: &SystemTime) -> chrono::DateTime<chrono
|
||||||
let secs = duration.as_secs() as i64;
|
let secs = duration.as_secs() as i64;
|
||||||
let nanos = duration.subsec_nanos();
|
let nanos = duration.subsec_nanos();
|
||||||
|
|
||||||
chrono::DateTime::from_timestamp(secs, nanos)
|
chrono::DateTime::from_timestamp(secs, nanos).unwrap_or_else(|| {
|
||||||
.unwrap_or_else(|| chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
|
chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
|
||||||
chrono::NaiveDateTime::default(),
|
chrono::NaiveDateTime::default(),
|
||||||
chrono::Utc,
|
chrono::Utc,
|
||||||
))
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Catalog version
|
/// Catalog version
|
||||||
|
|
@ -52,7 +53,7 @@ pub struct CatalogPartInfo {
|
||||||
/// Last modified timestamp in ISO-8601 'basic format' date in UTC
|
/// Last modified timestamp in ISO-8601 'basic format' date in UTC
|
||||||
#[serde(rename = "last-modified")]
|
#[serde(rename = "last-modified")]
|
||||||
pub last_modified: String,
|
pub last_modified: String,
|
||||||
|
|
||||||
/// Optional SHA-1 signature of the catalog part
|
/// Optional SHA-1 signature of the catalog part
|
||||||
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature_sha1: Option<String>,
|
pub signature_sha1: Option<String>,
|
||||||
|
|
@ -64,7 +65,7 @@ pub struct UpdateLogInfo {
|
||||||
/// Last modified timestamp in ISO-8601 'basic format' date in UTC
|
/// Last modified timestamp in ISO-8601 'basic format' date in UTC
|
||||||
#[serde(rename = "last-modified")]
|
#[serde(rename = "last-modified")]
|
||||||
pub last_modified: String,
|
pub last_modified: String,
|
||||||
|
|
||||||
/// Optional SHA-1 signature of the update log
|
/// Optional SHA-1 signature of the update log
|
||||||
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature_sha1: Option<String>,
|
pub signature_sha1: Option<String>,
|
||||||
|
|
@ -76,29 +77,29 @@ pub struct CatalogAttrs {
|
||||||
/// Optional signature information
|
/// Optional signature information
|
||||||
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature: Option<HashMap<String, String>>,
|
pub signature: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
/// Creation timestamp in ISO-8601 'basic format' date in UTC
|
/// Creation timestamp in ISO-8601 'basic format' date in UTC
|
||||||
pub created: String,
|
pub created: String,
|
||||||
|
|
||||||
/// Last modified timestamp in ISO-8601 'basic format' date in UTC
|
/// Last modified timestamp in ISO-8601 'basic format' date in UTC
|
||||||
#[serde(rename = "last-modified")]
|
#[serde(rename = "last-modified")]
|
||||||
pub last_modified: String,
|
pub last_modified: String,
|
||||||
|
|
||||||
/// Number of unique package stems in the catalog
|
/// Number of unique package stems in the catalog
|
||||||
#[serde(rename = "package-count")]
|
#[serde(rename = "package-count")]
|
||||||
pub package_count: usize,
|
pub package_count: usize,
|
||||||
|
|
||||||
/// Number of unique package versions in the catalog
|
/// Number of unique package versions in the catalog
|
||||||
#[serde(rename = "package-version-count")]
|
#[serde(rename = "package-version-count")]
|
||||||
pub package_version_count: usize,
|
pub package_version_count: usize,
|
||||||
|
|
||||||
/// Available catalog parts
|
/// Available catalog parts
|
||||||
pub parts: HashMap<String, CatalogPartInfo>,
|
pub parts: HashMap<String, CatalogPartInfo>,
|
||||||
|
|
||||||
/// Available update logs
|
/// Available update logs
|
||||||
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
#[serde(skip_serializing_if = "HashMap::is_empty")]
|
||||||
pub updates: HashMap<String, UpdateLogInfo>,
|
pub updates: HashMap<String, UpdateLogInfo>,
|
||||||
|
|
||||||
/// Catalog version
|
/// Catalog version
|
||||||
pub version: u32,
|
pub version: u32,
|
||||||
}
|
}
|
||||||
|
|
@ -108,7 +109,7 @@ impl CatalogAttrs {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let timestamp = format_iso8601_basic(&now);
|
let timestamp = format_iso8601_basic(&now);
|
||||||
|
|
||||||
CatalogAttrs {
|
CatalogAttrs {
|
||||||
signature: None,
|
signature: None,
|
||||||
created: timestamp.clone(),
|
created: timestamp.clone(),
|
||||||
|
|
@ -120,14 +121,14 @@ impl CatalogAttrs {
|
||||||
version: CatalogVersion::V1 as u32,
|
version: CatalogVersion::V1 as u32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save catalog attributes to a file
|
/// Save catalog attributes to a file
|
||||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
let json = serde_json::to_string_pretty(self)?;
|
let json = serde_json::to_string_pretty(self)?;
|
||||||
fs::write(path, json)?;
|
fs::write(path, json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load catalog attributes from a file
|
/// Load catalog attributes from a file
|
||||||
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
let json = fs::read_to_string(path)?;
|
let json = fs::read_to_string(path)?;
|
||||||
|
|
@ -141,11 +142,11 @@ impl CatalogAttrs {
|
||||||
pub struct PackageVersionEntry {
|
pub struct PackageVersionEntry {
|
||||||
/// Package version string
|
/// Package version string
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
|
||||||
/// Optional actions associated with this package version
|
/// Optional actions associated with this package version
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub actions: Option<Vec<String>>,
|
pub actions: Option<Vec<String>>,
|
||||||
|
|
||||||
/// Optional SHA-1 signature of the package manifest
|
/// Optional SHA-1 signature of the package manifest
|
||||||
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature_sha1: Option<String>,
|
pub signature_sha1: Option<String>,
|
||||||
|
|
@ -157,7 +158,7 @@ pub struct CatalogPart {
|
||||||
/// Optional signature information
|
/// Optional signature information
|
||||||
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature: Option<HashMap<String, String>>,
|
pub signature: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
/// Packages by publisher and stem
|
/// Packages by publisher and stem
|
||||||
pub packages: HashMap<String, HashMap<String, Vec<PackageVersionEntry>>>,
|
pub packages: HashMap<String, HashMap<String, Vec<PackageVersionEntry>>>,
|
||||||
}
|
}
|
||||||
|
|
@ -170,12 +171,23 @@ impl CatalogPart {
|
||||||
packages: HashMap::new(),
|
packages: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a package to the catalog part
|
/// Add a package to the catalog part
|
||||||
pub fn add_package(&mut self, publisher: &str, fmri: &Fmri, actions: Option<Vec<String>>, signature: Option<String>) {
|
pub fn add_package(
|
||||||
let publisher_packages = self.packages.entry(publisher.to_string()).or_insert_with(HashMap::new);
|
&mut self,
|
||||||
let stem_versions = publisher_packages.entry(fmri.stem().to_string()).or_insert_with(Vec::new);
|
publisher: &str,
|
||||||
|
fmri: &Fmri,
|
||||||
|
actions: Option<Vec<String>>,
|
||||||
|
signature: Option<String>,
|
||||||
|
) {
|
||||||
|
let publisher_packages = self
|
||||||
|
.packages
|
||||||
|
.entry(publisher.to_string())
|
||||||
|
.or_insert_with(HashMap::new);
|
||||||
|
let stem_versions = publisher_packages
|
||||||
|
.entry(fmri.stem().to_string())
|
||||||
|
.or_insert_with(Vec::new);
|
||||||
|
|
||||||
// Check if this version already exists
|
// Check if this version already exists
|
||||||
for entry in stem_versions.iter_mut() {
|
for entry in stem_versions.iter_mut() {
|
||||||
if !fmri.version().is_empty() && entry.version == fmri.version() {
|
if !fmri.version().is_empty() && entry.version == fmri.version() {
|
||||||
|
|
@ -189,25 +201,25 @@ impl CatalogPart {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a new entry
|
// Add a new entry
|
||||||
stem_versions.push(PackageVersionEntry {
|
stem_versions.push(PackageVersionEntry {
|
||||||
version: fmri.version(),
|
version: fmri.version(),
|
||||||
actions,
|
actions,
|
||||||
signature_sha1: signature,
|
signature_sha1: signature,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort versions (should be in ascending order)
|
// Sort versions (should be in ascending order)
|
||||||
stem_versions.sort_by(|a, b| a.version.cmp(&b.version));
|
stem_versions.sort_by(|a, b| a.version.cmp(&b.version));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save a catalog part to a file
|
/// Save a catalog part to a file
|
||||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
let json = serde_json::to_string_pretty(self)?;
|
let json = serde_json::to_string_pretty(self)?;
|
||||||
fs::write(path, json)?;
|
fs::write(path, json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load catalog part from a file
|
/// Load catalog part from a file
|
||||||
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
let json = fs::read_to_string(path)?;
|
let json = fs::read_to_string(path)?;
|
||||||
|
|
@ -231,18 +243,18 @@ pub struct PackageUpdateEntry {
|
||||||
/// Type of operation (add or remove)
|
/// Type of operation (add or remove)
|
||||||
#[serde(rename = "op-type")]
|
#[serde(rename = "op-type")]
|
||||||
pub op_type: CatalogOperationType,
|
pub op_type: CatalogOperationType,
|
||||||
|
|
||||||
/// Timestamp of the operation in ISO-8601 'basic format' date in UTC
|
/// Timestamp of the operation in ISO-8601 'basic format' date in UTC
|
||||||
#[serde(rename = "op-time")]
|
#[serde(rename = "op-time")]
|
||||||
pub op_time: String,
|
pub op_time: String,
|
||||||
|
|
||||||
/// Package version string
|
/// Package version string
|
||||||
pub version: String,
|
pub version: String,
|
||||||
|
|
||||||
/// Catalog part entries
|
/// Catalog part entries
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub catalog_parts: HashMap<String, HashMap<String, Vec<String>>>,
|
pub catalog_parts: HashMap<String, HashMap<String, Vec<String>>>,
|
||||||
|
|
||||||
/// Optional SHA-1 signature of the package manifest
|
/// Optional SHA-1 signature of the package manifest
|
||||||
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "signature-sha-1", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature_sha1: Option<String>,
|
pub signature_sha1: Option<String>,
|
||||||
|
|
@ -254,7 +266,7 @@ pub struct UpdateLog {
|
||||||
/// Optional signature information
|
/// Optional signature information
|
||||||
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
#[serde(rename = "_SIGNATURE", skip_serializing_if = "Option::is_none")]
|
||||||
pub signature: Option<HashMap<String, String>>,
|
pub signature: Option<HashMap<String, String>>,
|
||||||
|
|
||||||
/// Updates by publisher and stem
|
/// Updates by publisher and stem
|
||||||
pub updates: HashMap<String, HashMap<String, Vec<PackageUpdateEntry>>>,
|
pub updates: HashMap<String, HashMap<String, Vec<PackageUpdateEntry>>>,
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +279,7 @@ impl UpdateLog {
|
||||||
updates: HashMap::new(),
|
updates: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a package update to the log
|
/// Add a package update to the log
|
||||||
pub fn add_update(
|
pub fn add_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
|
@ -277,12 +289,17 @@ impl UpdateLog {
|
||||||
catalog_parts: HashMap<String, HashMap<String, Vec<String>>>,
|
catalog_parts: HashMap<String, HashMap<String, Vec<String>>>,
|
||||||
signature: Option<String>,
|
signature: Option<String>,
|
||||||
) {
|
) {
|
||||||
let publisher_updates = self.updates.entry(publisher.to_string()).or_insert_with(HashMap::new);
|
let publisher_updates = self
|
||||||
let stem_updates = publisher_updates.entry(fmri.stem().to_string()).or_insert_with(Vec::new);
|
.updates
|
||||||
|
.entry(publisher.to_string())
|
||||||
|
.or_insert_with(HashMap::new);
|
||||||
|
let stem_updates = publisher_updates
|
||||||
|
.entry(fmri.stem().to_string())
|
||||||
|
.or_insert_with(Vec::new);
|
||||||
|
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let timestamp = format_iso8601_basic(&now);
|
let timestamp = format_iso8601_basic(&now);
|
||||||
|
|
||||||
stem_updates.push(PackageUpdateEntry {
|
stem_updates.push(PackageUpdateEntry {
|
||||||
op_type,
|
op_type,
|
||||||
op_time: timestamp,
|
op_time: timestamp,
|
||||||
|
|
@ -291,14 +308,14 @@ impl UpdateLog {
|
||||||
signature_sha1: signature,
|
signature_sha1: signature,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save update log to a file
|
/// Save update log to a file
|
||||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||||
let json = serde_json::to_string_pretty(self)?;
|
let json = serde_json::to_string_pretty(self)?;
|
||||||
fs::write(path, json)?;
|
fs::write(path, json)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load update log from a file
|
/// Load update log from a file
|
||||||
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self> {
|
||||||
let json = fs::read_to_string(path)?;
|
let json = fs::read_to_string(path)?;
|
||||||
|
|
@ -311,13 +328,13 @@ impl UpdateLog {
|
||||||
pub struct CatalogManager {
|
pub struct CatalogManager {
|
||||||
/// Path to the catalog directory
|
/// Path to the catalog directory
|
||||||
catalog_dir: PathBuf,
|
catalog_dir: PathBuf,
|
||||||
|
|
||||||
/// Catalog attributes
|
/// Catalog attributes
|
||||||
attrs: CatalogAttrs,
|
attrs: CatalogAttrs,
|
||||||
|
|
||||||
/// Catalog parts
|
/// Catalog parts
|
||||||
parts: HashMap<String, CatalogPart>,
|
parts: HashMap<String, CatalogPart>,
|
||||||
|
|
||||||
/// Update logs
|
/// Update logs
|
||||||
update_logs: HashMap<String, UpdateLog>,
|
update_logs: HashMap<String, UpdateLog>,
|
||||||
}
|
}
|
||||||
|
|
@ -326,12 +343,12 @@ impl CatalogManager {
|
||||||
/// Create a new catalog manager
|
/// Create a new catalog manager
|
||||||
pub fn new<P: AsRef<Path>>(catalog_dir: P) -> Result<Self> {
|
pub fn new<P: AsRef<Path>>(catalog_dir: P) -> Result<Self> {
|
||||||
let catalog_dir = catalog_dir.as_ref().to_path_buf();
|
let catalog_dir = catalog_dir.as_ref().to_path_buf();
|
||||||
|
|
||||||
// Create catalog directory if it doesn't exist
|
// Create catalog directory if it doesn't exist
|
||||||
if !catalog_dir.exists() {
|
if !catalog_dir.exists() {
|
||||||
fs::create_dir_all(&catalog_dir)?;
|
fs::create_dir_all(&catalog_dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to load existing catalog attributes
|
// Try to load existing catalog attributes
|
||||||
let attrs_path = catalog_dir.join("catalog.attrs");
|
let attrs_path = catalog_dir.join("catalog.attrs");
|
||||||
let attrs = if attrs_path.exists() {
|
let attrs = if attrs_path.exists() {
|
||||||
|
|
@ -339,7 +356,7 @@ impl CatalogManager {
|
||||||
} else {
|
} else {
|
||||||
CatalogAttrs::new()
|
CatalogAttrs::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(CatalogManager {
|
Ok(CatalogManager {
|
||||||
catalog_dir,
|
catalog_dir,
|
||||||
attrs,
|
attrs,
|
||||||
|
|
@ -347,27 +364,27 @@ impl CatalogManager {
|
||||||
update_logs: HashMap::new(),
|
update_logs: HashMap::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get catalog attributes
|
/// Get catalog attributes
|
||||||
pub fn attrs(&self) -> &CatalogAttrs {
|
pub fn attrs(&self) -> &CatalogAttrs {
|
||||||
&self.attrs
|
&self.attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get mutable catalog attributes
|
/// Get mutable catalog attributes
|
||||||
pub fn attrs_mut(&mut self) -> &mut CatalogAttrs {
|
pub fn attrs_mut(&mut self) -> &mut CatalogAttrs {
|
||||||
&mut self.attrs
|
&mut self.attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a catalog part
|
/// Get a catalog part
|
||||||
pub fn get_part(&self, name: &str) -> Option<&CatalogPart> {
|
pub fn get_part(&self, name: &str) -> Option<&CatalogPart> {
|
||||||
self.parts.get(name)
|
self.parts.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable catalog part
|
/// Get a mutable catalog part
|
||||||
pub fn get_part_mut(&mut self, name: &str) -> Option<&mut CatalogPart> {
|
pub fn get_part_mut(&mut self, name: &str) -> Option<&mut CatalogPart> {
|
||||||
self.parts.get_mut(name)
|
self.parts.get_mut(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load a catalog part
|
/// Load a catalog part
|
||||||
pub fn load_part(&mut self, name: &str) -> Result<()> {
|
pub fn load_part(&mut self, name: &str) -> Result<()> {
|
||||||
let part_path = self.catalog_dir.join(name);
|
let part_path = self.catalog_dir.join(name);
|
||||||
|
|
@ -379,7 +396,7 @@ impl CatalogManager {
|
||||||
Err(anyhow::anyhow!("Catalog part does not exist: {}", name))
|
Err(anyhow::anyhow!("Catalog part does not exist: {}", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save a catalog part
|
/// Save a catalog part
|
||||||
pub fn save_part(&self, name: &str) -> Result<()> {
|
pub fn save_part(&self, name: &str) -> Result<()> {
|
||||||
if let Some(part) = self.parts.get(name) {
|
if let Some(part) = self.parts.get(name) {
|
||||||
|
|
@ -390,49 +407,56 @@ impl CatalogManager {
|
||||||
Err(anyhow::anyhow!("Catalog part not loaded: {}", name))
|
Err(anyhow::anyhow!("Catalog part not loaded: {}", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new catalog part
|
/// Create a new catalog part
|
||||||
pub fn create_part(&mut self, name: &str) -> &mut CatalogPart {
|
pub fn create_part(&mut self, name: &str) -> &mut CatalogPart {
|
||||||
self.parts.entry(name.to_string()).or_insert_with(CatalogPart::new)
|
self.parts
|
||||||
|
.entry(name.to_string())
|
||||||
|
.or_insert_with(CatalogPart::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save catalog attributes
|
/// Save catalog attributes
|
||||||
pub fn save_attrs(&self) -> Result<()> {
|
pub fn save_attrs(&self) -> Result<()> {
|
||||||
let attrs_path = self.catalog_dir.join("catalog.attrs");
|
let attrs_path = self.catalog_dir.join("catalog.attrs");
|
||||||
self.attrs.save(&attrs_path)?;
|
self.attrs.save(&attrs_path)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new update log
|
/// Create a new update log
|
||||||
pub fn create_update_log(&mut self, name: &str) -> &mut UpdateLog {
|
pub fn create_update_log(&mut self, name: &str) -> &mut UpdateLog {
|
||||||
self.update_logs.entry(name.to_string()).or_insert_with(UpdateLog::new)
|
self.update_logs
|
||||||
|
.entry(name.to_string())
|
||||||
|
.or_insert_with(UpdateLog::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save an update log
|
/// Save an update log
|
||||||
pub fn save_update_log(&self, name: &str) -> Result<()> {
|
pub fn save_update_log(&self, name: &str) -> Result<()> {
|
||||||
if let Some(log) = self.update_logs.get(name) {
|
if let Some(log) = self.update_logs.get(name) {
|
||||||
let log_path = self.catalog_dir.join(name);
|
let log_path = self.catalog_dir.join(name);
|
||||||
log.save(&log_path)?;
|
log.save(&log_path)?;
|
||||||
|
|
||||||
// Update catalog attributes
|
// Update catalog attributes
|
||||||
let now = SystemTime::now();
|
let now = SystemTime::now();
|
||||||
let timestamp = format_iso8601_basic(&now);
|
let timestamp = format_iso8601_basic(&now);
|
||||||
|
|
||||||
let mut attrs = self.attrs.clone();
|
let mut attrs = self.attrs.clone();
|
||||||
attrs.updates.insert(name.to_string(), UpdateLogInfo {
|
attrs.updates.insert(
|
||||||
last_modified: timestamp,
|
name.to_string(),
|
||||||
signature_sha1: None,
|
UpdateLogInfo {
|
||||||
});
|
last_modified: timestamp,
|
||||||
|
signature_sha1: None,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
let attrs_path = self.catalog_dir.join("catalog.attrs");
|
let attrs_path = self.catalog_dir.join("catalog.attrs");
|
||||||
attrs.save(&attrs_path)?;
|
attrs.save(&attrs_path)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(anyhow::anyhow!("Update log not loaded: {}", name))
|
Err(anyhow::anyhow!("Update log not loaded: {}", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load an update log
|
/// Load an update log
|
||||||
pub fn load_update_log(&mut self, name: &str) -> Result<()> {
|
pub fn load_update_log(&mut self, name: &str) -> Result<()> {
|
||||||
let log_path = self.catalog_dir.join(name);
|
let log_path = self.catalog_dir.join(name);
|
||||||
|
|
@ -444,14 +468,14 @@ impl CatalogManager {
|
||||||
Err(anyhow::anyhow!("Update log does not exist: {}", name))
|
Err(anyhow::anyhow!("Update log does not exist: {}", name))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an update log
|
/// Get an update log
|
||||||
pub fn get_update_log(&self, name: &str) -> Option<&UpdateLog> {
|
pub fn get_update_log(&self, name: &str) -> Option<&UpdateLog> {
|
||||||
self.update_logs.get(name)
|
self.update_logs.get(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a mutable update log
|
/// Get a mutable update log
|
||||||
pub fn get_update_log_mut(&mut self, name: &str) -> Option<&mut UpdateLog> {
|
pub fn get_update_log_mut(&mut self, name: &str) -> Option<&mut UpdateLog> {
|
||||||
self.update_logs.get_mut(name)
|
self.update_logs.get_mut(name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,7 @@ impl Transaction {
|
||||||
|
|
||||||
// Move the manifest to its final location in the repository
|
// Move the manifest to its final location in the repository
|
||||||
// Store in both the pkg directory and the trans directory as required
|
// Store in both the pkg directory and the trans directory as required
|
||||||
|
|
||||||
// Extract package name from manifest
|
// Extract package name from manifest
|
||||||
let mut package_name = String::from("unknown");
|
let mut package_name = String::from("unknown");
|
||||||
for attr in &self.manifest.attributes {
|
for attr in &self.manifest.attributes {
|
||||||
|
|
@ -442,7 +442,7 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the pkg directory path based on publisher
|
// Determine the pkg directory path based on publisher
|
||||||
let pkg_manifest_path = if let Some(publisher) = &self.publisher {
|
let pkg_manifest_path = if let Some(publisher) = &self.publisher {
|
||||||
// Create publisher directory if it doesn't exist
|
// Create publisher directory if it doesn't exist
|
||||||
|
|
@ -450,14 +450,14 @@ impl Transaction {
|
||||||
if !publisher_dir.exists() {
|
if !publisher_dir.exists() {
|
||||||
fs::create_dir_all(&publisher_dir)?;
|
fs::create_dir_all(&publisher_dir)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store in publisher-specific directory with package name
|
// Store in publisher-specific directory with package name
|
||||||
publisher_dir.join(format!("{}.manifest", package_name))
|
publisher_dir.join(format!("{}.manifest", package_name))
|
||||||
} else {
|
} else {
|
||||||
// Store in root pkg directory (legacy behavior)
|
// Store in root pkg directory (legacy behavior)
|
||||||
self.repo.join("pkg").join("manifest")
|
self.repo.join("pkg").join("manifest")
|
||||||
};
|
};
|
||||||
|
|
||||||
let trans_manifest_path = self
|
let trans_manifest_path = self
|
||||||
.repo
|
.repo
|
||||||
.join("trans")
|
.join("trans")
|
||||||
|
|
@ -623,7 +623,8 @@ impl ReadableRepository for FileBackend {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
// Skip directories, only process files with .manifest extension
|
// Skip directories, only process files with .manifest extension
|
||||||
if path.is_file() && path.extension().map_or(false, |ext| ext == "manifest") {
|
if path.is_file() && path.extension().map_or(false, |ext| ext == "manifest")
|
||||||
|
{
|
||||||
// Parse the manifest file to get real package information
|
// Parse the manifest file to get real package information
|
||||||
match Manifest::parse_file(&path) {
|
match Manifest::parse_file(&path) {
|
||||||
Ok(manifest) => {
|
Ok(manifest) => {
|
||||||
|
|
@ -765,7 +766,8 @@ impl ReadableRepository for FileBackend {
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
|
||||||
// Skip directories, only process files with .manifest extension
|
// Skip directories, only process files with .manifest extension
|
||||||
if path.is_file() && path.extension().map_or(false, |ext| ext == "manifest") {
|
if path.is_file() && path.extension().map_or(false, |ext| ext == "manifest")
|
||||||
|
{
|
||||||
// Parse the manifest file to get package information
|
// Parse the manifest file to get package information
|
||||||
match Manifest::parse_file(&path) {
|
match Manifest::parse_file(&path) {
|
||||||
Ok(manifest) => {
|
Ok(manifest) => {
|
||||||
|
|
@ -1260,7 +1262,9 @@ impl FileBackend {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get or initialize the catalog manager
|
/// Get or initialize the catalog manager
|
||||||
pub fn get_catalog_manager(&mut self) -> Result<&mut crate::repository::catalog::CatalogManager> {
|
pub fn get_catalog_manager(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<&mut crate::repository::catalog::CatalogManager> {
|
||||||
if self.catalog_manager.is_none() {
|
if self.catalog_manager.is_none() {
|
||||||
let catalog_dir = self.path.join("catalog");
|
let catalog_dir = self.path.join("catalog");
|
||||||
self.catalog_manager = Some(crate::repository::catalog::CatalogManager::new(
|
self.catalog_manager = Some(crate::repository::catalog::CatalogManager::new(
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,18 @@
|
||||||
// obtain one at https://mozilla.org/MPL/2.0/.
|
// obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use std::path::Path;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
mod catalog;
|
||||||
mod file_backend;
|
mod file_backend;
|
||||||
mod rest_backend;
|
mod rest_backend;
|
||||||
mod catalog;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
pub use catalog::{CatalogAttrs, CatalogManager, CatalogOperationType, CatalogPart, UpdateLog};
|
||||||
pub use file_backend::FileBackend;
|
pub use file_backend::FileBackend;
|
||||||
pub use rest_backend::RestBackend;
|
pub use rest_backend::RestBackend;
|
||||||
pub use catalog::{CatalogManager, CatalogAttrs, CatalogPart, UpdateLog, CatalogOperationType};
|
|
||||||
|
|
||||||
/// Repository configuration filename
|
/// Repository configuration filename
|
||||||
pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository";
|
pub const REPOSITORY_CONFIG_FILENAME: &str = "pkg6.repository";
|
||||||
|
|
@ -110,62 +110,85 @@ impl Default for RepositoryConfig {
|
||||||
/// Repository trait for read-only operations
|
/// Repository trait for read-only operations
|
||||||
pub trait ReadableRepository {
|
pub trait ReadableRepository {
|
||||||
/// Open an existing repository
|
/// Open an existing repository
|
||||||
fn open<P: AsRef<Path>>(path: P) -> Result<Self> where Self: Sized;
|
fn open<P: AsRef<Path>>(path: P) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
/// Get repository information
|
/// Get repository information
|
||||||
fn get_info(&self) -> Result<RepositoryInfo>;
|
fn get_info(&self) -> Result<RepositoryInfo>;
|
||||||
|
|
||||||
/// List packages in the repository
|
/// List packages in the repository
|
||||||
fn list_packages(&self, publisher: Option<&str>, pattern: Option<&str>) -> Result<Vec<PackageInfo>>;
|
fn list_packages(
|
||||||
|
&self,
|
||||||
|
publisher: Option<&str>,
|
||||||
|
pattern: Option<&str>,
|
||||||
|
) -> Result<Vec<PackageInfo>>;
|
||||||
|
|
||||||
/// Show contents of packages
|
/// Show contents of packages
|
||||||
fn show_contents(&self, publisher: Option<&str>, pattern: Option<&str>, action_types: Option<&[String]>) -> Result<Vec<PackageContents>>;
|
fn show_contents(
|
||||||
|
&self,
|
||||||
|
publisher: Option<&str>,
|
||||||
|
pattern: Option<&str>,
|
||||||
|
action_types: Option<&[String]>,
|
||||||
|
) -> Result<Vec<PackageContents>>;
|
||||||
|
|
||||||
/// Search for packages in the repository
|
/// Search for packages in the repository
|
||||||
///
|
///
|
||||||
/// This method searches for packages in the repository using the search index.
|
/// This method searches for packages in the repository using the search index.
|
||||||
/// It returns a list of packages that match the search query.
|
/// It returns a list of packages that match the search query.
|
||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
///
|
///
|
||||||
/// * `query` - The search query
|
/// * `query` - The search query
|
||||||
/// * `publisher` - Optional publisher to limit the search to
|
/// * `publisher` - Optional publisher to limit the search to
|
||||||
/// * `limit` - Optional maximum number of results to return
|
/// * `limit` - Optional maximum number of results to return
|
||||||
fn search(&self, query: &str, publisher: Option<&str>, limit: Option<usize>) -> Result<Vec<PackageInfo>>;
|
fn search(
|
||||||
|
&self,
|
||||||
|
query: &str,
|
||||||
|
publisher: Option<&str>,
|
||||||
|
limit: Option<usize>,
|
||||||
|
) -> Result<Vec<PackageInfo>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repository trait for write operations
|
/// Repository trait for write operations
|
||||||
pub trait WritableRepository {
|
pub trait WritableRepository {
|
||||||
/// Create a new repository at the specified path
|
/// Create a new repository at the specified path
|
||||||
fn create<P: AsRef<Path>>(path: P, version: RepositoryVersion) -> Result<Self> where Self: Sized;
|
fn create<P: AsRef<Path>>(path: P, version: RepositoryVersion) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
/// Save the repository configuration
|
/// Save the repository configuration
|
||||||
fn save_config(&self) -> Result<()>;
|
fn save_config(&self) -> Result<()>;
|
||||||
|
|
||||||
/// Add a publisher to the repository
|
/// Add a publisher to the repository
|
||||||
fn add_publisher(&mut self, publisher: &str) -> Result<()>;
|
fn add_publisher(&mut self, publisher: &str) -> Result<()>;
|
||||||
|
|
||||||
/// Remove a publisher from the repository
|
/// Remove a publisher from the repository
|
||||||
fn remove_publisher(&mut self, publisher: &str, dry_run: bool) -> Result<()>;
|
fn remove_publisher(&mut self, publisher: &str, dry_run: bool) -> Result<()>;
|
||||||
|
|
||||||
/// Set a repository property
|
/// Set a repository property
|
||||||
fn set_property(&mut self, property: &str, value: &str) -> Result<()>;
|
fn set_property(&mut self, property: &str, value: &str) -> Result<()>;
|
||||||
|
|
||||||
/// Set a publisher property
|
/// Set a publisher property
|
||||||
fn set_publisher_property(&mut self, publisher: &str, property: &str, value: &str) -> Result<()>;
|
fn set_publisher_property(
|
||||||
|
&mut self,
|
||||||
|
publisher: &str,
|
||||||
|
property: &str,
|
||||||
|
value: &str,
|
||||||
|
) -> Result<()>;
|
||||||
|
|
||||||
/// Rebuild repository metadata
|
/// Rebuild repository metadata
|
||||||
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
fn rebuild(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
||||||
|
|
||||||
/// Refresh repository metadata
|
/// Refresh repository metadata
|
||||||
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
fn refresh(&self, publisher: Option<&str>, no_catalog: bool, no_index: bool) -> Result<()>;
|
||||||
|
|
||||||
/// Set the default publisher for the repository
|
/// Set the default publisher for the repository
|
||||||
fn set_default_publisher(&mut self, publisher: &str) -> Result<()>;
|
fn set_default_publisher(&mut self, publisher: &str) -> Result<()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Repository trait defining the interface for all repository backends
|
/// Repository trait defining the interface for all repository backends
|
||||||
///
|
///
|
||||||
/// This trait combines both ReadableRepository and WritableRepository traits
|
/// This trait combines both ReadableRepository and WritableRepository traits
|
||||||
/// for backward compatibility.
|
/// for backward compatibility.
|
||||||
pub trait Repository: ReadableRepository + WritableRepository {}
|
pub trait Repository: ReadableRepository + WritableRepository {}
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ mod tests {
|
||||||
use crate::actions::Manifest;
|
use crate::actions::Manifest;
|
||||||
use crate::fmri::Fmri;
|
use crate::fmri::Fmri;
|
||||||
use crate::repository::{
|
use crate::repository::{
|
||||||
CatalogManager, FileBackend, ReadableRepository,
|
CatalogManager, FileBackend, ReadableRepository, RepositoryVersion, WritableRepository,
|
||||||
RepositoryVersion, WritableRepository, REPOSITORY_CONFIG_FILENAME,
|
REPOSITORY_CONFIG_FILENAME,
|
||||||
};
|
};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
@ -65,7 +65,7 @@ mod tests {
|
||||||
PathBuf::from("/tmp/pkg6_test/manifests"),
|
PathBuf::from("/tmp/pkg6_test/manifests"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to publish a package to a repository
|
// Helper function to publish a package to a repository
|
||||||
fn publish_package(
|
fn publish_package(
|
||||||
repo: &mut FileBackend,
|
repo: &mut FileBackend,
|
||||||
|
|
@ -73,26 +73,38 @@ mod tests {
|
||||||
prototype_dir: &PathBuf,
|
prototype_dir: &PathBuf,
|
||||||
publisher: &str,
|
publisher: &str,
|
||||||
) -> Result<(), anyhow::Error> {
|
) -> Result<(), anyhow::Error> {
|
||||||
println!("Publishing package from manifest: {}", manifest_path.display());
|
println!(
|
||||||
|
"Publishing package from manifest: {}",
|
||||||
|
manifest_path.display()
|
||||||
|
);
|
||||||
println!("Prototype directory: {}", prototype_dir.display());
|
println!("Prototype directory: {}", prototype_dir.display());
|
||||||
println!("Publisher: {}", publisher);
|
println!("Publisher: {}", publisher);
|
||||||
|
|
||||||
// Check if the manifest file exists
|
// Check if the manifest file exists
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
println!("Error: Manifest file does not exist");
|
println!("Error: Manifest file does not exist");
|
||||||
return Err(anyhow::anyhow!("Manifest file does not exist: {}", manifest_path.display()));
|
return Err(anyhow::anyhow!(
|
||||||
|
"Manifest file does not exist: {}",
|
||||||
|
manifest_path.display()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the prototype directory exists
|
// Check if the prototype directory exists
|
||||||
if !prototype_dir.exists() {
|
if !prototype_dir.exists() {
|
||||||
println!("Error: Prototype directory does not exist");
|
println!("Error: Prototype directory does not exist");
|
||||||
return Err(anyhow::anyhow!("Prototype directory does not exist: {}", prototype_dir.display()));
|
return Err(anyhow::anyhow!(
|
||||||
|
"Prototype directory does not exist: {}",
|
||||||
|
prototype_dir.display()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the manifest file
|
// Parse the manifest file
|
||||||
println!("Parsing manifest file...");
|
println!("Parsing manifest file...");
|
||||||
let manifest = Manifest::parse_file(manifest_path)?;
|
let manifest = Manifest::parse_file(manifest_path)?;
|
||||||
println!("Manifest parsed successfully. Files: {}", manifest.files.len());
|
println!(
|
||||||
|
"Manifest parsed successfully. Files: {}",
|
||||||
|
manifest.files.len()
|
||||||
|
);
|
||||||
|
|
||||||
// Begin a transaction
|
// Begin a transaction
|
||||||
println!("Beginning transaction...");
|
println!("Beginning transaction...");
|
||||||
|
|
@ -103,13 +115,16 @@ mod tests {
|
||||||
for file_action in manifest.files.iter() {
|
for file_action in manifest.files.iter() {
|
||||||
// Construct the full path to the file in the prototype directory
|
// Construct the full path to the file in the prototype directory
|
||||||
let file_path = prototype_dir.join(&file_action.path);
|
let file_path = prototype_dir.join(&file_action.path);
|
||||||
|
|
||||||
// Check if the file exists
|
// Check if the file exists
|
||||||
if !file_path.exists() {
|
if !file_path.exists() {
|
||||||
println!("Warning: File does not exist in prototype directory: {}", file_path.display());
|
println!(
|
||||||
|
"Warning: File does not exist in prototype directory: {}",
|
||||||
|
file_path.display()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the file to the transaction
|
// Add the file to the transaction
|
||||||
println!("Adding file: {}", file_action.path);
|
println!("Adding file: {}", file_action.path);
|
||||||
transaction.add_file(file_action.clone(), &file_path)?;
|
transaction.add_file(file_action.clone(), &file_path)?;
|
||||||
|
|
@ -127,14 +142,17 @@ mod tests {
|
||||||
println!("Committing transaction...");
|
println!("Committing transaction...");
|
||||||
transaction.commit()?;
|
transaction.commit()?;
|
||||||
println!("Transaction committed successfully");
|
println!("Transaction committed successfully");
|
||||||
|
|
||||||
// Debug: Check if the package manifest was stored in the correct location
|
// Debug: Check if the package manifest was stored in the correct location
|
||||||
let publisher_pkg_dir = repo.path.join("pkg").join(publisher);
|
let publisher_pkg_dir = repo.path.join("pkg").join(publisher);
|
||||||
println!("Publisher package directory: {}", publisher_pkg_dir.display());
|
println!(
|
||||||
|
"Publisher package directory: {}",
|
||||||
|
publisher_pkg_dir.display()
|
||||||
|
);
|
||||||
|
|
||||||
if publisher_pkg_dir.exists() {
|
if publisher_pkg_dir.exists() {
|
||||||
println!("Publisher directory exists");
|
println!("Publisher directory exists");
|
||||||
|
|
||||||
// List files in the publisher directory
|
// List files in the publisher directory
|
||||||
if let Ok(entries) = std::fs::read_dir(&publisher_pkg_dir) {
|
if let Ok(entries) = std::fs::read_dir(&publisher_pkg_dir) {
|
||||||
println!("Files in publisher directory:");
|
println!("Files in publisher directory:");
|
||||||
|
|
@ -257,15 +275,15 @@ mod tests {
|
||||||
|
|
||||||
// Check that the files were published
|
// Check that the files were published
|
||||||
assert!(repo_path.join("file").exists());
|
assert!(repo_path.join("file").exists());
|
||||||
|
|
||||||
// Get repository information
|
// Get repository information
|
||||||
let repo_info = repo.get_info().unwrap();
|
let repo_info = repo.get_info().unwrap();
|
||||||
|
|
||||||
// Check that the publisher information is correct
|
// Check that the publisher information is correct
|
||||||
assert_eq!(repo_info.publishers.len(), 1);
|
assert_eq!(repo_info.publishers.len(), 1);
|
||||||
let publisher_info = &repo_info.publishers[0];
|
let publisher_info = &repo_info.publishers[0];
|
||||||
assert_eq!(publisher_info.name, "test");
|
assert_eq!(publisher_info.name, "test");
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
@ -291,13 +309,13 @@ mod tests {
|
||||||
|
|
||||||
// List packages
|
// List packages
|
||||||
let packages = repo.list_packages(Some("test"), None).unwrap();
|
let packages = repo.list_packages(Some("test"), None).unwrap();
|
||||||
|
|
||||||
// Check that packages were listed
|
// Check that packages were listed
|
||||||
assert!(!packages.is_empty());
|
assert!(!packages.is_empty());
|
||||||
|
|
||||||
// Check that the package name is correct
|
// Check that the package name is correct
|
||||||
assert_eq!(packages[0].fmri.name, "example");
|
assert_eq!(packages[0].fmri.name, "example");
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
@ -323,20 +341,22 @@ mod tests {
|
||||||
|
|
||||||
// Show contents
|
// Show contents
|
||||||
let contents = repo.show_contents(Some("test"), None, None).unwrap();
|
let contents = repo.show_contents(Some("test"), None, None).unwrap();
|
||||||
|
|
||||||
// Check that contents were shown
|
// Check that contents were shown
|
||||||
assert!(!contents.is_empty());
|
assert!(!contents.is_empty());
|
||||||
|
|
||||||
// Check that the contents include the expected files
|
// Check that the contents include the expected files
|
||||||
let package_contents = &contents[0];
|
let package_contents = &contents[0];
|
||||||
assert!(package_contents.files.is_some());
|
assert!(package_contents.files.is_some());
|
||||||
let files = package_contents.files.as_ref().unwrap();
|
let files = package_contents.files.as_ref().unwrap();
|
||||||
|
|
||||||
// Check for specific files
|
// Check for specific files
|
||||||
assert!(files.iter().any(|f| f.contains("usr/bin/hello")));
|
assert!(files.iter().any(|f| f.contains("usr/bin/hello")));
|
||||||
assert!(files.iter().any(|f| f.contains("usr/share/doc/example/README.txt")));
|
assert!(files
|
||||||
|
.iter()
|
||||||
|
.any(|f| f.contains("usr/share/doc/example/README.txt")));
|
||||||
assert!(files.iter().any(|f| f.contains("etc/config/example.conf")));
|
assert!(files.iter().any(|f| f.contains("etc/config/example.conf")));
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
@ -365,14 +385,14 @@ mod tests {
|
||||||
|
|
||||||
// Search for packages
|
// Search for packages
|
||||||
let results = repo.search("example", Some("test"), None).unwrap();
|
let results = repo.search("example", Some("test"), None).unwrap();
|
||||||
|
|
||||||
// Check that search results were returned
|
// Check that search results were returned
|
||||||
assert!(!results.is_empty());
|
assert!(!results.is_empty());
|
||||||
|
|
||||||
// Check that the package name is correct
|
// Check that the package name is correct
|
||||||
assert_eq!(results[0].fmri.name, "example");
|
assert_eq!(results[0].fmri.name, "example");
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
use libips::actions::{ActionError, File, Manifest};
|
use libips::actions::{ActionError, File, Manifest};
|
||||||
use libips::repository::{ReadableRepository, WritableRepository, FileBackend};
|
use libips::repository::{FileBackend, ReadableRepository, WritableRepository};
|
||||||
|
|
||||||
use anyhow::{Result, anyhow};
|
use anyhow::{anyhow, Result};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::fs::{read_dir, OpenOptions};
|
use std::fs::{read_dir, OpenOptions};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
@ -325,12 +325,18 @@ fn publish_package(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Check if the manifest file exists
|
// Check if the manifest file exists
|
||||||
if !manifest_path.exists() {
|
if !manifest_path.exists() {
|
||||||
return Err(anyhow!("Manifest file does not exist: {}", manifest_path.display()));
|
return Err(anyhow!(
|
||||||
|
"Manifest file does not exist: {}",
|
||||||
|
manifest_path.display()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the prototype directory exists
|
// Check if the prototype directory exists
|
||||||
if !prototype_dir.exists() {
|
if !prototype_dir.exists() {
|
||||||
return Err(anyhow!("Prototype directory does not exist: {}", prototype_dir.display()));
|
return Err(anyhow!(
|
||||||
|
"Prototype directory does not exist: {}",
|
||||||
|
prototype_dir.display()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the manifest file
|
// Parse the manifest file
|
||||||
|
|
@ -368,17 +374,23 @@ fn publish_package(
|
||||||
let mut transaction = repo.begin_transaction()?;
|
let mut transaction = repo.begin_transaction()?;
|
||||||
|
|
||||||
// Add files from the prototype directory to the transaction
|
// Add files from the prototype directory to the transaction
|
||||||
println!("Adding files from prototype directory: {}", prototype_dir.display());
|
println!(
|
||||||
|
"Adding files from prototype directory: {}",
|
||||||
|
prototype_dir.display()
|
||||||
|
);
|
||||||
for file_action in manifest.files.iter() {
|
for file_action in manifest.files.iter() {
|
||||||
// Construct the full path to the file in the prototype directory
|
// Construct the full path to the file in the prototype directory
|
||||||
let file_path = prototype_dir.join(&file_action.path);
|
let file_path = prototype_dir.join(&file_action.path);
|
||||||
|
|
||||||
// Check if the file exists
|
// Check if the file exists
|
||||||
if !file_path.exists() {
|
if !file_path.exists() {
|
||||||
println!("Warning: File does not exist in prototype directory: {}", file_path.display());
|
println!(
|
||||||
|
"Warning: File does not exist in prototype directory: {}",
|
||||||
|
file_path.display()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the file to the transaction
|
// Add the file to the transaction
|
||||||
println!("Adding file: {}", file_action.path);
|
println!("Adding file: {}", file_action.path);
|
||||||
transaction.add_file(file_action.clone(), &file_path)?;
|
transaction.add_file(file_action.clone(), &file_path)?;
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,11 @@ mod e2e_tests {
|
||||||
|
|
||||||
// Create a repository using pkg6repo
|
// Create a repository using pkg6repo
|
||||||
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
||||||
assert!(result.is_ok(), "Failed to create repository: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to create repository: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Check that the repository was created
|
// Check that the repository was created
|
||||||
assert!(repo_path.exists());
|
assert!(repo_path.exists());
|
||||||
|
|
@ -126,15 +130,19 @@ mod e2e_tests {
|
||||||
|
|
||||||
// Create a repository using pkg6repo
|
// Create a repository using pkg6repo
|
||||||
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
||||||
assert!(result.is_ok(), "Failed to create repository: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to create repository: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Add a publisher using pkg6repo
|
// Add a publisher using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["add-publisher", repo_path.to_str().unwrap(), "example.com"]);
|
||||||
"add-publisher",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
"example.com",
|
"Failed to add publisher: {:?}",
|
||||||
]);
|
result.err()
|
||||||
assert!(result.is_ok(), "Failed to add publisher: {:?}", result.err());
|
);
|
||||||
|
|
||||||
// Check that the publisher was added
|
// Check that the publisher was added
|
||||||
assert!(repo_path.join("catalog").join("example.com").exists());
|
assert!(repo_path.join("catalog").join("example.com").exists());
|
||||||
|
|
@ -155,15 +163,19 @@ mod e2e_tests {
|
||||||
|
|
||||||
// Create a repository using pkg6repo
|
// Create a repository using pkg6repo
|
||||||
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
||||||
assert!(result.is_ok(), "Failed to create repository: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to create repository: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Add a publisher using pkg6repo
|
// Add a publisher using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["add-publisher", repo_path.to_str().unwrap(), "test"]);
|
||||||
"add-publisher",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
"test",
|
"Failed to add publisher: {:?}",
|
||||||
]);
|
result.err()
|
||||||
assert!(result.is_ok(), "Failed to add publisher: {:?}", result.err());
|
);
|
||||||
|
|
||||||
// Publish a package using pkg6dev
|
// Publish a package using pkg6dev
|
||||||
let manifest_path = manifest_dir.join("example.p5m");
|
let manifest_path = manifest_dir.join("example.p5m");
|
||||||
|
|
@ -173,17 +185,25 @@ mod e2e_tests {
|
||||||
prototype_dir.to_str().unwrap(),
|
prototype_dir.to_str().unwrap(),
|
||||||
repo_path.to_str().unwrap(),
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(result.is_ok(), "Failed to publish package: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to publish package: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Check that the package was published
|
// Check that the package was published
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["list", repo_path.to_str().unwrap()]);
|
||||||
"list",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
]);
|
"Failed to list packages: {:?}",
|
||||||
assert!(result.is_ok(), "Failed to list packages: {:?}", result.err());
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
assert!(output.contains("example"), "Package not found in repository");
|
assert!(
|
||||||
|
output.contains("example"),
|
||||||
|
"Package not found in repository"
|
||||||
|
);
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
@ -200,15 +220,19 @@ mod e2e_tests {
|
||||||
|
|
||||||
// Create a repository using pkg6repo
|
// Create a repository using pkg6repo
|
||||||
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
||||||
assert!(result.is_ok(), "Failed to create repository: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to create repository: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Add a publisher using pkg6repo
|
// Add a publisher using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["add-publisher", repo_path.to_str().unwrap(), "test"]);
|
||||||
"add-publisher",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
"test",
|
"Failed to add publisher: {:?}",
|
||||||
]);
|
result.err()
|
||||||
assert!(result.is_ok(), "Failed to add publisher: {:?}", result.err());
|
);
|
||||||
|
|
||||||
// Publish a package using pkg6dev
|
// Publish a package using pkg6dev
|
||||||
let manifest_path = manifest_dir.join("example.p5m");
|
let manifest_path = manifest_dir.join("example.p5m");
|
||||||
|
|
@ -218,20 +242,33 @@ mod e2e_tests {
|
||||||
prototype_dir.to_str().unwrap(),
|
prototype_dir.to_str().unwrap(),
|
||||||
repo_path.to_str().unwrap(),
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(result.is_ok(), "Failed to publish package: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to publish package: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Show package contents using pkg6repo
|
// Show package contents using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["contents", repo_path.to_str().unwrap(), "example"]);
|
||||||
"contents",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
"example",
|
"Failed to show package contents: {:?}",
|
||||||
]);
|
result.err()
|
||||||
assert!(result.is_ok(), "Failed to show package contents: {:?}", result.err());
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
assert!(output.contains("usr/bin/hello"), "File not found in package contents");
|
assert!(
|
||||||
assert!(output.contains("usr/share/doc/example/README.txt"), "File not found in package contents");
|
output.contains("usr/bin/hello"),
|
||||||
assert!(output.contains("etc/config/example.conf"), "File not found in package contents");
|
"File not found in package contents"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
output.contains("usr/share/doc/example/README.txt"),
|
||||||
|
"File not found in package contents"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
output.contains("etc/config/example.conf"),
|
||||||
|
"File not found in package contents"
|
||||||
|
);
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
|
|
@ -248,15 +285,19 @@ mod e2e_tests {
|
||||||
|
|
||||||
// Create a repository using pkg6repo
|
// Create a repository using pkg6repo
|
||||||
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
let result = run_pkg6repo(&["create", "--repo-version", "4", repo_path.to_str().unwrap()]);
|
||||||
assert!(result.is_ok(), "Failed to create repository: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to create repository: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Add a publisher using pkg6repo
|
// Add a publisher using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["add-publisher", repo_path.to_str().unwrap(), "test"]);
|
||||||
"add-publisher",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
"test",
|
"Failed to add publisher: {:?}",
|
||||||
]);
|
result.err()
|
||||||
assert!(result.is_ok(), "Failed to add publisher: {:?}", result.err());
|
);
|
||||||
|
|
||||||
// Publish the first package using pkg6dev
|
// Publish the first package using pkg6dev
|
||||||
let manifest_path1 = manifest_dir.join("example.p5m");
|
let manifest_path1 = manifest_dir.join("example.p5m");
|
||||||
|
|
@ -266,7 +307,11 @@ mod e2e_tests {
|
||||||
prototype_dir.to_str().unwrap(),
|
prototype_dir.to_str().unwrap(),
|
||||||
repo_path.to_str().unwrap(),
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(result.is_ok(), "Failed to publish first package: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to publish first package: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// Publish the second package using pkg6dev
|
// Publish the second package using pkg6dev
|
||||||
let manifest_path2 = manifest_dir.join("example2.p5m");
|
let manifest_path2 = manifest_dir.join("example2.p5m");
|
||||||
|
|
@ -276,20 +321,31 @@ mod e2e_tests {
|
||||||
prototype_dir.to_str().unwrap(),
|
prototype_dir.to_str().unwrap(),
|
||||||
repo_path.to_str().unwrap(),
|
repo_path.to_str().unwrap(),
|
||||||
]);
|
]);
|
||||||
assert!(result.is_ok(), "Failed to publish second package: {:?}", result.err());
|
assert!(
|
||||||
|
result.is_ok(),
|
||||||
|
"Failed to publish second package: {:?}",
|
||||||
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
// List packages using pkg6repo
|
// List packages using pkg6repo
|
||||||
let result = run_pkg6repo(&[
|
let result = run_pkg6repo(&["list", repo_path.to_str().unwrap()]);
|
||||||
"list",
|
assert!(
|
||||||
repo_path.to_str().unwrap(),
|
result.is_ok(),
|
||||||
]);
|
"Failed to list packages: {:?}",
|
||||||
assert!(result.is_ok(), "Failed to list packages: {:?}", result.err());
|
result.err()
|
||||||
|
);
|
||||||
|
|
||||||
let output = result.unwrap();
|
let output = result.unwrap();
|
||||||
assert!(output.contains("example"), "First package not found in repository");
|
assert!(
|
||||||
assert!(output.contains("example2"), "Second package not found in repository");
|
output.contains("example"),
|
||||||
|
"First package not found in repository"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
output.contains("example2"),
|
||||||
|
"Second package not found in repository"
|
||||||
|
);
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,28 +1,31 @@
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use libips::repository::{ReadableRepository, WritableRepository, RepositoryVersion, FileBackend, REPOSITORY_CONFIG_FILENAME, PublisherInfo, RepositoryInfo};
|
use libips::repository::{
|
||||||
use std::path::PathBuf;
|
FileBackend, ReadableRepository, RepositoryVersion,
|
||||||
|
WritableRepository, REPOSITORY_CONFIG_FILENAME,
|
||||||
|
};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// These tests interact with real repositories in a known location
|
// These tests interact with real repositories in a known location
|
||||||
// instead of using temporary directories. This allows for better
|
// instead of using temporary directories. This allows for better
|
||||||
// debugging and inspection of the repositories during testing.
|
// debugging and inspection of the repositories during testing.
|
||||||
|
|
||||||
// The base directory for all test repositories
|
// The base directory for all test repositories
|
||||||
const TEST_REPO_BASE_DIR: &str = "/tmp/pkg6repo_test";
|
const TEST_REPO_BASE_DIR: &str = "/tmp/pkg6repo_test";
|
||||||
|
|
||||||
// Helper function to create a unique test directory
|
// Helper function to create a unique test directory
|
||||||
fn create_test_dir(test_name: &str) -> PathBuf {
|
fn create_test_dir(test_name: &str) -> PathBuf {
|
||||||
let test_dir = PathBuf::from(format!("{}/{}", TEST_REPO_BASE_DIR, test_name));
|
let test_dir = PathBuf::from(format!("{}/{}", TEST_REPO_BASE_DIR, test_name));
|
||||||
|
|
||||||
// Clean up any existing directory
|
// Clean up any existing directory
|
||||||
if test_dir.exists() {
|
if test_dir.exists() {
|
||||||
fs::remove_dir_all(&test_dir).unwrap();
|
fs::remove_dir_all(&test_dir).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the directory
|
// Create the directory
|
||||||
fs::create_dir_all(&test_dir).unwrap();
|
fs::create_dir_all(&test_dir).unwrap();
|
||||||
|
|
||||||
test_dir
|
test_dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,10 +41,10 @@ mod tests {
|
||||||
// Create a real test directory
|
// Create a real test directory
|
||||||
let test_dir = create_test_dir("create_repository");
|
let test_dir = create_test_dir("create_repository");
|
||||||
let repo_path = test_dir.join("repo");
|
let repo_path = test_dir.join("repo");
|
||||||
|
|
||||||
// Create a repository
|
// Create a repository
|
||||||
let _ = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let _ = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
// Check that the repository was created
|
// Check that the repository was created
|
||||||
assert!(repo_path.exists());
|
assert!(repo_path.exists());
|
||||||
assert!(repo_path.join("catalog").exists());
|
assert!(repo_path.join("catalog").exists());
|
||||||
|
|
@ -50,109 +53,125 @@ mod tests {
|
||||||
assert!(repo_path.join("pkg").exists());
|
assert!(repo_path.join("pkg").exists());
|
||||||
assert!(repo_path.join("trans").exists());
|
assert!(repo_path.join("trans").exists());
|
||||||
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
assert!(repo_path.join(REPOSITORY_CONFIG_FILENAME).exists());
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_add_publisher() {
|
fn test_add_publisher() {
|
||||||
// Create a real test directory
|
// Create a real test directory
|
||||||
let test_dir = create_test_dir("add_publisher");
|
let test_dir = create_test_dir("add_publisher");
|
||||||
let repo_path = test_dir.join("repo");
|
let repo_path = test_dir.join("repo");
|
||||||
|
|
||||||
// Create a repository
|
// Create a repository
|
||||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
// Add a publisher
|
// Add a publisher
|
||||||
repo.add_publisher("example.com").unwrap();
|
repo.add_publisher("example.com").unwrap();
|
||||||
|
|
||||||
// Check that the publisher was added
|
// Check that the publisher was added
|
||||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||||
assert!(repo_path.join("catalog").join("example.com").exists());
|
assert!(repo_path.join("catalog").join("example.com").exists());
|
||||||
assert!(repo_path.join("pkg").join("example.com").exists());
|
assert!(repo_path.join("pkg").join("example.com").exists());
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_remove_publisher() {
|
fn test_remove_publisher() {
|
||||||
// Create a real test directory
|
// Create a real test directory
|
||||||
let test_dir = create_test_dir("remove_publisher");
|
let test_dir = create_test_dir("remove_publisher");
|
||||||
let repo_path = test_dir.join("repo");
|
let repo_path = test_dir.join("repo");
|
||||||
|
|
||||||
// Create a repository
|
// Create a repository
|
||||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
// Add a publisher
|
// Add a publisher
|
||||||
repo.add_publisher("example.com").unwrap();
|
repo.add_publisher("example.com").unwrap();
|
||||||
|
|
||||||
// Check that the publisher was added
|
// Check that the publisher was added
|
||||||
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
assert!(repo.config.publishers.contains(&"example.com".to_string()));
|
||||||
|
|
||||||
// Check that the publisher directories were created
|
// Check that the publisher directories were created
|
||||||
let catalog_dir = repo_path.join("catalog").join("example.com");
|
let catalog_dir = repo_path.join("catalog").join("example.com");
|
||||||
let pkg_dir = repo_path.join("pkg").join("example.com");
|
let pkg_dir = repo_path.join("pkg").join("example.com");
|
||||||
assert!(catalog_dir.exists(), "Catalog directory should exist after adding publisher");
|
assert!(
|
||||||
assert!(pkg_dir.exists(), "Package directory should exist after adding publisher");
|
catalog_dir.exists(),
|
||||||
|
"Catalog directory should exist after adding publisher"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
pkg_dir.exists(),
|
||||||
|
"Package directory should exist after adding publisher"
|
||||||
|
);
|
||||||
|
|
||||||
// Remove the publisher
|
// Remove the publisher
|
||||||
repo.remove_publisher("example.com", false).unwrap();
|
repo.remove_publisher("example.com", false).unwrap();
|
||||||
|
|
||||||
// Check that the publisher was removed from the configuration
|
// Check that the publisher was removed from the configuration
|
||||||
assert!(!repo.config.publishers.contains(&"example.com".to_string()));
|
assert!(!repo.config.publishers.contains(&"example.com".to_string()));
|
||||||
|
|
||||||
// Check that the publisher directories were removed
|
// Check that the publisher directories were removed
|
||||||
assert!(!catalog_dir.exists(), "Catalog directory should not exist after removing publisher");
|
assert!(
|
||||||
assert!(!pkg_dir.exists(), "Package directory should not exist after removing publisher");
|
!catalog_dir.exists(),
|
||||||
|
"Catalog directory should not exist after removing publisher"
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!pkg_dir.exists(),
|
||||||
|
"Package directory should not exist after removing publisher"
|
||||||
|
);
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_set_property() {
|
fn test_set_property() {
|
||||||
// Create a real test directory
|
// Create a real test directory
|
||||||
let test_dir = create_test_dir("set_property");
|
let test_dir = create_test_dir("set_property");
|
||||||
let repo_path = test_dir.join("repo");
|
let repo_path = test_dir.join("repo");
|
||||||
|
|
||||||
// Create a repository
|
// Create a repository
|
||||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
// Set a property
|
// Set a property
|
||||||
repo.set_property("publisher/prefix", "example.com").unwrap();
|
repo.set_property("publisher/prefix", "example.com")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// Check that the property was set
|
// Check that the property was set
|
||||||
assert_eq!(repo.config.properties.get("publisher/prefix").unwrap(), "example.com");
|
assert_eq!(
|
||||||
|
repo.config.properties.get("publisher/prefix").unwrap(),
|
||||||
|
"example.com"
|
||||||
|
);
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_info() {
|
fn test_get_info() {
|
||||||
// Create a real test directory
|
// Create a real test directory
|
||||||
let test_dir = create_test_dir("get_info");
|
let test_dir = create_test_dir("get_info");
|
||||||
let repo_path = test_dir.join("repo");
|
let repo_path = test_dir.join("repo");
|
||||||
|
|
||||||
// Create a repository
|
// Create a repository
|
||||||
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
let mut repo = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
||||||
|
|
||||||
// Add a publisher
|
// Add a publisher
|
||||||
repo.add_publisher("example.com").unwrap();
|
repo.add_publisher("example.com").unwrap();
|
||||||
|
|
||||||
// Get repository information
|
// Get repository information
|
||||||
let repo_info = repo.get_info().unwrap();
|
let repo_info = repo.get_info().unwrap();
|
||||||
|
|
||||||
// Check that the information is correct
|
// Check that the information is correct
|
||||||
assert_eq!(repo_info.publishers.len(), 1);
|
assert_eq!(repo_info.publishers.len(), 1);
|
||||||
let publisher_info = &repo_info.publishers[0];
|
let publisher_info = &repo_info.publishers[0];
|
||||||
assert_eq!(publisher_info.name, "example.com");
|
assert_eq!(publisher_info.name, "example.com");
|
||||||
assert_eq!(publisher_info.package_count, 0); // No packages yet
|
assert_eq!(publisher_info.package_count, 0); // No packages yet
|
||||||
assert_eq!(publisher_info.status, "online");
|
assert_eq!(publisher_info.status, "online");
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
cleanup_test_dir(&test_dir);
|
cleanup_test_dir(&test_dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,13 @@
|
||||||
|
use anyhow::Result;
|
||||||
use pest::Parser;
|
use pest::Parser;
|
||||||
use pest_derive::Parser;
|
use pest_derive::Parser;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum MacroParserError {
|
pub enum MacroParserError {
|
||||||
#[error("macro does not exist: {macro_name}")]
|
#[error("macro does not exist: {macro_name}")]
|
||||||
DoesNotExist {
|
DoesNotExist { macro_name: String },
|
||||||
macro_name: String,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|
@ -18,17 +16,17 @@ struct InternalMacroParser;
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct MacroParser {
|
pub struct MacroParser {
|
||||||
pub macros: HashMap<String, String>
|
pub macros: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct Macro {
|
pub struct Macro {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub parameters: Vec<String>
|
pub parameters: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacroParser {
|
impl MacroParser {
|
||||||
pub fn parse(&self ,raw_string: String) -> Result<String> {
|
pub fn parse(&self, raw_string: String) -> Result<String> {
|
||||||
let mut return_string = String::new();
|
let mut return_string = String::new();
|
||||||
|
|
||||||
for (i, line) in raw_string.lines().enumerate() {
|
for (i, line) in raw_string.lines().enumerate() {
|
||||||
|
|
@ -91,9 +89,10 @@ impl MacroParser {
|
||||||
|
|
||||||
fn get_variable(&self, macro_name: &str) -> Result<&str> {
|
fn get_variable(&self, macro_name: &str) -> Result<&str> {
|
||||||
if self.macros.contains_key(macro_name) {
|
if self.macros.contains_key(macro_name) {
|
||||||
return Ok(self.macros[macro_name].as_str())
|
return Ok(self.macros[macro_name].as_str());
|
||||||
}
|
}
|
||||||
Err(MacroParserError::DoesNotExist {macro_name: macro_name.into()})?
|
Err(MacroParserError::DoesNotExist {
|
||||||
|
macro_name: macro_name.into(),
|
||||||
|
})?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue