starship/modules/
openstack.rs1use yaml_rust2::YamlLoader;
2
3use super::{Context, Module, ModuleConfig};
4
5use crate::configs::openstack::OspConfig;
6use crate::formatter::StringFormatter;
7use crate::utils;
8
9type Cloud = String;
10type Project = String;
11
12fn get_osp_project_from_config(context: &Context, osp_cloud: &str) -> Option<Project> {
13 let config = [
16 context.get_env("PWD").map(|pwd| pwd + "/clouds.yaml"),
17 context.get_home().map(|home| {
18 home.join(".config/openstack/clouds.yaml")
19 .display()
20 .to_string()
21 }),
22 Some(String::from("/etc/openstack/clouds.yaml")),
23 ];
24
25 config
26 .iter()
27 .filter_map(|file| {
28 let config = utils::read_file(file.as_ref()?).ok()?;
29 let clouds = YamlLoader::load_from_str(config.as_str()).ok()?;
30 clouds.first()?["clouds"][osp_cloud]["auth"]["project_name"]
31 .as_str()
32 .map(ToOwned::to_owned)
33 })
34 .find(|s| !s.is_empty())
35}
36
37fn get_osp_cloud_and_project(context: &Context) -> (Option<Cloud>, Option<Project>) {
38 match (
39 context.get_env("OS_CLOUD"),
40 context.get_env("OS_PROJECT_NAME"),
41 ) {
42 (Some(p), Some(r)) => (Some(p), Some(r)),
43 (None, Some(r)) => (None, Some(r)),
44 (Some(ref p), None) => (Some(p.clone()), get_osp_project_from_config(context, p)),
45 (None, None) => (None, None),
46 }
47}
48
49pub fn module<'a>(context: &'a Context) -> Option<Module<'a>> {
50 let mut module = context.new_module("openstack");
51 let config: OspConfig = OspConfig::try_load(module.config);
52
53 let (osp_cloud, osp_project) = get_osp_cloud_and_project(context);
54
55 osp_cloud.as_ref()?;
56
57 let parsed = StringFormatter::new(config.format).and_then(|formatter| {
58 formatter
59 .map_meta(|variable, _| match variable {
60 "symbol" => Some(config.symbol),
61 _ => None,
62 })
63 .map_style(|variable| match variable {
64 "style" => Some(Ok(config.style)),
65 _ => None,
66 })
67 .map(|variable| match variable {
68 "cloud" => osp_cloud.as_ref().map(Ok),
69 "project" => osp_project.as_ref().map(Ok),
70 _ => None,
71 })
72 .parse(None, Some(context))
73 });
74
75 module.set_segments(match parsed {
76 Ok(segments) => segments,
77 Err(error) => {
78 log::error!("Error in module `openstack`: \n{}", error);
79 return None;
80 }
81 });
82
83 Some(module)
84}
85
86#[cfg(test)]
87mod tests {
88 use crate::test::ModuleRenderer;
89 use nu_ansi_term::Color;
90 use std::fs::File;
91 use std::io::{self, Write};
92
93 #[test]
94 fn parse_valid_config() -> io::Result<()> {
95 let dir = tempfile::tempdir()?;
96 let config_path = dir.path().join("clouds.yaml");
97 let mut file = File::create(config_path)?;
98 file.write_all(
99 b"---
100clouds:
101 corp:
102 auth:
103 auth_url: https://overcloud.example.com:13000/v3
104 project_name: testproject
105 identity_api_version: '3'
106 interface: public
107",
108 )?;
109 let actual = ModuleRenderer::new("openstack")
110 .env("PWD", dir.path().to_str().unwrap())
111 .env("OS_CLOUD", "corp")
112 .config(toml::toml! {
113 [openstack]
114 })
115 .collect();
116 let expected = Some(format!(
117 "on {} ",
118 Color::Yellow.bold().paint("☁️ corp(testproject)")
119 ));
120
121 assert_eq!(actual, expected);
122 dir.close()
123 }
124
125 #[test]
126 fn parse_broken_config() -> io::Result<()> {
127 let dir = tempfile::tempdir()?;
128 let config_path = dir.path().join("clouds.yaml");
129 let mut file = File::create(config_path)?;
130 file.write_all(
131 b"---
132dummy_yaml
133",
134 )?;
135 let actual = ModuleRenderer::new("openstack")
136 .env("PWD", dir.path().to_str().unwrap())
137 .env("OS_CLOUD", "test")
138 .config(toml::toml! {
139 [openstack]
140 })
141 .collect();
142 let expected = Some(format!("on {} ", Color::Yellow.bold().paint("☁️ test")));
143
144 assert_eq!(actual, expected);
145 dir.close()
146 }
147
148 #[test]
149 fn dont_crash_on_empty_config() -> io::Result<()> {
150 let dir = tempfile::tempdir()?;
151 let config_path = dir.path().join("clouds.yaml");
152 let mut file = File::create(config_path)?;
153 file.write_all(b"")?;
154 drop(file);
155 let actual = ModuleRenderer::new("openstack")
156 .env("PWD", dir.path().to_str().unwrap())
157 .env("OS_CLOUD", "test")
158 .config(toml::toml! {
159 [openstack]
160 })
161 .collect();
162 let expected = Some(format!("on {} ", Color::Yellow.bold().paint("☁️ test")));
163
164 assert_eq!(actual, expected);
165 dir.close()
166 }
167}