Skip to content

Commit

Permalink
Merge pull request #46 from hiro-o918/refactor/configs
Browse files Browse the repository at this point in the history
♻️ refactor configs and add tests
  • Loading branch information
hiro-o918 authored May 3, 2022
2 parents b4052eb + 7b598dc commit d0f8f9a
Show file tree
Hide file tree
Showing 2 changed files with 141 additions and 19 deletions.
154 changes: 137 additions & 17 deletions src/configs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::path::PathBuf;
use std::{collections::HashMap, path::Path};

use anyhow::{Context, Result};
use config::{Config, File};
use config::{Config, File, FileFormat};
use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

Expand All @@ -14,16 +14,16 @@ use crate::ctx;
type ProfileName = String;
type AuthScript = String;

static CONFIGS_PATH: Lazy<PathBuf> = Lazy::new(|| {
pub static CONFIGS_PATH: Lazy<PathBuf> = Lazy::new(|| {
let mut path = home_dir().unwrap();
path.push(".awsctx/configs.yaml");
path
});
const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
# # You can manually edit configurations according to the following usage
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
# auth_commands:
# auth_commands:
# # configuration for `foo` profile with aws configure
# foo: |
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
Expand All @@ -35,7 +35,7 @@ const CONFIGS_DESCRIPTIONS: &str = r#"# # Configurations for awsctx
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u user@example.com
"#;

#[derive(Debug, Serialize, Deserialize, Default)]
#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct Configs {
pub auth_commands: HashMap<ProfileName, AuthScript>,
}
Expand All @@ -46,17 +46,16 @@ impl Configs {
.map(|p| p.as_ref().to_path_buf())
.unwrap_or_else(|| CONFIGS_PATH.clone());
let c = Config::builder()
.add_source(File::with_name(path.to_str().unwrap()))
.add_source(File::new(path.to_str().unwrap(), FileFormat::Yaml))
.build()
.context(format!(
"failed to build configuration from path: {}",
path.to_str().unwrap()
))
.map_err(|e| ctx::CTXError::InvalidConfigurations {
message: format!(
"failed to load configurations, check your configurations ({})",
path.to_str().unwrap()
),
message:
"failed to load configurations, check your configurations (~/.aws/configs.yaml)"
.to_string(),
source: Some(e),
})?;

Expand All @@ -66,16 +65,17 @@ impl Configs {
path.to_str().unwrap()
))
.map_err(|e| ctx::CTXError::InvalidConfigurations {
message: format!(
"failed to deserialize configurations, check your configurations ({})",
path.to_str().unwrap()
),
message: "failed to deserialize configurations, check your configurations (~/.aws/configs.yaml)".to_string(),
source: Some(e),
})
}

