1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use crateFormat;
use crateGenericFinding;
use ;
use Write;
/// Render ANY type that implements [`Reportable`] into the given format.
///
/// This is the most flexible rendering function - it works with any type that
/// implements the `Reportable` trait from the `secfinding` crate. For rendering
/// native [`Finding`] types, see [`render`].
///
/// # Parameters
///
/// - `findings`: A slice of findings that implement `Reportable`
/// - `format`: The output format to use
/// - `tool_name`: Name of the tool generating the report (used in SARIF/Markdown headers)
///
/// # Returns
///
/// - `Ok(String)` containing the rendered output
/// - `Err(serde_json::Error)` if conversion or serialization fails
///
/// # Example
///
/// ```
/// use secfinding::{Finding, Severity};
/// use secreport::format::Format;
/// use secreport::render::render_any;
///
/// let finding = Finding::builder("my-scanner", "example.com", Severity::High)
/// .title("Test Finding")
/// .build()
/// .unwrap();
///
/// let output = render_any(&[finding], Format::Json, "my-scanner").unwrap();
/// assert!(output.contains("Test Finding"));
/// ```
/// Render native [`Finding`] types in the given format.
///
/// This is a convenience wrapper around [`render_any`] specifically for
/// the native `Finding` type from the `secfinding` crate.
///
/// # Parameters
///
/// - `findings`: A slice of [`Finding`] objects
/// - `format`: The output format to use
/// - `tool_name`: Name of the tool generating the report
///
/// # Returns
///
/// - `Ok(String)` containing the rendered output
/// - `Err(serde_json::Error)` if serialization fails
///
/// # Example
///
/// ```
/// use secfinding::{Finding, Severity};
/// use secreport::format::Format;
/// use secreport::render::render;
///
/// let findings = vec![
/// Finding::builder("scanner", "target.com", Severity::Critical)
/// .title("Critical Vulnerability")
/// .build()
/// .unwrap(),
/// ];
///
/// let markdown = render(&findings, Format::Markdown, "security-scanner").unwrap();
/// assert!(markdown.contains("Critical Vulnerability"));
/// ```
/// Write rendered output to a writer.
///
/// This function writes the rendered content to any type implementing
/// `std::io::Write`, such as files or stdout.
///
/// # Parameters
///
/// - `content`: The rendered content to write
/// - `writer`: Any type implementing `Write`
///
/// # Returns
///
/// - `Ok(())` on success
/// - `Err(std::io::Error)` if writing fails
///
/// # Example
///
/// ```
/// use secreport::render::emit;
///
/// let content = "Security Report\n===============";
/// let mut output = Vec::new();
///
/// emit(content, &mut output).unwrap();
/// assert_eq!(String::from_utf8(output).unwrap(), content);
/// ```
///
/// Writing to stdout:
///
/// ```
/// use secreport::render::emit;
/// use std::io;
///
/// # fn example() -> io::Result<()> {
/// emit("Report complete!\n", io::stdout())?;
/// # Ok(())
/// # }
/// ```