1use std::result;
20
21pub mod collector;
22pub mod format;
23mod helper;
24pub mod report;
25
26use collector::{CollectionError, Collector};
27use format::Format;
28use report::{Report, ReportSection};
29
30pub(crate) type Result<T> = result::Result<T, CollectionError>;
31
32#[doc(hidden)]
33pub struct CrateInfo<'a> {
34 pkg_name: &'a str,
35 pkg_version: &'a str,
36 git_hash: Option<&'a str>,
37}
38
39pub struct BugReport<'a> {
43 info: CrateInfo<'a>,
44 collectors: Vec<Box<dyn Collector>>,
45}
46
47impl<'a> BugReport<'a> {
48 #[doc(hidden)]
49 pub fn from_name_and_version(pkg_name: &'a str, pkg_version: &'a str) -> Self {
50 BugReport {
51 info: CrateInfo {
52 pkg_name,
53 pkg_version,
54 git_hash: None,
55 },
56 collectors: vec![],
57 }
58 }
59
60 #[doc(hidden)]
61 pub fn set_git_hash(&mut self, git_hash: Option<&'a str>) {
62 self.info.git_hash = git_hash;
63 }
64
65 pub fn info<C: Collector + 'static>(mut self, collector: C) -> Self {
67 self.collectors.push(Box::new(collector));
68 self
69 }
70
71 fn generate(&mut self) -> Report {
72 let mut sections = vec![];
73
74 for collector in &mut self.collectors {
75 let entry = collector
76 .collect(&self.info)
77 .unwrap_or_else(|e| e.to_entry());
78 sections.push(ReportSection {
79 title: collector.description(),
80 entry,
81 });
82 }
83
84 Report { sections }
85 }
86
87 pub fn format<F: Format>(&mut self) -> String {
89 let mut format = F::default();
90 self.generate().format_as(&mut format)
91 }
92
93 pub fn print<F: Format>(&mut self) {
95 println!("{}", self.format::<F>());
96 }
97}
98
99#[cfg(feature = "git_hash")]
101pub use git_version::git_version;
102
103#[cfg(feature = "git_hash")]
104#[doc(hidden)]
105#[macro_export]
106macro_rules! bugreport_set_git_hash {
107 ($br:ident) => {{
108 let hash = bugreport::git_version!(fallback = "");
109 if !hash.is_empty() {
110 $br.set_git_hash(Some(hash));
111 }
112 }};
113}
114
115#[cfg(not(feature = "git_hash"))]
116#[doc(hidden)]
117#[macro_export]
118macro_rules! bugreport_set_git_hash {
119 ($br:ident) => {};
120}
121
122#[macro_export]
124macro_rules! bugreport {
125 () => {{
126 let mut br = bugreport::BugReport::from_name_and_version(
127 env!("CARGO_PKG_NAME"),
128 env!("CARGO_PKG_VERSION"),
129 );
130 bugreport::bugreport_set_git_hash!(br);
131 br
132 }};
133}
134
135#[cfg(test)]
136mod tests {
137 #[test]
138 #[cfg(feature = "format_markdown")]
139 fn basic() {
140 use super::BugReport;
141 use crate::collector::*;
142 use crate::format::Markdown;
143
144 std::env::set_var("BUGREPORT_TEST", "42");
145
146 let report = BugReport::from_name_and_version("dummy", "0.1")
147 .info(EnvironmentVariables::list(&["BUGREPORT_TEST"]))
148 .format::<Markdown>();
149
150 assert_eq!(
151 report,
152 "#### Environment variables\n\
153 \n\
154 ```bash\n\
155 BUGREPORT_TEST=42\n\
156 ```\n\n"
157 );
158 }
159}