pub fn initialize_default_configs() -> Result<Self, ctx::CTXError> {
let path: &PathBuf = &CONFIGS_PATH;
pub fn initialize_default_configs<P: AsRef<Path>>(
path: Option<P>,
) -> Result<Self, ctx::CTXError> {
let path = path
.map(|p| p.as_ref().to_path_buf())
.unwrap_or_else(|| CONFIGS_PATH.clone());
if path.exists() {
return Self::load_configs(Some(path));
}
Expand All @@ -87,7 +87,7 @@ impl Configs {
None => (),
}
let c = Configs::default();
let mut file = fs::File::create(path)
let mut file = fs::File::create(&path)
.context("failed to create a configuration file")
.map_err(|e| ctx::CTXError::UnexpectedError { source: Some(e) })?;
file.write_all(CONFIGS_DESCRIPTIONS.as_bytes())
Expand All @@ -104,3 +104,123 @@ impl Configs {
Self::load_configs(Some(path))
}
}

#[cfg(test)]
mod tests {
use std::io::{Seek, SeekFrom};

use rstest::*;
use tempfile::{NamedTempFile, TempDir};

use super::*;

#[fixture]
pub fn configs_text() -> String {
r#"# # Configurations for awsctx
# # You can manually edit configurations according to the following usage
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
# auth_commands:
# # configuration for `foo` profile with aws configure
# foo: |
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
# # In this case, `{{profile}}` is replaced by `foo`
# aws configure --profile {{profile}}
# # configuration for `bar` profile with [onelogin-aws-cli](https://github.com/physera/onelogin-aws-cli)
# bar: |
# # In this case, name of one-login configuration is same as `profile`
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u user@example.com
---
auth_commands:
foo: |
echo 1"#
.to_string()
}

#[fixture]
pub fn configs_file(configs_text: String) -> NamedTempFile {
let mut f = NamedTempFile::new().unwrap();
write!(f, "{}", configs_text).unwrap();
f.flush().unwrap();
f.seek(SeekFrom::Start(0)).unwrap();
f
}

#[fixture]
pub fn configs() -> Configs {
Configs {
auth_commands: vec![("foo".to_string(), "echo 1".to_string())]
.into_iter()
.collect::<HashMap<String, String>>(),
}
}

#[rstest(input, expect)]
#[case(configs_file(configs_text()), Ok(configs()))]
#[case(
configs_file("invalid_yaml_format: a:a:".to_string()),
Err(
ctx::CTXError::InvalidConfigurations {
message: "failed to load configurations, check your configurations (~/.aws/configs.yaml)".to_string(),
source: None
}
)
)]
#[case(
configs_file("unknown_key: foo".to_string()),
Err(
ctx::CTXError::InvalidConfigurations {
message: "failed to deserialize configurations, check your configurations (~/.aws/configs.yaml)".to_string(),
source: None
}
)
)]
fn test_configs_load_configs(input: NamedTempFile, expect: Result<Configs, ctx::CTXError>) {
let actual = Configs::load_configs(Some(input.path()));
match (expect, actual) {
(Ok(expect), Ok(actual)) => {
assert_eq!(expect, actual);
}
(Err(expect), Err(actual)) => match (&expect, &actual) {
(
ctx::CTXError::InvalidConfigurations {
message: expect_message,
source: _,
},
ctx::CTXError::InvalidConfigurations {
message: actual_message,
source: _,
},
) => assert_eq!(expect_message, actual_message),
_ => panic!("unexpected error"),
},
_ => panic!("expect and actual are not match"),
}
}

#[rstest]
fn test_initialize_default_configs() {
let tmpdir = TempDir::new().unwrap();
let tmpfile = tmpdir.path().join("configs.yaml");
Configs::initialize_default_configs(Some(&tmpfile)).unwrap();
let expect = r#"# # Configurations for awsctx
# # You can manually edit configurations according to the following usage
# # To use subcommand `auth` or `refresh`, fill the below configs for each profile.
# auth_commands:
# # configuration for `foo` profile with aws configure
# foo: |
# # you can use pre-defined parameter `{{profile}}` which is replaced by key of this block
# # In this case, `{{profile}}` is replaced by `foo`
# aws configure --profile {{profile}}
# # configuration for `bar` profile with [onelogin-aws-cli](https://github.com/physera/onelogin-aws-cli)
# bar: |
# # In this case, name of one-login configuration is same as `profile`
# onelogin-aws-login -C {{profile}} --profile {{profile}} -u user@example.com
---
auth_commands: {}
"#;
let actual = fs::read_to_string(tmpfile).unwrap();
assert_eq!(expect, actual);
}
}
6 changes: 4 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{io, rc::Rc};
use std::{io, path::PathBuf, rc::Rc};

use awsctx::{
aws::{AWS, CREDENTIALS_PATH},
Expand Down Expand Up @@ -89,7 +89,9 @@ fn main() {
)
.unwrap();

let configs = Rc::new(fatal_ctxerr(Configs::initialize_default_configs()));
let configs = Rc::new(fatal_ctxerr(
Configs::initialize_default_configs::<PathBuf>(None),
));
let aws = AWS::new(Rc::clone(&configs), CREDENTIALS_PATH.clone()).unwrap();
let opts = cli.opts.unwrap_or(Opts::UseContextByInteractiveFinder {});
let skim_options = SkimOptionsBuilder::default()
Expand Down

0 comments on commit d0f8f9a

Please sign in to comment.