mirror of
https://codeberg.org/Toasterson/ips.git
synced 2026-04-10 21:30:41 +00:00
Expand ManifestBuilder with new helper methods for setting attributes, licenses, links, and dependencies.
- Added `add_set`, `add_license`, `add_link`, and `add_depend` methods for streamlined manifest construction. - Updated documentation with new examples showcasing the extended `ManifestBuilder` API. - Enhanced test coverage to validate the new helper methods.
This commit is contained in:
parent
d78cd9f659
commit
23815a2aab
1 changed files with 110 additions and 6 deletions
|
|
@ -58,9 +58,9 @@ use walkdir::WalkDir;
|
||||||
|
|
||||||
pub use crate::actions::Manifest;
|
pub use crate::actions::Manifest;
|
||||||
// Core typed manifest
|
// Core typed manifest
|
||||||
use crate::actions::{Attr, File as FileAction};
|
use crate::actions::{Attr, Dependency as DependAction, File as FileAction, License as LicenseAction, Link as LinkAction, Property};
|
||||||
pub use crate::fmri::Fmri;
|
|
||||||
pub use crate::depend::{FileDep, GenerateOptions as DependGenerateOptions};
|
pub use crate::depend::{FileDep, GenerateOptions as DependGenerateOptions};
|
||||||
|
pub use crate::fmri::Fmri;
|
||||||
// For BaseMeta
|
// For BaseMeta
|
||||||
use crate::repository::file_backend::{FileBackend, Transaction};
|
use crate::repository::file_backend::{FileBackend, Transaction};
|
||||||
use crate::repository::{ReadableRepository, RepositoryError, RepositoryVersion, WritableRepository};
|
use crate::repository::{ReadableRepository, RepositoryError, RepositoryVersion, WritableRepository};
|
||||||
|
|
@ -123,6 +123,31 @@ pub struct BaseMeta {
|
||||||
/// Example (no_run):
|
/// Example (no_run):
|
||||||
/// ```no_run
|
/// ```no_run
|
||||||
/// use libips::api as ips;
|
/// use libips::api as ips;
|
||||||
|
/// let mut builder = ips::ManifestBuilder::new();
|
||||||
|
/// let fmri = ips::Fmri::parse("pkg://pub/name@1.0").unwrap();
|
||||||
|
/// let summary = String::from("A summary");
|
||||||
|
/// let classification = "Applications/Other";
|
||||||
|
/// let project_url = String::from("https://example.com");
|
||||||
|
/// let source_url = String::from("https://example.com/src.tar.gz");
|
||||||
|
/// let license_file_name = "license.txt";
|
||||||
|
/// let license_name = "MIT";
|
||||||
|
/// builder.add_set("pkg.fmri", &fmri.to_string());
|
||||||
|
/// builder.add_set("pkg.summary", &summary);
|
||||||
|
/// builder.add_set(
|
||||||
|
/// "info.classification",
|
||||||
|
/// &format!("org.opensolaris.category.2008:{}", classification),
|
||||||
|
/// );
|
||||||
|
/// builder.add_set("info.upstream-url", &project_url);
|
||||||
|
/// builder.add_set("info.source-url", &source_url);
|
||||||
|
/// builder.add_license(&license_file_name, &license_name);
|
||||||
|
/// let manifest = builder.build();
|
||||||
|
/// # Ok::<(), ips::IpsError>(())
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Another style using with_base_metadata:
|
||||||
|
/// Example (no_run):
|
||||||
|
/// ```no_run
|
||||||
|
/// use libips::api as ips;
|
||||||
/// use std::path::Path;
|
/// use std::path::Path;
|
||||||
/// let proto = Path::new("/proto");
|
/// let proto = Path::new("/proto");
|
||||||
/// let mut manifest = ips::ManifestBuilder::new()
|
/// let mut manifest = ips::ManifestBuilder::new()
|
||||||
|
|
@ -142,6 +167,48 @@ pub struct ManifestBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ManifestBuilder {
|
impl ManifestBuilder {
|
||||||
|
/// Add a simple set (attribute) action: set name=<key> value=<value>
|
||||||
|
/// Returns self for chaining.
|
||||||
|
pub fn add_set<K: Into<String>, V: ToString>(&mut self, key: K, value: V) -> &mut Self {
|
||||||
|
self.manifest.attributes.push(Attr {
|
||||||
|
key: key.into(),
|
||||||
|
values: vec![value.to_string()],
|
||||||
|
properties: Default::default(),
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a license action, equivalent to: license path=<path> license=<license_name>
|
||||||
|
pub fn add_license(&mut self, path: &str, license_name: &str) -> &mut Self {
|
||||||
|
let mut props = std::collections::HashMap::new();
|
||||||
|
props.insert(
|
||||||
|
"path".to_string(),
|
||||||
|
Property { key: "path".to_string(), value: path.to_string() },
|
||||||
|
);
|
||||||
|
props.insert(
|
||||||
|
"license".to_string(),
|
||||||
|
Property { key: "license".to_string(), value: license_name.to_string() },
|
||||||
|
);
|
||||||
|
self.manifest.licenses.push(LicenseAction { payload: String::new(), properties: props });
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a link action
|
||||||
|
pub fn add_link(&mut self, path: &str, target: &str) -> &mut Self {
|
||||||
|
self.manifest.links.push(LinkAction { path: path.to_string(), target: target.to_string(), properties: Default::default() });
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a dependency action with a type and an FMRI string (name or full FMRI).
|
||||||
|
/// If FMRI parsing fails, the dependency is added without an fmri (will be flagged by lint).
|
||||||
|
pub fn add_depend(&mut self, dep_type: &str, fmri_str: &str) -> &mut Self {
|
||||||
|
let fmri = Fmri::parse(fmri_str).ok();
|
||||||
|
let mut d = DependAction::default();
|
||||||
|
d.dependency_type = dep_type.to_string();
|
||||||
|
d.fmri = fmri;
|
||||||
|
self.manifest.dependencies.push(d);
|
||||||
|
self
|
||||||
|
}
|
||||||
/// Start a new empty builder
|
/// Start a new empty builder
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { manifest: Manifest::new() }
|
Self { manifest: Manifest::new() }
|
||||||
|
|
@ -560,7 +627,6 @@ pub struct LintConfig {
|
||||||
pub mod lint {
|
pub mod lint {
|
||||||
use super::*;
|
use super::*;
|
||||||
use miette::Diagnostic;
|
use miette::Diagnostic;
|
||||||
use std::collections::HashSet;
|
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
@ -619,7 +685,7 @@ pub mod lint {
|
||||||
if fmri_attr_count > 1 { diags.push(miette::Report::new(LintIssue::DuplicateFmri)); }
|
if fmri_attr_count > 1 { diags.push(miette::Report::new(LintIssue::DuplicateFmri)); }
|
||||||
match (fmri_attr_count, fmri_text) {
|
match (fmri_attr_count, fmri_text) {
|
||||||
(0, _) => diags.push(miette::Report::new(LintIssue::MissingOrInvalidFmri)),
|
(0, _) => diags.push(miette::Report::new(LintIssue::MissingOrInvalidFmri)),
|
||||||
(_, Some(txt)) => { if Fmri::parse(&txt).is_err() { diags.push(miette::Report::new(LintIssue::MissingOrInvalidFmri)); } },
|
(_, Some(txt)) => { if crate::fmri::Fmri::parse(&txt).is_err() { diags.push(miette::Report::new(LintIssue::MissingOrInvalidFmri)); } },
|
||||||
(_, None) => diags.push(miette::Report::new(LintIssue::MissingOrInvalidFmri)),
|
(_, None) => diags.push(miette::Report::new(LintIssue::MissingOrInvalidFmri)),
|
||||||
}
|
}
|
||||||
diags
|
diags
|
||||||
|
|
@ -666,10 +732,10 @@ pub mod lint {
|
||||||
|
|
||||||
fn rule_enabled(rule_id: &str, cfg: &LintConfig) -> bool {
|
fn rule_enabled(rule_id: &str, cfg: &LintConfig) -> bool {
|
||||||
if let Some(only) = &cfg.enabled_only {
|
if let Some(only) = &cfg.enabled_only {
|
||||||
let set: HashSet<&str> = only.iter().map(|s| s.as_str()).collect();
|
let set: std::collections::HashSet<&str> = only.iter().map(|s| s.as_str()).collect();
|
||||||
return set.contains(rule_id);
|
return set.contains(rule_id);
|
||||||
}
|
}
|
||||||
let disabled: HashSet<&str> = cfg.disabled_rules.iter().map(|s| s.as_str()).collect();
|
let disabled: std::collections::HashSet<&str> = cfg.disabled_rules.iter().map(|s| s.as_str()).collect();
|
||||||
!disabled.contains(rule_id)
|
!disabled.contains(rule_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -808,4 +874,42 @@ mod tests {
|
||||||
// fmri is valid, dependencies empty, summary rule disabled => no diags
|
// fmri is valid, dependencies empty, summary rule disabled => no diags
|
||||||
assert!(diags.is_empty(), "expected no diagnostics when summary rule disabled, got: {:?}", diags);
|
assert!(diags.is_empty(), "expected no diagnostics when summary rule disabled, got: {:?}", diags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn builder_add_set_license_link_depend() {
|
||||||
|
// add_set with Fmri and strings
|
||||||
|
let fmri = Fmri::parse("pkg://pub/example@1.0").unwrap();
|
||||||
|
let mut b = ManifestBuilder::new();
|
||||||
|
b.add_set("pkg.fmri", &fmri);
|
||||||
|
b.add_set("pkg.summary", "Summary");
|
||||||
|
b.add_set("info.upstream-url", "https://example.com");
|
||||||
|
b.add_license("LICENSE", "MIT");
|
||||||
|
b.add_link("usr/bin/foo", "../libexec/foo");
|
||||||
|
b.add_depend("require", "pkg://pub/dep@1.2");
|
||||||
|
let m = b.build();
|
||||||
|
|
||||||
|
// Validate attributes include fmri and summary
|
||||||
|
assert!(m.attributes.iter().any(|a| a.key == "pkg.fmri" && a.values.get(0).map(|v| v == &fmri.to_string()).unwrap_or(false)));
|
||||||
|
assert!(m.attributes.iter().any(|a| a.key == "pkg.summary" && a.values.get(0).map(|v| v == "Summary").unwrap_or(false)));
|
||||||
|
|
||||||
|
// Validate license
|
||||||
|
assert_eq!(m.licenses.len(), 1);
|
||||||
|
let lic = &m.licenses[0];
|
||||||
|
assert_eq!(lic.properties.get("path").map(|p| p.value.as_str()), Some("LICENSE"));
|
||||||
|
assert_eq!(lic.properties.get("license").map(|p| p.value.as_str()), Some("MIT"));
|
||||||
|
|
||||||
|
// Validate link
|
||||||
|
assert_eq!(m.links.len(), 1);
|
||||||
|
let ln = &m.links[0];
|
||||||
|
assert_eq!(ln.path, "usr/bin/foo");
|
||||||
|
assert_eq!(ln.target, "../libexec/foo");
|
||||||
|
|
||||||
|
// Validate dependency
|
||||||
|
assert_eq!(m.dependencies.len(), 1);
|
||||||
|
let dep = &m.dependencies[0];
|
||||||
|
assert_eq!(dep.dependency_type, "require");
|
||||||
|
let df = dep.fmri.as_ref().expect("dep fmri parsed");
|
||||||
|
assert_eq!(df.publisher.as_deref(), Some("pub"));
|
||||||
|
assert_eq!(df.version.as_ref().unwrap().to_string(), "1.2");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue