1pub mod enums;
34pub mod error;
35pub mod model;
36pub mod runtime;
37pub mod writer;
38
39pub use enums::{ContentType, LabelName, LinkType, ParameterMode, Severity, Stage, Status};
41pub use error::{AllureError, AllureResult};
42pub use model::{
43 Attachment, Category, FixtureResult, Label, Link, Parameter, StatusDetails, StepResult,
44 TestResult, TestResultContainer,
45};
46pub use runtime::{
47 allure_id, attach_binary, attach_file, attach_json, attach_text, configure, description,
48 description_html, display_name, epic, feature, flaky, issue, known_issue, label, link,
49 log_step, muted, owner, parameter, parent_suite, run_test, severity, step, story, sub_suite,
50 suite, tag, tags, test_case_id, title, tms, with_context, with_test_context, AllureConfig,
51 AllureConfigBuilder, TestContext,
52};
53pub use writer::{compute_history_id, generate_uuid, AllureWriter, DEFAULT_RESULTS_DIR};
54
55#[cfg(feature = "async")]
57pub use futures;
58
59pub mod bdd {
61 use crate::runtime::step;
62
63 pub fn given<F, R>(description: impl Into<String>, body: F) -> R
65 where
66 F: FnOnce() -> R,
67 {
68 step(format!("Given {}", description.into()), body)
69 }
70
71 pub fn when<F, R>(description: impl Into<String>, body: F) -> R
73 where
74 F: FnOnce() -> R,
75 {
76 step(format!("When {}", description.into()), body)
77 }
78
79 pub fn then<F, R>(description: impl Into<String>, body: F) -> R
81 where
82 F: FnOnce() -> R,
83 {
84 step(format!("Then {}", description.into()), body)
85 }
86
87 pub fn and<F, R>(description: impl Into<String>, body: F) -> R
89 where
90 F: FnOnce() -> R,
91 {
92 step(format!("And {}", description.into()), body)
93 }
94
95 pub fn but<F, R>(description: impl Into<String>, body: F) -> R
97 where
98 F: FnOnce() -> R,
99 {
100 step(format!("But {}", description.into()), body)
101 }
102}
103
104pub mod attachment {
106 use crate::enums::ContentType;
107 use crate::runtime::{attach_binary, attach_file as attach_file_fn, attach_json, attach_text};
108
109 pub fn text(name: impl Into<String>, content: impl AsRef<str>) {
111 attach_text(name, content);
112 }
113
114 pub fn json<T: serde::Serialize>(name: impl Into<String>, value: &T) {
116 attach_json(name, value);
117 }
118
119 pub fn binary(name: impl Into<String>, content: &[u8], content_type: ContentType) {
121 attach_binary(name, content, content_type);
122 }
123
124 pub fn file(
126 name: impl Into<String>,
127 path: impl AsRef<std::path::Path>,
128 content_type: Option<ContentType>,
129 ) {
130 attach_file_fn(name, path, content_type);
131 }
132
133 pub fn png(name: impl Into<String>, content: &[u8]) {
135 attach_binary(name, content, ContentType::Png);
136 }
137
138 pub fn jpeg(name: impl Into<String>, content: &[u8]) {
140 attach_binary(name, content, ContentType::Jpeg);
141 }
142
143 pub fn html(name: impl Into<String>, content: impl AsRef<str>) {
145 attach_binary(name, content.as_ref().as_bytes(), ContentType::Html);
146 }
147
148 pub fn xml(name: impl Into<String>, content: impl AsRef<str>) {
150 attach_binary(name, content.as_ref().as_bytes(), ContentType::Xml);
151 }
152
153 pub fn csv(name: impl Into<String>, content: impl AsRef<str>) {
155 attach_binary(name, content.as_ref().as_bytes(), ContentType::Csv);
156 }
157}
158
159pub struct EnvironmentBuilder {
161 properties: Vec<(String, String)>,
162 results_dir: String,
163}
164
165impl EnvironmentBuilder {
166 pub fn new() -> Self {
168 Self {
169 properties: Vec::new(),
170 results_dir: DEFAULT_RESULTS_DIR.to_string(),
171 }
172 }
173
174 pub fn results_dir(mut self, path: impl Into<String>) -> Self {
176 self.results_dir = path.into();
177 self
178 }
179
180 pub fn set(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
182 self.properties.push((key.into(), value.into()));
183 self
184 }
185
186 pub fn set_from_env(mut self, key: impl Into<String>, env_var: &str) -> Self {
188 if let Ok(value) = std::env::var(env_var) {
189 self.properties.push((key.into(), value));
190 }
191 self
192 }
193
194 pub fn write(self) -> std::io::Result<std::path::PathBuf> {
196 let writer = AllureWriter::with_results_dir(&self.results_dir);
197 writer.write_environment(&self.properties)
198 }
199}
200
201impl Default for EnvironmentBuilder {
202 fn default() -> Self {
203 Self::new()
204 }
205}
206
207pub fn environment() -> EnvironmentBuilder {
209 EnvironmentBuilder::new()
210}
211
212pub struct CategoriesBuilder {
214 categories: Vec<Category>,
215 results_dir: String,
216}
217
218impl CategoriesBuilder {
219 pub fn new() -> Self {
221 Self {
222 categories: Vec::new(),
223 results_dir: DEFAULT_RESULTS_DIR.to_string(),
224 }
225 }
226
227 pub fn results_dir(mut self, path: impl Into<String>) -> Self {
229 self.results_dir = path.into();
230 self
231 }
232
233 pub fn with_category(mut self, category: Category) -> Self {
235 self.categories.push(category);
236 self
237 }
238
239 pub fn with_product_defects(mut self) -> Self {
241 self.categories
242 .push(Category::new("Product defects").with_status(Status::Failed));
243 self
244 }
245
246 pub fn with_test_defects(mut self) -> Self {
248 self.categories
249 .push(Category::new("Test defects").with_status(Status::Broken));
250 self
251 }
252
253 pub fn write(self) -> std::io::Result<std::path::PathBuf> {
255 let writer = AllureWriter::with_results_dir(&self.results_dir);
256 writer.write_categories(&self.categories)
257 }
258}
259
260impl Default for CategoriesBuilder {
261 fn default() -> Self {
262 Self::new()
263 }
264}
265
266pub fn categories() -> CategoriesBuilder {
268 CategoriesBuilder::new()
269}
270
271#[cfg(test)]
272mod tests {
273 use super::*;
274
275 #[test]
276 fn test_bdd_step_names() {
277 let result = bdd::given("a value", || 42);
279 assert_eq!(result, 42);
280
281 let result = bdd::when("something happens", || "ok");
282 assert_eq!(result, "ok");
283
284 let result = bdd::then("we check", || true);
285 assert!(result);
286 }
287
288 #[test]
289 fn test_environment_builder() {
290 let builder = environment().set("key1", "value1").set("key2", "value2");
291
292 assert_eq!(builder.properties.len(), 2);
293 }
294
295 #[test]
296 fn test_categories_builder() {
297 let builder = categories()
298 .with_product_defects()
299 .with_test_defects()
300 .with_category(Category::new("Custom").with_status(Status::Skipped));
301
302 assert_eq!(builder.categories.len(), 3);
303 }
304}