Skip to content

Commit

Permalink
[#83] Create 'AssetError' and refactor 'select_asset' function to ret…
Browse files Browse the repository at this point in the history
…urn it (#97)

Resolves #83

Refactored `ToolInfo.select_asset(&assets)` to return a custom
`AssetError` error instead of a string and added tests for the function.

The `AssetError` enum has two possible values:
- `NameUnknown`: this is when `AssetName` does not know what the asset
name is for a specific OS the user uses.
- `NotFound`: this is when the `AssetName` is not in the `Assets` slice
passed into the `select_asset(&assets)` method.

Implemented `std::fmt::Display` trait for `AssetError` and added tests
for it.
 
### Additional tasks

- [x] Documentation for changes provided/changed
- [x] Tests added

Co-authored-by: Dmitrii Kovanikov <[email protected]>
  • Loading branch information
zixuan-x and chshersh authored Sep 18, 2022
1 parent c768b8a commit 1dcd1e7
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 9 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ store_directory = "~/.local/bin"
By default `tool-sync` reads configuration from `$HOME/.tool.toml` you can run `tool
default-config` to print a default configuration example to std out. You can
redirect this out put to a file like so `tool default-config >
$HOME/.tools.toml`.
$HOME/.tool.toml`.

You can also quickly copy the above configuration to the default path by running
the following command (Unix-only):
Expand Down
30 changes: 29 additions & 1 deletion src/model/release.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,42 @@
use serde::Deserialize;
use std::env;
use std::fmt::{Display, Formatter};

#[derive(Deserialize, Debug)]
pub struct Release {
pub tag_name: String,
pub assets: Vec<Asset>,
}

#[derive(Deserialize, Debug, Clone)]
#[derive(Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct Asset {
pub id: u32,
pub name: String,
pub size: u64,
}

#[derive(Debug, PartialEq, Eq)]
pub enum AssetError {
/// Asset name of this OS is unknown
OsSelectorUnknown,

/// Asset name is not in the fetched assets
NotFound(String),
}

impl Display for AssetError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::OsSelectorUnknown => {
write!(
f,
"Unknown asset selector for OS: {}. Specify 'asset_name.your_os' in the cofig.",
env::consts::OS
)
}
Self::NotFound(asset_name) => {
write!(f, "No asset matching name: {}", asset_name)
}
}
}
}
119 changes: 114 additions & 5 deletions src/model/tool.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::release::Asset;
use crate::infra::client::Client;
use crate::model::asset_name::AssetName;
use crate::model::release::AssetError;

#[derive(Debug, PartialEq, Eq)]
pub enum Tool {
Expand Down Expand Up @@ -69,16 +70,15 @@ pub struct ToolInfo {
}

impl ToolInfo {
pub fn select_asset(&self, assets: &[Asset]) -> Result<Asset, String> {
/// Select an Asset from all Assets based on which Operating System is used
pub fn select_asset(&self, assets: &[Asset]) -> Result<Asset, AssetError> {
match self.asset_name.get_name_by_os() {
None => Err(String::from(
"Don't know the asset name for this OS: specify it explicitly in the config",
)),
None => Err(AssetError::OsSelectorUnknown),
Some(asset_name) => {
let asset = assets.iter().find(|&asset| asset.name.contains(asset_name));

match asset {
None => Err(format!("No asset matching name: {}", asset_name)),
None => Err(AssetError::NotFound(asset_name.to_owned())),
Some(asset) => Ok(asset.clone()),
}
}
Expand Down Expand Up @@ -107,3 +107,112 @@ pub struct ToolAsset {
/// GitHub API client that produces the stream for downloading the asset
pub client: Client,
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn asset_fount() {
let asset_name = "asset";

let tool_info = ToolInfo {
owner: "owner".to_string(),
repo: "repo".to_string(),
exe_name: "exe".to_string(),
tag: ToolInfoTag::Latest,
asset_name: AssetName {
linux: Some(asset_name.to_string()),
macos: Some(asset_name.to_string()),
windows: Some(asset_name.to_string()),
},
};

let assets = vec![
Asset {
id: 1,
name: "1".to_string(),
size: 10,
},
Asset {
id: 2,
name: asset_name.to_string(),
size: 50,
},
Asset {
id: 3,
name: "3".to_string(),
size: 77,
},
];

assert_eq!(
tool_info.select_asset(&assets),
Ok(Asset {
id: 2,
name: asset_name.to_string(),
size: 50
})
);
}

#[test]
fn asset_not_found() {
let asset_name = "asset";

let tool_info = ToolInfo {
owner: "owner".to_string(),
repo: "repo".to_string(),
exe_name: "exe".to_string(),
tag: ToolInfoTag::Latest,
asset_name: AssetName {
linux: Some(asset_name.to_string()),
macos: Some(asset_name.to_string()),
windows: Some(asset_name.to_string()),
},
};

let assets = vec![
Asset {
id: 1,
name: "1".to_string(),
size: 10,
},
Asset {
id: 2,
name: "2".to_string(),
size: 50,
},
Asset {
id: 3,
name: "3".to_string(),
size: 77,
},
];

assert_eq!(
tool_info.select_asset(&assets),
Err(AssetError::NotFound(asset_name.to_string()))
);
}

#[test]
fn asset_os_selector_unknown() {
let tool_info = ToolInfo {
owner: "owner".to_string(),
repo: "repo".to_string(),
exe_name: "exe".to_string(),
tag: ToolInfoTag::Latest,
asset_name: AssetName {
linux: None,
macos: None,
windows: None,
},
};

assert_eq!(
tool_info.select_asset(&Vec::new()),
Err(AssetError::OsSelectorUnknown)
);
}
}
4 changes: 2 additions & 2 deletions src/sync/prefetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ fn prefetch_tool(
None
}
Ok(release) => match tool_info.select_asset(&release.assets) {
Err(msg) => {
prefetch_progress.unexpected_err_msg(tool_name, &msg);
Err(err) => {
prefetch_progress.unexpected_err_msg(tool_name, &err.to_string());
prefetch_progress.update_message(already_completed);
None
}
Expand Down

0 comments on commit 1dcd1e7

Please sign in to comment.