Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: use custom error types in internal and external crates #182

Merged
merged 4 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ path = "src/main.rs"

# Main binary (wws CLI dependencies)
[dependencies]
anyhow = { workspace = true }
actix-web = { workspace = true }
anyhow = "1.0.66"
env_logger = "0.10.0"
clap = { version = "4.0.10", features = ["derive"] }
prettytable-rs = "0.10.0"
Expand Down Expand Up @@ -74,7 +74,6 @@ exclude = [

[workspace.dependencies]
actix-web = "4"
anyhow = "1.0.66"
lazy_static = "1.4.0"
reqwest = "0.11"
serde = { version = "1.0", features = ["derive"] }
Expand Down
1 change: 0 additions & 1 deletion crates/config/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ license = { workspace = true }
repository = { workspace = true }

[dependencies]
anyhow = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
toml = { workspace = true }
Expand Down
39 changes: 39 additions & 0 deletions crates/config/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

pub type Result<T> = std::result::Result<T, ConfigError>;

#[derive(Debug)]
pub enum ConfigError {
CannotLoadConfig(std::io::Error),
CannotParseConfig(toml::de::Error),
CannotSaveConfig,
}

impl std::fmt::Display for ConfigError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::CannotLoadConfig(err) => write!(f, "Could not load configuration: {}", err),
Self::CannotParseConfig(err) => write!(f, "Could not parse configuration: {}", err),
Self::CannotSaveConfig => write!(f, "Could not save configuration"),
}
}
}

impl From<std::io::Error> for ConfigError {
fn from(error: std::io::Error) -> Self {
ConfigError::CannotLoadConfig(error)
}
}

impl From<toml::de::Error> for ConfigError {
fn from(error: toml::de::Error) -> ConfigError {
ConfigError::CannotParseConfig(error)
}
}

impl From<toml::ser::Error> for ConfigError {
fn from(_: toml::ser::Error) -> ConfigError {
ConfigError::CannotSaveConfig
}
}
16 changes: 8 additions & 8 deletions crates/config/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright 2022 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use anyhow::{anyhow, Result};
pub mod errors;
pub use errors::Result;

use serde::{Deserialize, Serialize};
use std::{
fs,
Expand Down Expand Up @@ -42,9 +44,7 @@ impl Config {
let config_path = Self::config_path(project_root);

if config_path.exists() {
toml::from_str(&fs::read_to_string(config_path)?).map_err(|_| {
anyhow!("Error opening the .wws.toml file. The file format is not correct")
})
Ok(toml::from_str(&fs::read_to_string(config_path)?)?)
} else {
Ok(Self::default())
}
Expand Down Expand Up @@ -111,10 +111,10 @@ impl Config {
/// Write the current configuration into the `.wws.toml` file. It will
/// store it in the project root folder
pub fn save(&self, project_root: &Path) -> Result<()> {
let contents = toml::to_string_pretty(self)?;

fs::write(Self::config_path(project_root), contents)
.map_err(|_| anyhow!("Error saving the .wws.toml file"))
Ok(fs::write(
Self::config_path(project_root),
toml::to_string_pretty(self)?,
)?)
ereslibre marked this conversation as resolved.
Show resolved Hide resolved
}

/// Retrieve the configuration path from the project root
Expand Down
1 change: 0 additions & 1 deletion crates/data-kv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ license = { workspace = true }
repository = { workspace = true }

[dependencies]
anyhow = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
1 change: 0 additions & 1 deletion crates/project/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ license = { workspace = true }
repository = { workspace = true }

[dependencies]
anyhow = { workspace = true }
reqwest = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
Expand Down
68 changes: 68 additions & 0 deletions crates/project/src/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use wws_store::errors::StoreError;

pub type Result<T> = std::result::Result<T, FetchError>;

#[derive(Debug)]
pub enum FetchError {
DefaultBranchMissing,
GitError(git2::Error),
HttpError(reqwest::Error),
InvalidURL,
InvalidReusedRepository,
InvalidRepository,
InvalidChecksum,
MissingPathInFilesystem,
StoreError(StoreError),
}

impl std::fmt::Display for FetchError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::DefaultBranchMissing => write!(
f,
"Couldn't find the default main branch. Please, set the Git branch you want to use"
),
Self::GitError(err) => write!(f, "Git error: {}", err),
Self::HttpError(err) => write!(f, "HTTP error: {}", err),
Self::InvalidURL => write!(f, "Invalid URL"),
Self::InvalidReusedRepository => write!(f, "Invalid local copy of repository"),
Self::InvalidRepository => write!(f, "Invalid repository"),
Self::InvalidChecksum => write!(f, "Invalid checksum"),
Self::MissingPathInFilesystem => write!(f, "Missing path in filesystem"),
Self::StoreError(err) => write!(f, "Store error: {}", err),
}
}
}

impl From<git2::Error> for FetchError {
fn from(error: git2::Error) -> Self {
FetchError::GitError(error)
}
}

impl From<url::ParseError> for FetchError {
fn from(_error: url::ParseError) -> Self {
FetchError::InvalidURL
}
}

impl From<reqwest::Error> for FetchError {
fn from(error: reqwest::Error) -> Self {
FetchError::HttpError(error)
}
}

impl From<StoreError> for FetchError {
fn from(error: StoreError) -> Self {
FetchError::StoreError(error)
}
}

