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 pub fn discover() -> Vec<Self> {
137 let mut sources = Vec::new();
138
139 if Acpi::default().is_available() {
140 sources.push(Self::Acpi);
141 }
142
143 if EventLog::default().is_available() {
144 sources.push(Self::EventLog);
145 }
146
147 let pmt_devices: Vec<CrashLogSource> = Pmt::default()
148 .discover()
149 .into_iter()
150 .map(Self::PmtDevice)
151 .collect();
152
153 sources.extend(pmt_devices);
154
155 sources
156 }
157
158 #[cfg(feature = "extraction")]
160 pub fn extract(&self) -> Result<Vec<CrashLog>, Error> {
161 let mut crashlogs = match self {
162 Self::Acpi => Acpi::default().extract().map(|crashlog| vec![crashlog]),
163 Self::PmtDevice(dev) => Pmt::default().extract(dev),
164 Self::EventLog => EventLog::default().extract(),
165 };
166
167 if let Ok(ref mut crashlogs) = crashlogs {
168 for crashlog in crashlogs.iter_mut() {
169 crashlog.metadata.source = Some(self.clone());
170 }
171 }
172
173 crashlogs
174 }
175
176 #[cfg(feature = "control_commands")]
178 pub fn trigger(&self) -> Result<(), Error> {
179 match self {
180 Self::PmtDevice(dev) => Pmt::default().trigger(dev),
181 _ => Err(Error::Unsupported),
182 }
183 }
184
185 #[cfg(feature = "control_commands")]
187 pub fn clear(&self) -> Result<(), Error> {
188 match self {
189 Self::PmtDevice(dev) => Pmt::default().clear(dev),
190 _ => Err(Error::Unsupported),
191 }
192 }
193
194 #[cfg(feature = "control_commands")]
196 pub fn enable(&self) -> Result<(), Error> {
197 match self {
198 Self::PmtDevice(dev) => Pmt::default().enable_disable(dev, true),
199 _ => Err(Error::Unsupported),
200 }
201 }
202
203 #[cfg(feature = "control_commands")]
205 pub fn disable(&self) -> Result<(), Error> {
206 match self {
207 Self::PmtDevice(dev) => Pmt::default().enable_disable(dev, false),
208 _ => Err(Error::Unsupported),
209 }
210 }
211
212 pub fn description(&self) -> String {
214 match &self {
215 Self::Acpi => "ACPI BERT".to_string(),
216 Self::EventLog => "Windows Event Log".to_string(),
217 Self::PmtDevice(dev) => Pmt::default().description(dev),
218 }
219 }
220
221 pub fn capabilities(&self) -> Capabilities {
223 match self {
224 Self::Acpi => Capabilities::from([Capability::Extract]),
225 Self::EventLog => Capabilities::from([Capability::Extract]),
226 Self::PmtDevice(dev) => Pmt::default().capabilities(dev),
227 }
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::pmt::PciBdf;
234 use super::*;
235
236 #[test]
237 fn parse() {
238 assert_eq!("acpi".parse(), Ok(CrashLogSource::Acpi));
239 assert_eq!(
240 "pmt:crashlog42".parse(),
241 Ok(CrashLogSource::PmtDevice(PmtDeviceId::Name(
242 "crashlog42".to_string()
243 )))
244 );
245 assert_eq!(
246 "pmt:1111:22:33.4".parse(),
247 Ok(CrashLogSource::PmtDevice(PmtDeviceId::Bdf(PciBdf::new(
248 0x1111, 0x22, 0x33, 0x4
249 ))))
250 );
251 assert_eq!("evt".parse(), Ok(CrashLogSource::EventLog));
252 assert_eq!(
253 "foo".parse::<CrashLogSource>(),
254 Err(ParseCrashLogSourceError::InvalidSource)
255 );
256 assert_eq!(
257 "acpi:foo".parse::<CrashLogSource>(),
258 Err(ParseCrashLogSourceError::InvalidParameter)
259 );
260 }
261}