use crate::VersionFinder;
use regex::bytes::Regex;
use std::str;
use tokio::io::{AsyncRead, AsyncReadExt};
pub(crate) struct UBoot<'a, R: AsyncRead + Unpin> {
buf: &'a mut R,
}
impl<'a, R: AsyncRead + Unpin> UBoot<'a, R> {
pub(crate) fn from_reader(buf: &'a mut R) -> Self {
UBoot { buf }
}
}
#[async_trait::async_trait(?Send)]
impl<'a, R: AsyncRead + Unpin> VersionFinder for UBoot<'a, R> {
async fn get_version(&mut self) -> Option<String> {
let mut buffer = [0; 0x200];
let re = Regex::new(r"U-Boot(?: SPL)? (?P<version>\d+.?\.[^\s]+) \(.*\)").unwrap();
loop {
let n = self.buf.read(&mut buffer).await.ok()?;
if n == 0 {
return None;
}
if let Some(version) = re
.captures(&buffer)
.and_then(|m| m.name("version"))
.and_then(|v| str::from_utf8(v.as_bytes()).ok())
.map(|v| v.to_string())
{
return Some(version);
}
}
}
}
#[cfg(test)]
mod test {
use crate::{version, BinaryKind};
use tokio::io::{AsyncRead, AsyncSeek};
async fn fixture(name: &str) -> impl AsyncRead + AsyncSeek {
use tokio::{fs::File, io::BufReader};
BufReader::new(
File::open(&format!("tests/fixtures/uboot/{}", name))
.await
.unwrap_or_else(|_| panic!("Couldn't open the fixture {}", name)),
)
}
#[tokio::test]
async fn valid() {
for (f, v) in &[
("arm-spl", "2017.11+fslc+ga07698f"),
("arm-u-boot-dtb.img", "2019.04-00014-gc93ced78db"),
] {
assert_eq!(
version(&mut fixture(f).await, BinaryKind::UBoot).await,
Some(v.to_string()),
);
}
}
}