ilass_cli/
errors.rs

1// This file is part of the Rust library and binary `ilass`.
2//
3// Copyright (C) 2017 kaegi
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18use failure::{Backtrace, Context, Fail};
19use std::fmt;
20use std::path::PathBuf;
21use subparse::SubtitleFormat;
22
23#[macro_export]
24macro_rules! define_error {
25    ($error:ident, $errorKind:ident) => {
26        #[derive(Debug)]
27        pub struct $error {
28            inner: Context<$errorKind>,
29        }
30
31        impl Fail for $error {
32            fn name(&self) -> Option<&str> {
33                self.inner.name()
34            }
35
36            fn cause(&self) -> Option<&dyn Fail> {
37                self.inner.cause()
38            }
39
40            fn backtrace(&self) -> Option<&Backtrace> {
41                self.inner.backtrace()
42            }
43        }
44
45        impl fmt::Display for $error {
46            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
47                fmt::Display::fmt(&self.inner, f)
48            }
49        }
50
51        #[allow(dead_code)]
52        impl $error {
53            pub fn kind(&self) -> &$errorKind {
54                self.inner.get_context()
55            }
56        }
57
58        #[allow(dead_code)]
59        impl $errorKind {
60            pub fn into_error(self) -> $error {
61                $error {
62                    inner: Context::new(self),
63                }
64            }
65        }
66
67        impl From<$errorKind> for $error {
68            fn from(kind: $errorKind) -> $error {
69                $error {
70                    inner: Context::new(kind),
71                }
72            }
73        }
74
75        impl From<Context<$errorKind>> for $error {
76            fn from(inner: Context<$errorKind>) -> $error {
77                $error { inner: inner }
78            }
79        }
80    };
81}
82
83define_error!(InputFileError, InputFileErrorKind);
84
85#[derive(Clone, Eq, PartialEq, Debug, Fail)]
86pub enum InputFileErrorKind {
87    VideoFile(PathBuf),
88    SubtitleFile(PathBuf),
89}
90
91impl fmt::Display for InputFileErrorKind {
92    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93        match self {
94            InputFileErrorKind::VideoFile(p) => write!(f, "processing video file '{}' failed", p.display()),
95            InputFileErrorKind::SubtitleFile(p) => write!(f, "processing subtitle file '{}' failed", p.display()),
96        }
97    }
98}
99
100define_error!(FileOperationError, FileOperationErrorKind);
101
102#[derive(Clone, Eq, PartialEq, Debug, Fail)]
103pub enum FileOperationErrorKind {
104    FileOpen { path: PathBuf },
105    FileRead { path: PathBuf },
106    FileWrite { path: PathBuf },
107}
108
109impl fmt::Display for FileOperationErrorKind {
110    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
111        match self {
112            FileOperationErrorKind::FileOpen { path } => write!(f, "failed to open file '{}'", path.display()),
113            FileOperationErrorKind::FileRead { path } => write!(f, "failed to read file '{}'", path.display()),
114            FileOperationErrorKind::FileWrite { path } => write!(f, "failed to read file '{}'", path.display()),
115        }
116    }
117}
118
119define_error!(InputVideoError, InputVideoErrorKind);
120
121#[derive(Clone, Eq, PartialEq, Debug, Fail)]
122pub enum InputVideoErrorKind {
123    FailedToDecode { path: PathBuf },
124    VadAnalysisFailed,
125}
126
127impl fmt::Display for InputVideoErrorKind {
128    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
129        match self {
130            InputVideoErrorKind::FailedToDecode { path } => {
131                write!(f, "failed to extract voice segments from file '{}'", path.display())
132            }
133            InputVideoErrorKind::VadAnalysisFailed => write!(f, "failed to analyse audio segment for voice activity"),
134        }
135    }
136}
137
138define_error!(InputSubtitleError, InputSubtitleErrorKind);
139
140#[derive(Clone, Eq, PartialEq, Debug, Fail)]
141pub enum InputSubtitleErrorKind {
142    ReadingSubtitleFileFailed(PathBuf),
143    UnknownSubtitleFormat(PathBuf),
144    ParsingSubtitleFailed(PathBuf),
145    RetreivingSubtitleLinesFailed(PathBuf),
146}
147
148impl fmt::Display for InputSubtitleErrorKind {
149    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
150        match self {
151            InputSubtitleErrorKind::ReadingSubtitleFileFailed(path) => {
152                write!(f, "reading subtitle file '{}' failed", path.display())
153            }
154            InputSubtitleErrorKind::UnknownSubtitleFormat(path) => {
155                write!(f, "unknown subtitle format for file '{}'", path.display())
156            }
157            InputSubtitleErrorKind::ParsingSubtitleFailed(path) => {
158                write!(f, "parsing subtitle file '{}' failed", path.display())
159            }
160            InputSubtitleErrorKind::RetreivingSubtitleLinesFailed(path) => {
161                write!(f, "retreiving subtitle file '{}' failed", path.display())
162            }
163        }
164    }
165}
166
167define_error!(InputArgumentsError, InputArgumentsErrorKind);
168
169#[derive(Clone, PartialEq, Debug, Fail)]
170pub enum InputArgumentsErrorKind {
171    #[fail(
172        display = "expected value '{}' to be in range '{}'-'{}', found value '{}'",
173        argument_name, min, max, value
174    )]
175    ValueNotInRange {
176        argument_name: String,
177        min: f64,
178        max: f64,
179        value: f64,
180    },
181    #[fail(display = "expected positive number for '{}', found '{}'", argument_name, value)]
182    ExpectedPositiveNumber { argument_name: String, value: i64 },
183
184    #[fail(display = "expected non-negative number for '{}', found '{}'", argument_name, value)]
185    ExpectedNonNegativeNumber { argument_name: String, value: f64 },
186
187    #[fail(display = "argument '{}' with value '{}' could not be parsed", argument_name, value)]
188    ArgumentParseError { argument_name: String, value: String },
189}
190
191define_error!(TopLevelError, TopLevelErrorKind);
192
193pub enum TopLevelErrorKind {
194    FileFormatMismatch {
195        input_file_path: PathBuf,
196        output_file_path: PathBuf,
197        input_file_format: SubtitleFormat,
198    },
199    FailedToUpdateSubtitle,
200    FailedToGenerateSubtitleData,
201    FailedToInstantiateSubtitleFile,
202}
203
204impl fmt::Display for TopLevelErrorKind {
205    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
206        match self {
207         TopLevelErrorKind::FileFormatMismatch { input_file_path, output_file_path, input_file_format } => write!(f, "output file '{}' seems to have a different format than input file '{}' with format '{}' (this program does not perform conversions)", output_file_path.display(), input_file_path.display(), input_file_format.get_name()),
208         TopLevelErrorKind::FailedToUpdateSubtitle => write!(f, "failed to change lines in the subtitle"),
209         TopLevelErrorKind::FailedToGenerateSubtitleData => write!(f, "failed to generate data for subtitle"),
210         TopLevelErrorKind::FailedToInstantiateSubtitleFile => write!(f, "failed to instantiate subtitle file"),
211        }
212    }
213}