1use crate::{Error, Result};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum EnvironmentFormat {
6 Yml,
8 Yaml,
10 Properties,
12}
13
14impl EnvironmentFormat {
15 pub(crate) fn suffix(self) -> &'static str {
16 match self {
17 Self::Yml => ".yml",
18 Self::Yaml => ".yaml",
19 Self::Properties => ".properties",
20 }
21 }
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct EnvironmentRequest {
27 application: String,
28 profiles: Vec<String>,
29 label: Option<String>,
30 resolve_placeholders: bool,
31}
32
33impl EnvironmentRequest {
34 pub fn new<A, I, P>(application: A, profiles: I) -> Result<Self>
36 where
37 A: Into<String>,
38 I: IntoIterator<Item = P>,
39 P: Into<String>,
40 {
41 let application = sanitize_required(application.into(), Error::EmptyApplication)?;
42 let profiles = sanitize_profiles(profiles)?;
43
44 Ok(Self {
45 application,
46 profiles,
47 label: None,
48 resolve_placeholders: false,
49 })
50 }
51
52 pub fn label(mut self, label: impl Into<String>) -> Self {
54 let label = label.into().trim().to_string();
55 self.label = if label.is_empty() { None } else { Some(label) };
56 self
57 }
58
59 pub fn resolve_placeholders(mut self, enabled: bool) -> Self {
61 self.resolve_placeholders = enabled;
62 self
63 }
64
65 pub fn application(&self) -> &str {
67 &self.application
68 }
69
70 pub fn profiles(&self) -> &[String] {
72 &self.profiles
73 }
74
75 pub fn label_ref(&self) -> Option<&str> {
77 self.label.as_deref()
78 }
79
80 pub fn resolve_placeholders_enabled(&self) -> bool {
82 self.resolve_placeholders
83 }
84
85 pub(crate) fn joined_profiles(&self) -> String {
86 self.profiles.join(",")
87 }
88}
89
90#[derive(Debug, Clone, PartialEq, Eq)]
92pub struct ResourceRequest {
93 application: String,
94 profiles: Vec<String>,
95 label: Option<String>,
96 path: String,
97}
98
99impl ResourceRequest {
100 pub fn new<A, I, P, R>(application: A, profiles: I, path: R) -> Result<Self>
102 where
103 A: Into<String>,
104 I: IntoIterator<Item = P>,
105 P: Into<String>,
106 R: Into<String>,
107 {
108 let application = sanitize_required(application.into(), Error::EmptyApplication)?;
109 let profiles = sanitize_profiles(profiles)?;
110 let path = sanitize_required(
111 normalize_resource_path(path.into()),
112 Error::EmptyResourcePath,
113 )?;
114
115 Ok(Self {
116 application,
117 profiles,
118 label: None,
119 path,
120 })
121 }
122
123 pub fn label(mut self, label: impl Into<String>) -> Self {
125 let label = label.into().trim().to_string();
126 self.label = if label.is_empty() { None } else { Some(label) };
127 self
128 }
129
130 pub fn application(&self) -> &str {
132 &self.application
133 }
134
135 pub fn profiles(&self) -> &[String] {
137 &self.profiles
138 }
139
140 pub fn label_ref(&self) -> Option<&str> {
142 self.label.as_deref()
143 }
144
145 pub fn path(&self) -> &str {
147 &self.path
148 }
149
150 pub(crate) fn joined_profiles(&self) -> String {
151 self.profiles.join(",")
152 }
153
154 pub(crate) fn path_segments(&self) -> Vec<String> {
155 self.path
156 .split('/')
157 .filter(|segment| !segment.is_empty())
158 .map(ToOwned::to_owned)
159 .collect()
160 }
161}
162
163fn sanitize_profiles<I, P>(profiles: I) -> Result<Vec<String>>
164where
165 I: IntoIterator<Item = P>,
166 P: Into<String>,
167{
168 let sanitized: Vec<String> = profiles
169 .into_iter()
170 .map(Into::into)
171 .map(|profile| profile.trim().to_string())
172 .filter(|profile| !profile.is_empty())
173 .collect();
174
175 if sanitized.is_empty() {
176 Err(Error::EmptyProfiles)
177 } else {
178 Ok(sanitized)
179 }
180}
181
182fn sanitize_required(value: String, error: Error) -> Result<String> {
183 let value = value.trim().to_string();
184 if value.is_empty() {
185 Err(error)
186 } else {
187 Ok(value)
188 }
189}
190
191fn normalize_resource_path(path: String) -> String {
192 path.replace('\\', "/").trim_matches('/').to_string()
193}