Skip to content
Snippets Groups Projects
Commit 19baf95f authored by James Hendry's avatar James Hendry
Browse files

Bug 1682239 - [geckodriver] Use anyhow for errors in geckodriver....

Bug 1682239 - [geckodriver] Use anyhow for errors in geckodriver. r=whimboo,webdriver-reviewers,jgraham

Differential Revision: https://phabricator.services.mozilla.com/D195255
parent d20809a0
No related branches found
No related tags found
No related merge requests found
......@@ -2055,6 +2055,7 @@ dependencies = [
name = "geckodriver"
version = "0.33.0"
dependencies = [
"anyhow",
"base64 0.21.3",
"chrono",
"clap",
......@@ -2073,6 +2074,7 @@ dependencies = [
"serde_json",
"serde_yaml",
"tempfile",
"thiserror",
"unicode-segmentation",
"url",
"uuid",
......
......@@ -21,6 +21,7 @@ license = "MPL-2.0"
repository = "https://hg.mozilla.org/mozilla-central/file/tip/testing/geckodriver"
[dependencies]
anyhow = "1"
base64 = "0.21"
chrono = "0.4.6"
clap = { version = "4", default-features = false, features = ["cargo", "std", "suggestions", "wrap_help", "string"] }
......@@ -38,6 +39,7 @@ serde_derive = "1.0"
serde_json = "1.0"
serde_yaml = "0.8"
tempfile = "3"
thiserror = "1"
unicode-segmentation = "1.9"
url = "2.4"
uuid = { version = "1.0", features = ["v4"] }
......
......@@ -3,9 +3,9 @@ use mozdevice::{AndroidStorage, Device, Host, UnixPathBuf};
use mozprofile::profile::Profile;
use serde::Serialize;
use serde_yaml::{Mapping, Value};
use std::fmt;
use std::io;
use std::time;
use thiserror::Error;
use webdriver::error::{ErrorStatus, WebDriverError};
// TODO: avoid port clashes across GeckoView-vehicles.
......@@ -20,47 +20,22 @@ const CONFIG_FILE_HEADING: &str = r#"## GeckoView configuration YAML
pub type Result<T> = std::result::Result<T, AndroidError>;
#[derive(Debug)]
#[derive(Debug, Error)]
pub enum AndroidError {
#[error("Activity for package '{0}' not found")]
ActivityNotFound(String),
Device(mozdevice::DeviceError),
IO(io::Error),
PackageNotFound(String),
Serde(serde_yaml::Error),
}
impl fmt::Display for AndroidError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
AndroidError::ActivityNotFound(ref package) => {
write!(f, "Activity for package '{}' not found", package)
}
AndroidError::Device(ref message) => message.fmt(f),
AndroidError::IO(ref message) => message.fmt(f),
AndroidError::PackageNotFound(ref package) => {
write!(f, "Package '{}' not found", package)
}
AndroidError::Serde(ref message) => message.fmt(f),
}
}
}
#[error(transparent)]
Device(#[from] mozdevice::DeviceError),
impl From<io::Error> for AndroidError {
fn from(value: io::Error) -> AndroidError {
AndroidError::IO(value)
}
}
#[error(transparent)]
IO(#[from] io::Error),
impl From<mozdevice::DeviceError> for AndroidError {
fn from(value: mozdevice::DeviceError) -> AndroidError {
AndroidError::Device(value)
}
}
#[error("Package '{0}' not found")]
PackageNotFound(String),
impl From<serde_yaml::Error> for AndroidError {
fn from(value: serde_yaml::Error) -> AndroidError {
AndroidError::Serde(value)
}
#[error(transparent)]
Serde(#[from] serde_yaml::Error),
}
impl From<AndroidError> for WebDriverError {
......
......@@ -18,37 +18,24 @@ use serde_json::{Map, Value};
use std::collections::BTreeMap;
use std::default::Default;
use std::ffi::OsString;
use std::fmt::{self, Display};
use std::fs;
use std::io;
use std::io::BufWriter;
use std::io::Cursor;
use std::path::{Path, PathBuf};
use std::str::{self, FromStr};
use thiserror::Error;
use webdriver::capabilities::{BrowserCapabilities, Capabilities};
use webdriver::error::{ErrorStatus, WebDriverError, WebDriverResult};
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Error)]
enum VersionError {
VersionError(mozversion::Error),
#[error(transparent)]
VersionError(#[from] mozversion::Error),
#[error("No binary provided")]
MissingBinary,
}
impl From<mozversion::Error> for VersionError {
fn from(err: mozversion::Error) -> VersionError {
VersionError::VersionError(err)
}
}
impl Display for VersionError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
VersionError::VersionError(ref x) => x.fmt(f),
VersionError::MissingBinary => "No binary provided".fmt(f),
}
}
}
impl From<VersionError> for WebDriverError {
fn from(err: VersionError) -> WebDriverError {
WebDriverError::new(ErrorStatus::SessionNotCreated, err.to_string())
......@@ -398,8 +385,6 @@ pub enum ProfileType {
Temporary,
}
/// Rust representation of `moz:firefoxOptions`.
///
/// Calling `FirefoxOptions::from_capabilities(binary, capabilities)` causes
......
......@@ -27,11 +27,10 @@ extern crate zip;
extern crate log;
use std::env;
use std::fmt;
use std::io;
use std::net::{IpAddr, SocketAddr, ToSocketAddrs};
use std::path::PathBuf;
use std::result;
use std::process::ExitCode;
use std::str::FromStr;
use clap::{Arg, ArgAction, Command};
......@@ -60,69 +59,13 @@ pub mod test;
use crate::command::extension_routes;
use crate::logging::Level;
use crate::marionette::{MarionetteHandler, MarionetteSettings};
use anyhow::{bail, Result as ProgramResult};
use clap::ArgMatches;
use mozdevice::AndroidStorageInput;
use url::{Host, Url};
const EXIT_SUCCESS: i32 = 0;
const EXIT_USAGE: i32 = 64;
const EXIT_UNAVAILABLE: i32 = 69;
enum FatalError {
Parsing(clap::Error),
Usage(String),
Server(io::Error),
}
impl FatalError {
fn exit_code(&self) -> i32 {
use FatalError::*;
match *self {
Parsing(_) | Usage(_) => EXIT_USAGE,
Server(_) => EXIT_UNAVAILABLE,
}
}
fn help_included(&self) -> bool {
matches!(*self, FatalError::Parsing(_))
}
}
impl From<clap::Error> for FatalError {
fn from(err: clap::Error) -> FatalError {
FatalError::Parsing(err)
}
}
impl From<io::Error> for FatalError {
fn from(err: io::Error) -> FatalError {
FatalError::Server(err)
}
}
// harmonise error message from clap to avoid duplicate "error:" prefix
impl fmt::Display for FatalError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use FatalError::*;
let s = match *self {
Parsing(ref err) => err.to_string(),
Usage(ref s) => format!("error: {}", s),
Server(ref err) => format!("error: {}", err),
};
write!(f, "{}", s)
}
}
macro_rules! usage {
($msg:expr) => {
return Err(FatalError::Usage($msg.to_string()))
};
($fmt:expr, $($arg:tt)+) => {
return Err(FatalError::Usage(format!($fmt, $($arg)+)))
};
}
type ProgramResult<T> = result::Result<T, FatalError>;
const EXIT_USAGE: u8 = 64;
const EXIT_UNAVAILABLE: u8 = 69;
#[allow(clippy::large_enum_variant)]
enum Operation {
......@@ -151,10 +94,10 @@ fn server_address(webdriver_host: &str, webdriver_port: u16) -> ProgramResult<So
let mut socket_addrs = match format!("{}:{}", webdriver_host, webdriver_port).to_socket_addrs()
{
Ok(addrs) => addrs.collect::<Vec<_>>(),
Err(e) => usage!("{}: {}:{}", e, webdriver_host, webdriver_port),
Err(e) => bail!("{}: {}:{}", e, webdriver_host, webdriver_port),
};
if socket_addrs.is_empty() {
usage!(
bail!(
"Unable to resolve host: {}:{}",
webdriver_host,
webdriver_port
......@@ -224,9 +167,7 @@ fn get_allowed_origins(allow_origins: Option<clap::parser::ValuesRef<Url>>) -> V
allow_origins.into_iter().flatten().cloned().collect()
}
fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
let args = cmd.try_get_matches_from_mut(env::args())?;
fn parse_args(args: &ArgMatches) -> ProgramResult<Operation> {
if args.get_flag("help") {
return Ok(Operation::Help);
} else if args.get_flag("version") {
......@@ -248,7 +189,7 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
let s = args.get_one::<String>("webdriver_port").unwrap();
match u16::from_str(s) {
Ok(n) => n,
Err(e) => usage!("invalid --port: {}: {}", e, s),
Err(e) => bail!("invalid --port: {}: {}", e, s),
}
};
......@@ -269,7 +210,7 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
tempfile::tempdir()
};
if tmp_dir.is_err() {
usage!("Unable to write to temporary directory; consider --profile-root with a writeable directory")
bail!("Unable to write to temporary directory; consider --profile-root with a writeable directory")
}
}
......@@ -277,7 +218,7 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
let marionette_port = match args.get_one::<String>("marionette_port") {
Some(s) => match u16::from_str(s) {
Ok(n) => Some(n),
Err(e) => usage!("invalid --marionette-port: {}", e),
Err(e) => bail!("invalid --marionette-port: {}", e),
},
None => None,
};
......@@ -287,14 +228,14 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
let websocket_port = match args.get_one::<String>("websocket_port") {
Some(s) => match u16::from_str(s) {
Ok(n) => n,
Err(e) => usage!("invalid --websocket-port: {}", e),
Err(e) => bail!("invalid --websocket-port: {}", e),
},
None => 9222,
};
let host = match parse_hostname(webdriver_host) {
Ok(name) => name,
Err(e) => usage!("invalid --host {}: {}", webdriver_host, e),
Err(e) => bail!("invalid --host {}: {}", webdriver_host, e),
};
let allow_hosts = get_allowed_hosts(host, args.get_many("allow_hosts"));
......@@ -326,8 +267,8 @@ fn parse_args(cmd: &mut Command) -> ProgramResult<Operation> {
})
}
fn inner_main(cmd: &mut Command) -> ProgramResult<()> {
match parse_args(cmd)? {
fn inner_main(operation: Operation, cmd: &mut Command) -> ProgramResult<()> {
match operation {
Operation::Help => print_help(cmd),
Operation::Version => print_version(),
......@@ -365,24 +306,34 @@ fn inner_main(cmd: &mut Command) -> ProgramResult<()> {
Ok(())
}
fn main() {
use std::process::exit;
fn main() -> ExitCode {
let mut cmd = make_command();
// use std::process:Termination when it graduates
exit(match inner_main(&mut cmd) {
Ok(_) => EXIT_SUCCESS,
let args = match cmd.try_get_matches_from_mut(env::args()) {
Ok(args) => args,
Err(e) => {
// Clap already says "error:" and don't repeat help.
eprintln!("{}: {}", get_program_name(), e);
if !e.help_included() {
print_help(&mut cmd);
}
return ExitCode::from(EXIT_USAGE);
}
};
e.exit_code()
let operation = match parse_args(&args) {
Ok(op) => op,
Err(e) => {
eprintln!("{}: error: {}", get_program_name(), e);
print_help(&mut cmd);
return ExitCode::from(EXIT_USAGE);
}
});
};
if let Err(e) = inner_main(operation, &mut cmd) {
eprintln!("{}: error: {}", get_program_name(), e);
print_help(&mut cmd);
return ExitCode::from(EXIT_UNAVAILABLE);
}
ExitCode::SUCCESS
}
fn make_command() -> Command {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment