Skip to main content

alpm_types/
lib.rs

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