impl From<std::string::FromUtf8Error> for FetchError {
fn from(_error: std::string::FromUtf8Error) -> Self {
FetchError::InvalidRepository
}
}
2 changes: 1 addition & 1 deletion crates/project/src/fetch.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright 2022 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::errors::Result;
use crate::metadata::Checksum;
use anyhow::Result;
use reqwest::header::USER_AGENT;

/// The current wws version
Expand Down
13 changes: 7 additions & 6 deletions crates/project/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
// Copyright 2022-2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

pub mod errors;
mod fetch;
pub mod metadata;
pub mod options;
pub mod types;

use anyhow::{bail, Result};
use errors::Result;
use fetch::fetch_and_validate;
use metadata::{RemoteFile, Runtime};
use options::Options;
Expand Down Expand Up @@ -68,7 +69,7 @@ pub fn identify_type(location: &Path) -> Result<ProjectType> {
if path.exists() {
Ok(ProjectType::Local)
} else {
bail!("The given path does not exist in the local filesystem.")
Err(errors::FetchError::MissingPathInFilesystem)
}
}
}
Expand Down Expand Up @@ -138,19 +139,19 @@ pub fn check_runtime(project_root: &Path, repository: &str, runtime: &Runtime) -
/// and delete the folder.
pub fn uninstall_runtime(project_root: &Path, repository: &str, metadata: &Runtime) -> Result<()> {
// Delete the current folder
Store::new(
Ok(Store::new(
project_root,
&["runtimes", repository, &metadata.name, &metadata.version],
)
.delete_root_folder()
.delete_root_folder()?)
}

/// Downloads a remote file and saves into the given store. This
/// method also validates the file against the checksum provided
/// in the metadata.
async fn download_file(file: &RemoteFile, store: &Store) -> Result<()> {
let contents = fetch_and_validate(&file.url, &file.checksum).await?;
store.write(&[&file.filename], &contents)
Ok(store.write(&[&file.filename], &contents)?)
}

#[cfg(test)]
Expand Down Expand Up @@ -190,7 +191,7 @@ mod tests {
Ok(_) => {
panic!("The folder doesn't exist, so identifying it should fail.");
}
Err(err) => assert!(err.to_string().contains("does not exist")),
Err(err) => assert!(err.to_string().contains("Missing path in filesystem")),
}
}
}
Expand Down
11 changes: 4 additions & 7 deletions crates/project/src/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright 2022 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::errors::{self, Result};
use crate::fetch::fetch;
use anyhow::{anyhow, Result};
use serde::{Deserialize, Serialize};
use sha256::digest as sha256_digest;
use std::collections::HashMap;
Expand Down Expand Up @@ -61,15 +61,12 @@ impl Repository {
}

impl FromStr for Repository {
type Err = anyhow::Error;
type Err = errors::FetchError;

/// Reads and parses the metadata from a slice of bytes. It will return
/// a result as the deserialization may fail.
fn from_str(data: &str) -> Result<Self> {
toml::from_str::<Repository>(data).map_err(|err| {
println!("Err: {err}");
anyhow!("wws could not deserialize the repository metadata")
})
toml::from_str::<Repository>(data).map_err(|_| errors::FetchError::InvalidRepository)
}
}

Expand Down Expand Up @@ -169,7 +166,7 @@ impl Checksum {
pub fn validate(&self, bytes: &[u8]) -> Result<()> {
match self {
Checksum::Sha256 { value } if value == &sha256_digest(bytes) => Ok(()),
_ => Err(anyhow!("The checksums don't match")),
_ => Err(errors::FetchError::InvalidChecksum),
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions crates/project/src/types/git.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright 2023 VMware, Inc.
// SPDX-License-Identifier: Apache-2.0

use crate::errors::{self, Result};
use crate::options::{GitReference, Options};
use anyhow::{anyhow, bail, Result};
use git2::{build::CheckoutBuilder, FetchOptions, Oid, Repository};
use sha256::digest as sha256_digest;
use std::{
Expand All @@ -18,19 +18,17 @@ static DEFAULT_REMOTE: &str = "origin";
pub fn prepare_git_project(location: &Path, options: Options) -> Result<PathBuf> {
let project_url = location
.to_str()
.ok_or(anyhow!("The project URL cannot be retrieved"))?;
.ok_or(errors::FetchError::InvalidRepository)?;
let (folder, git_ref) = parse_options(options);
// By default, we use temporary dirs
let mut dir = temp_dir().join(sha256_digest(project_url));

let repo = if dir.exists() {
// Reuse the same repository.
Repository::open(&dir)
.map_err(|e| anyhow!("There was an error opening the repository: {e}"))?
Repository::open(&dir).map_err(|_| errors::FetchError::InvalidReusedRepository)?
} else {
// clone it
Repository::clone(project_url, &dir)
.map_err(|e| anyhow!("There was an error cloning the repository: {e}"))?
Repository::clone(project_url, &dir).map_err(|_| errors::FetchError::InvalidRepository)?
};

if let Some(git_ref) = git_ref.as_ref() {
Expand Down Expand Up @@ -106,7 +104,7 @@ fn detect_main_branch(repo: &Repository) -> Result<&str> {
} else if repo.find_branch("master", git2::BranchType::Local).is_ok() {
Ok("master")
} else {
bail!("Couldn't find the default main branch. Please, set the Git branch you want to use.")
Err(errors::FetchError::DefaultBranchMissing)
}
}

Expand Down
1 change: 0 additions & 1 deletion crates/runtimes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ license = { workspace = true }
repository = { workspace = true }

[dependencies]
anyhow = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
wasmtime-wasi = { workspace = true }
Expand Down
Loading