Skip to main content

nifty_download/worker/
stopped.rs

1//! How a download stops other than by being saved, and why one fails.
2
3use std::fmt;
4use std::io;
5use std::path::PathBuf;
6
7use nifty_runtime::NoHomeFolder;
8
9use super::Abandoned;
10use crate::connection::ConnectionError;
11use crate::http::BadHeader;
12
13/// How a download ended, other than being saved.
14#[derive(Debug)]
15pub(super) enum Stopped {
16    Failed(DownloadError),
17    Cancelled,
18    Abandoned,
19}
20
21#[derive(Debug)]
22pub(super) enum DownloadError {
23    Connection(ConnectionError),
24    Header(BadHeader),
25    File(io::Error),
26    NoHomeFolder(NoHomeFolder),
27    /// The server sent a part of the file after the part already saved.
28    PartTooFar {
29        starts_at: u64,
30        saved: u64,
31    },
32    /// The server sent a part of the file other than the one a connection asked for.
33    PartMoved {
34        starts_at: u64,
35        asked: u64,
36    },
37    /// A connection's body ended before the end of its part.
38    PartCutShort {
39        saved: u64,
40        end: u64,
41    },
42    /// The download stopped and its partly written file couldn't be removed.
43    LeftBehind {
44        reason: Box<Stopped>,
45        path: PathBuf,
46    },
47}
48
49impl fmt::Display for DownloadError {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        match self {
52            Self::Connection(error) => write!(f, "{error}"),
53            Self::Header(error) => write!(f, "{error}"),
54            Self::File(error) => write!(f, "{error}"),
55            Self::NoHomeFolder(error) => write!(f, "{error}"),
56            Self::PartTooFar { starts_at, saved } => write!(
57                f,
58                "the server sent the file from byte {starts_at}, but only {saved} bytes are saved"
59            ),
60            Self::PartMoved { starts_at, asked } => write!(
61                f,
62                "the server sent the file from byte {starts_at} when asked for byte {asked} on"
63            ),
64            Self::PartCutShort { saved, end } => write!(
65                f,
66                "the server stopped sending the file at byte {saved}, before byte {end}"
67            ),
68            Self::LeftBehind { reason, path } => {
69                let reason = match &**reason {
70                    Stopped::Failed(error) => error.to_string(),
71                    Stopped::Cancelled => "cancelled".to_owned(),
72                    Stopped::Abandoned => "the app stopped waiting for it".to_owned(),
73                };
74                write!(
75                    f,
76                    "{reason}; the partial file {} is left behind",
77                    path.display()
78                )
79            }
80        }
81    }
82}
83
84impl From<io::Error> for Stopped {
85    fn from(error: io::Error) -> Self {
86        Self::Failed(DownloadError::File(error))
87    }
88}
89
90impl From<NoHomeFolder> for Stopped {
91    fn from(error: NoHomeFolder) -> Self {
92        Self::Failed(DownloadError::NoHomeFolder(error))
93    }
94}
95
96impl From<Abandoned> for Stopped {
97    fn from(Abandoned: Abandoned) -> Self {
98        Self::Abandoned
99    }
100}