Skip to main content

alpm_types/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod checksum;
4pub use checksum::{
5    Blake2b512Checksum,
6    Checksum,
7    ChecksumAlgorithm,
8    Crc32CksumChecksum,
9    DigestString as Digest,
10    Md5Checksum,
11    Sha1Checksum,
12    Sha224Checksum,
13    Sha256Checksum,
14    Sha384Checksum,
15    Sha512Checksum,
16    SkippableChecksum,
17};
18
19mod source;
20pub use source::Source;
21
22pub mod url;
23pub use url::{SourceUrl, Url};
24
25/// Public re-exports of common hash functions, for use with [`Checksum`].
26pub mod digests {
27    pub use blake2::Blake2b512;
28    pub use md5::Md5;
29    pub use sha1::Sha1;
30    pub use sha2::{Sha224, Sha256, Sha384, Sha512};
31
32    pub use crate::checksum::{Crc32Cksum, DigestEncoding, DigestString as Digest};
33}
34
35mod compression;
36pub use compression::CompressionAlgorithmFileExtension;
37
38mod date;
39pub use date::{BuildDate, FromOffsetDateTime};
40
41mod env;
42pub use env::{BuildEnvironmentOption, InstalledPackage, MakepkgOption, PackageOption};
43
44mod file_type;
45pub use file_type::FileTypeIdentifier;
46
47mod error;
48pub use error::Error;
49
50mod license;
51pub use license::License;
52
53mod name;
54pub use name::{BuildTool, Name, SharedObjectName};
55
56mod package;
57pub use package::{
58    contents::{INSTALL_SCRIPTLET_FILE_NAME, MetadataFileName},
59    error::Error as PackageError,
60    file_name::PackageFileName,
61    installation::PackageInstallReason,
62    source::{PKGBUILD_FILE_NAME, SRCINFO_FILE_NAME},
63    validation::PackageValidation,
64};
65
66mod path;
67pub use path::{
68    AbsolutePath,
69    Backup,
70    BuildDirectory,
71    Changelog,
72    Install,
73    RelativeFilePath,
74    RelativePath,
75    SonameLookupDirectory,
76    StartDirectory,
77};
78
79mod openpgp;
80pub use openpgp::{
81    Base64OpenPGPSignature,
82    OpenPGPIdentifier,
83    OpenPGPKeyId,
84    OpenPGPv4Fingerprint,
85    Packager,
86};
87
88mod pkg;
89pub use pkg::{ExtraData, ExtraDataEntry, PackageBaseName, PackageDescription, PackageType};
90
91mod relation;
92pub use relation::{
93    Group,
94    OptionalDependency,
95    PackageRelation,
96    RelationOrSoname,
97    SharedLibraryPrefix,
98    Soname,
99    SonameV1,
100    SonameV2,
101    VersionOrSoname,
102};
103
104mod size;
105pub use size::{CompressedSize, InstalledSize};
106
107mod system;
108pub use system::{
109    Architecture,
110    Architectures,
111    ElfArchitectureFormat,
112    SystemArchitecture,
113    UnknownArchitecture,
114};
115
116mod version;
117pub use version::{
118    base::{Epoch, PackageRelease, PackageVersion},
119    buildtool::BuildToolVersion,
120    comparison::{VersionSegment, VersionSegments},
121    pkg_full::FullVersion,
122    pkg_generic::Version,
123    pkg_minimal::MinimalVersion,
124    requirement::{VersionComparison, VersionRequirement},
125    schema::SchemaVersion,
126};
127
128/// Public re-exports for use with [`SchemaVersion`].
129pub mod semver_version {
130    pub use semver::Version;
131}
132
133fluent_i18n::i18n!("locales");
134
135/// This is a helper macro that is used by unit tests in the `alpm-types` crate.
136///
137/// Specifically, it takes care of two things:
138///
139/// 1. Make the test filename **somewhat** human readable. cargo-insta uses the full module name by
140///    default, which is absurdly long due to our usage of rstest. Since the snapshots are placed in
141///    the immediate module anyway, we only need one level of module indirection. The tests
142///    filenames have the format of: `{module}::{test_function}@{test_case_name}`
143/// 2. Remove the `expression` field from the snapshot, as it's of no use in parser tests.
144///
145/// The function returns the test name to use in `assert_snapshot`, as well as a settings guard,
146/// which assures that the settings we just adjusted are local to this thread and stay up until the
147/// guard goes out of scope.
148///
149/// # Example
150///
151/// ```rs,norun
152/// #[rstest]
153/// #[case::something_bad("oh no")]
154/// #[case::something_else_bad("oh nooo")]
155/// fn invalid_version_requirement(#[case] requirement: &str) {
156///     let Err(Error::ParseError(err_msg)) = VersionRequirement::from_str(requirement) else {
157///         panic!("'{requirement}' erroneously parsed as VersionRequirement")
158///     };
159///
160///     let (test_name, _guard) = configure_insta();
161///     assert_snapshot!(test_name, err_msg.to_string());
162/// }
163#[cfg(test)]
164// We ignore `expect_fun_call`, as this is test code and more this makes it significantly
165// more convenient/easier to read.
166#[allow(clippy::expect_fun_call)]
167fn configure_insta() -> (String, insta::internals::SettingsBindDropGuard) {
168    // Get the full thread name, which is pretty much a rust module string
169    // e.g. `version::base::tests::invalid_pkgver::case_4`
170    let thread_name = std::thread::current()
171        .name()
172        .expect("Couldn't determine test thread name!!")
173        .to_string();
174
175    let (mut rest, mut end) = thread_name.rsplit_once("::").expect(&format!(
176        "Test thread name does not have an expected first level: {thread_name}"
177    ));
178
179    // If we're inside an rstest test case, the last section of the test will be something along the
180    // line of `case_4` followed by an optional test case name.
181    //
182    // Otherwise, it's the name of the test function.
183    let mut test_case_name: Option<String> = None;
184    let function_name = if end.contains("case") {
185        test_case_name = Some(end.to_string());
186
187        // The next part will then be the actual test name
188        (rest, end) = rest.rsplit_once("::").expect(&format!(
189            "Test thread name does not have an expected second level: {thread_name}"
190        ));
191
192        end.to_string()
193    } else {
194        end.to_string()
195    };
196
197    // Now get the module name which contains the test function.
198    let module_name = loop {
199        (rest, end) = match rest.rsplit_once("::") {
200            Some(split) => split,
201            // Handle the case that we reached the topmost module.
202            None => break rest.to_string(),
203        };
204
205        // Ignore any *test* modules, as those are not interesting for us.
206        if !end.contains("test") {
207            break end.to_string();
208        };
209    };
210
211    let mut settings = insta::Settings::clone_current();
212    settings.set_prepend_module_to_snapshot(false);
213    // If we're inside a testcase, set the case name as a suffix.
214    if let Some(test_case_name) = test_case_name {
215        settings.set_snapshot_suffix(test_case_name.to_string());
216    }
217
218    // Since we're test parsers, the expression is always generic and only clutters the output.
219    // The expression that's needed for context is always fully visible in the actual error message.
220    settings.set_omit_expression(true);
221    let guard = settings.bind_to_scope();
222
223    // Return the cargo insta test name for usage in the `assert_snapshot` function.
224    (format!("{module_name}::{function_name}"), guard)
225}