Skip to main content

compress_tools/
error.rs

1// Copyright (C) 2019-2021 O.S. Systems Software LTDA
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5use crate::ffi;
6use derive_more::{Display, Error, From};
7use std::{borrow::Cow, ffi::CStr, io};
8
9pub type Result<T> = std::result::Result<T, Error>;
10
11#[derive(Display, From, Error, Debug)]
12#[non_exhaustive]
13pub enum Error {
14    #[display(
15        "Extraction error:{}{} '{}'",
16        match code {
17            Some(_) => " ",
18            None => ""
19        },
20        if let Some(e) = code {
21            e as &dyn std::fmt::Display
22        } else {
23            &"" as &_
24        },
25        details
26    )]
27    Extraction {
28        /// The code stemming from `archive_errno`, unless it is not a valid
29        /// value for `errno(3)`, like `ARCHIVE_ERRNO_MISC`
30        #[error(source)]
31        code: Option<io::Error>,
32        /// The string returned by `archive_error_string`
33        details: String,
34    },
35
36    Io(io::Error),
37
38    Utf(std::str::Utf8Error),
39
40    #[display("Encoding error: '{}'", _0)]
41    Encoding(#[error(not(source))] Cow<'static, str>),
42
43    #[cfg(feature = "tokio_support")]
44    JoinError(tokio::task::JoinError),
45
46    #[display("Error to create the archive struct, is null")]
47    NullArchive,
48
49    #[display(
50        "Unsupported ZIP compression method in {} {}: {:?}",
51        _0.len(),
52        if _0.len() == 1 { "entry" } else { "entries" },
53        _0
54    )]
55    UnsupportedZipCompression(#[error(not(source))] Vec<(String, u16)>),
56
57    #[display("Unknown error")]
58    Unknown,
59}
60
61pub(crate) fn archive_result(value: i32, archive: *mut ffi::archive) -> Result<()> {
62    match value {
63        ffi::ARCHIVE_OK | ffi::ARCHIVE_WARN => Ok(()),
64        _ => Err(Error::from(archive)),
65    }
66}
67
68/// Like [`archive_result`], but treats `ARCHIVE_WARN` as an error.
69///
70/// Use this on call sites where a warning indicates user-visible data loss —
71/// for example, `archive_write_header` and `archive_write_data_block`, which
72/// can return `ARCHIVE_WARN` when the target filesystem returns `ENOSPC`. See
73/// https://github.com/OSSystems/compress-tools-rs/issues/142.
74pub(crate) fn archive_result_strict(value: i32, archive: *mut ffi::archive) -> Result<()> {
75    match value {
76        ffi::ARCHIVE_OK => Ok(()),
77        _ => Err(Error::from(archive)),
78    }
79}
80
81#[allow(clippy::not_unsafe_ptr_arg_deref)]
82impl From<*mut ffi::archive> for Error {
83    fn from(input: *mut ffi::archive) -> Self {
84        let (details, code) = unsafe {
85            let error_string = ffi::archive_error_string(input);
86            let details = if !error_string.is_null() {
87                Some(CStr::from_ptr(error_string).to_string_lossy().into_owned())
88            } else {
89                None
90            };
91
92            let errno = ffi::archive_errno(input);
93            let code = if errno > 0 {
94                Some(io::Error::from_raw_os_error(errno))
95            } else {
96                // 0 (unexpected) or ARCHIVE_ERRNO_MISC which is not a valid value of errno(3)
97                None
98            };
99            (details, code)
100        };
101        match (details, code) {
102            (Some(details), code) => Error::Extraction { code, details },
103            (None, Some(code)) => Error::Io(code),
104            (None, None) => Error::Unknown,
105        }
106    }
107}