1mod acpi;
30mod capability;
31mod event_log;
32mod pmt;
33
34use crate::CrashLog;
35use crate::error::Error;
36use acpi::Acpi;
37#[cfg(not(feature = "std"))]
38use alloc::{fmt, str::FromStr, string::String, string::ToString, vec, vec::Vec};
39use event_log::EventLog;
40use pmt::{Pmt, PmtDeviceId};
41#[cfg(feature = "std")]
42use std::{fmt, str::FromStr};
43
44pub use capability::{Capabilities, Capability};
45
46#[derive(Debug, Clone, Eq, Ord, PartialEq, PartialOrd)]
51pub enum CrashLogSource {
52 Acpi,
54 PmtDevice(PmtDeviceId),
56 EventLog,
58}
59
60impl fmt::Display for CrashLogSource {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 match self {
63 Self::Acpi => write!(f, "acpi"),
64 Self::PmtDevice(dev) => write!(f, "pmt:{}", dev),
65 Self::EventLog => write!(f, "evt"),
66 }
67 }
68}
69
70#[derive(Debug, PartialEq)]
75pub enum ParseCrashLogSourceError {
76 InvalidSource,
80 InvalidParameter,
87}
88
89impl fmt::Display for ParseCrashLogSourceError {
90 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
91 match self {
92 Self::InvalidSource => write!(f, "Invalid source"),
93 Self::InvalidParameter => write!(f, "Invalid parameter"),
94 }
95 }
96}
97
98#[cfg(feature = "std")]
99impl std::error::Error for ParseCrashLogSourceError {}
100
101impl FromStr for CrashLogSource {
102 type Err = ParseCrashLogSourceError;
103
104 fn from_str(s: &str) -> Result<Self, Self::Err> {
105 let (source, parameter) = s.split_once(":").unwrap_or((s, ""));
106
107 match source {
108 "acpi" => {
109 if parameter.is_empty() {
110 Ok(Self::Acpi)
111 } else {
112 Err(Self::Err::InvalidParameter)
113 }
114 }
115 "pmt" => {
116 if let Ok(dev) = parameter.parse() {
117 Ok(Self::PmtDevice(dev))
118 } else {
119 Err(Self::Err::InvalidParameter)
120 }
121 }
122 "evt" => {
123 if parameter.is_empty() {
124 Ok(Self::EventLog)
125 } else {
126 Err(Self::Err::InvalidParameter)
127 }
128 }
129 _ => Err(Self::Err::InvalidSource),
130 }
131 }
132}
133
134impl CrashLogSource {
135 #[deprecated(
137 since = "1.2.0",
138 note = "use `discover_all_sources` or `discover_distinct` instead"
139 )]
140 pub fn discover() -> Vec<Self> {
141 Self::discover_sources()
142 }
143
144 pub fn discover_all_sources() -> Vec<Self> {
146 Self::discover_sources()
147 }
148
149 pub fn discover_distinct() -> Vec<Self> {
151 Self::discover_sources()
152 .into_iter()
153 .filter(|src| !src.is_alias())
154 .collect()
155 }
156
157 fn discover_sources() -> Vec<Self> {
158 let mut sources = Vec::new();
159
160 if Acpi::default().is_available() {
161 sources.push(Self::Acpi);
162 }
163
164 if EventLog::default().is_available() {
165 sources.push(Self::EventLog);
166 }
167
168 let pmt_devices: Vec<CrashLogSource> = Pmt::default()
169 .discover()
170 .into_iter()
171 .map(Self::PmtDevice)
172 .collect();
173
174 sources.extend(pmt_devices);
175
176 sources
177 }
178
179 fn is_alias(&self) -> bool {
181 match self {
182 Self::PmtDevice(dev) => dev.is_alias(),
183 _ => false,
184 }
185 }
186
187 #[cfg(feature = "extraction")]
189 pub fn extract(&self) -> Result<Vec<CrashLog>, Error> {
190 let mut crashlogs = match self {
191 Self::Acpi => Acpi::default().extract().map(|crashlog| vec![crashlog]),
192 Self::PmtDevice(dev) => Pmt::default().extract(dev),
193 Self::EventLog => EventLog::default().extract(),
194 };
195
196 if let Ok(ref mut crashlogs) = crashlogs {
197 for crashlog in crashlogs.iter_mut() {
198 crashlog.metadata.source = Some(self.clone());
199 }
200 }
201 crashlogs
202 }
203
204 #[cfg(feature = "control_commands")]
206 pub fn trigger(&self) -> Result<(), Error> {
207 match self {
208 Self::PmtDevice(dev) => Pmt::default().trigger(dev),
209 _ => Err(Error::Unsupported),
210 }
211 }
212
213 #[cfg(feature = "control_commands")]
215 pub fn rearm(&self) -> Result<(), Error> {
216 match self {
217 Self::PmtDevice(dev) => Pmt::default().rearm(dev),
218 _ => Err(Error::Unsupported),
219 }
220 }
221
222 #[cfg(feature = "control_commands")]
224 pub fn clear(&self) -> Result<(), Error> {
225 match self {
226 Self::PmtDevice(dev) => Pmt::default().clear(dev),
227 Self::EventLog => EventLog::default().clear(),
228 _ => Err(Error::Unsupported),
229 }
230 }
231
232 #[cfg(feature = "control_commands")]
234 pub fn enable(&self) -> Result<(), Error> {
235 match self {
236 Self::PmtDevice(dev) => Pmt::default().enable_disable(dev, true),
237 _ => Err(Error::Unsupported),
238 }
239 }
240
241 #[cfg(feature = "control_commands")]
243 pub fn disable(&self) -> Result<(), Error> {
244 match self {
245 Self::PmtDevice(dev) => Pmt::default().enable_disable(dev, false),
246 _ => Err(Error::Unsupported),
247 }
248 }
249
250 pub fn description(&self) -> String {
252 match &self {
253 Self::Acpi => "ACPI BERT".to_string(),
254 Self::EventLog => "Windows Event Log".to_string(),
255 Self::PmtDevice(dev) => Pmt::default().description(dev),
256 }
257 }
258
259 pub fn capabilities(&self) -> Capabilities {
261 match self {
262 Self::Acpi => Capabilities::from([Capability::Extract]),
263 Self::EventLog => Capabilities::from([Capability::Extract, Capability::Clear]),
264 Self::PmtDevice(dev) => Pmt::default().capabilities(dev),
265 }
266 }
267}
268
269#[cfg(test)]
270mod tests {
271 use super::pmt::PciBdf;
272 use super::*;
273
274 #[test]
275 fn parse() {
276 assert_eq!("acpi".parse(), Ok(CrashLogSource::Acpi));
277 assert_eq!(
278 "pmt:crashlog42".parse(),
279 Ok(CrashLogSource::PmtDevice(PmtDeviceId::Name(
280 "crashlog42".to_string()
281 )))
282 );
283 assert_eq!(
284 "pmt:1111:22:33.4".parse(),
285 Ok(CrashLogSource::PmtDevice(PmtDeviceId::Bdf(PciBdf::new(
286 0x1111, 0x22, 0x33, 0x4
287 ))))
288 );
289 assert_eq!("evt".parse(), Ok(CrashLogSource::EventLog));
290 assert_eq!(
291 "foo".parse::<CrashLogSource>(),
292 Err(ParseCrashLogSourceError::InvalidSource)
293 );
294 assert_eq!(
295 "acpi:foo".parse::<CrashLogSource>(),
296 Err(ParseCrashLogSourceError::InvalidParameter)
297 );
298 }
299}