Provide colorful user interface and error handling

This commit is contained in:
Emi Simpson 2022-08-11 23:00:15 -04:00
parent a29322f108
commit eb79a65500
Signed by: Emi
GPG key ID: A12F2C2FFDC3D847
2 changed files with 99 additions and 8 deletions

View file

@ -1,3 +1,4 @@
use core::fmt;
use std::{path::{PathBuf, Path}, io};
#[derive(Debug)]
@ -92,3 +93,81 @@ impl AviaryError {
}
}
}
impl fmt::Display for AviaryError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "\x1b[31mError!\x1b[0m\n")?;
match self {
Self::FileDNE(path) =>
writeln!(f, "The file at \x1b[36m{}\x1b[0m does not exist", path.display()),
Self::ReadPermissionDenied(path) =>
writeln!(f, "Permission denied for the file \x1b[36m{}\x1b[0m", path.display()),
Self::StreamReadError(path, err) =>
writeln!(f,
concat!(
"Something bad happened while we were reading from ",
"\x1b[36m{}\x1b[0m. This could be caused by removing a ",
"drive/phone without unplugging it, or several other possible ",
"things. This error might give you a clue:\n\n{}"
),
path.display(), err
),
Self::ImageFormatError(path) =>
writeln!(f,
concat!(
"We had a little bit of trouble understanding the image at ",
"\x1b[36m{}\x1b[0m. Normally, this indicates that it might not be ",
"an image file (e.g. if it's a video instead). If you're sure that ",
"it's an image, this could indicate that it's corrupted or of an ",
"unsupported format.\n\n",
"\x1b[34mSupported formats:\x1b[0m\n",
" - gif\n",
" - jpg\n",
" - ico\n",
" - png\n",
" - tiff\n",
" - webp\n",
" - bmp\n",
" - hdr",
),
path.display(),
),
Self::ServerError(msg) =>
writeln!(f,
concat!(
"The server seems to be down or misbehaving at the minute. ",
"Normally, this issue should resolve by itself once the server ",
"maintainers identify the problem and fix it.\n\n",
"If the issue persists, please submit an issue on the bug ",
"tracker. This error message may help to identify the problem:",
"\n\n\x1b[31m{}\x1b[0m",
),
msg,
),
Self::ConnectionError(msg) =>
writeln!(f,
concat!(
"There was an issue connecting to the server! Check that your ",
"internet is online, and you can access websites normally. ",
"This information might help diagnose the issue:\n\n",
"\x1b[31m{}\x1b[0m",
),
msg,
),
Self::BadServerParameter =>
writeln!(f,
concat!(
"The server URL you provided was \x1b[31minvalid\x1b[0m! ",
"Please check to make sure that it is properly formatted, like ",
"\x1b[1m0x0.st\x1b[0m or \x1b[1menvs.sh\x1b[0m.",
),
),
}
}
}

View file

@ -33,6 +33,7 @@ fn trim_url<'a>(base_url: &str, url: &'a str) -> Option<&'a str> {
fn main() {
let args: parse::Args = argh::from_env();
print!("Checking files...");
let (files, errors): (Vec<_>, Vec<_>) = args.images.iter()
.map(Path::new)
.map(|path| File::open(path)
@ -42,7 +43,7 @@ fn main() {
.partition_result();
if !errors.is_empty() {
println!("[FATAL] The was a problem accessing some of the files you provided!");
println!(" \x1b[31mError!\x1b[0m");
let (nonexistant, noread): (Vec<_>, Vec<_>) = errors.iter()
.partition_map(|e| match e {
AviaryError::FileDNE(path) => Either::Left(path),
@ -51,13 +52,14 @@ fn main() {
});
if !nonexistant.is_empty() {
println!("\nWe didn't see any files at the following locations:");
nonexistant.iter().for_each(|path| println!("{}", path.display()));
nonexistant.iter().for_each(|path| println!("\x1b[37m- \x1b[31m{}", path.display()));
}
if !noread.is_empty() {
println!("\nWe found these files, but didn't have permission to open them:");
noread.iter().for_each(|path| println!("{}", path.display()));
println!("\x1b[0m\nWe found these files, but didn't have permission to open them:");
noread.iter().for_each(|path| println!("\x1b[37m- \x1b[31m{}", path.display()));
}
} else {
println!(" \x1b[32mDone!\n");
let agent = upload::get_agent();
let full_server = if args.server.starts_with("http") {
Cow::Borrowed(&args.server)
@ -65,6 +67,7 @@ fn main() {
Cow::Owned(format!("https://{}", args.server))
};
let index_url = files.into_iter()
.inspect(|(path, _)| print!("\x1b[36m{}\x1b[0m\n\x1b[37m├─\x1b[0m Reading... ", path.display()))
.map(|(path, mut file)|
(|| {
let mut buff = Vec::with_capacity(file.metadata()?.len() as usize);
@ -72,11 +75,13 @@ fn main() {
Ok((path, buff))
})().map_err(|e| AviaryError::StreamReadError(path.to_owned(), e))
)
.inspect(|r| if r.is_ok() { print!("\x1b[32mDone!\n\x1b[37m├─\x1b[0m Thumbnailing... ") })
.map(|r| r.and_then(|(path, raw_dat)| {
let (thumbnail, blurhash) = thumbnailing::thumbnail(&raw_dat)
.map_err(|_| AviaryError::ImageFormatError(path.to_owned()))?;
Ok((raw_dat, thumbnail, blurhash))
}))
.inspect(|r| if r.is_ok() { print!("\x1b[32mDone!\n\x1b[37m├─\x1b[0m Encrypting... ")})
.map_ok(|(raw_dat, thumbnail, blurhash)| {
let key = crypto::make_key();
(
@ -86,6 +91,7 @@ fn main() {
blurhash
)
})
.inspect(|r| if r.is_ok() { print!("\x1b[32mDone!\n\x1b[37m└─\x1b[0m Uploading... ")})
.map(|r| r.and_then(|(key, full_img, thumb, blurhash)|
upload::put_data(&agent, &*full_server, &thumb)
.and_then(|thumb_url|
@ -102,6 +108,7 @@ fn main() {
Err(AviaryError::ServerError(format!("Received bad response from server: {}", full_url)))
}
}))
.inspect(|r| if r.is_ok() { println!("\x1b[32mDone!\n")})
.map_ok(|(key, full_url, thumb_url, blurhash)| protobuf::image::Image {
key: key.into(),
full_url, thumb_url, blurhash,
@ -122,11 +129,16 @@ fn main() {
.expect("Error serializing protocol buffers")
);
let encoded_key = base64::encode(index_key);
print!("\x1b[0mUploading index... ");
upload::put_data(&agent, &*full_server, &encrypted_index)
.map_err(|e| AviaryError::from_upload_error(e))
.map(|url| format!("{}#{}", url.trim(), &encoded_key))
})
.unwrap();
println!("Success! {}", index_url);
.map(|url| format!("{}#{}", url.trim().trim_end_matches(".bin"), &encoded_key))
});
match index_url {
Ok(url) =>
println!("\x1b[32mDone!\n\n\x1b[34mYour gallery is: \x1b[1;0m{}", url),
Err(e) =>
print!("{}", e),
}
}
}