Encapsulate header parsing logic

This commit is contained in:
Timothy Warren 2020-12-01 09:32:18 -05:00
parent 6cb42ec21b
commit 2c96172e9d
4 changed files with 48 additions and 11 deletions

View File

@ -21,4 +21,4 @@ impl fmt::Display for Error {
}
}
impl std::error::Error for Error {}
impl std::error::Error for Error {}

View File

@ -6,6 +6,29 @@ static HEADER_STRING: &[u8] = &[
83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0,
];
/// A value stored as a Write Format Verson or
/// Read Format Version
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum FormatVersion {
/// Represents the rollback journal mode
Legacy,
/// Represents the Write Ahead Log mode
WriteAheadLog,
/// Represents any mode not 1 or 2, the value
/// will be provided
Unknown(u8),
}
impl From<u8> for FormatVersion {
fn from(v: u8) -> Self {
match v {
1 => Self::Legacy,
2 => Self::WriteAheadLog,
_ => Self::Unknown(v),
}
}
}
/// This struct will wrap our 32bit number allowing us
/// to control how it gets used later.
#[derive(Debug)]
@ -39,12 +62,27 @@ impl TryFrom<u16> for PageSize {
}
}
pub fn parse_header(bytes: &[u8]) -> Result<(PageSize, FormatVersion, FormatVersion), Error> {
// Check that the first 16 bytes match the header string
validate_magic_string(&bytes)?;
// capture the page size
let page_size = parse_page_size(bytes)?;
// capture the write format version
let write_version = FormatVersion::from(bytes[18]);
// capture the read format version
let read_version = FormatVersion::from(bytes[19]);
Ok((page_size, write_version, read_version))
}
/// Validate that the bytes provided match the special string
/// at the start of Sqlite3 files
pub fn validate_magic_string(bytes: &[u8]) -> Result<(), Error> {
let buf = &bytes[0..16];
if buf != HEADER_STRING {
return Err(Error::HeaderString(String::from_utf8_lossy(buf).to_string()));
return Err(Error::HeaderString(
String::from_utf8_lossy(buf).to_string(),
));
}
Ok(())
@ -54,8 +92,8 @@ pub fn validate_magic_string(bytes: &[u8]) -> Result<(), Error> {
pub fn parse_page_size(bytes: &[u8]) -> Result<PageSize, Error> {
// Convert into array, and convert error if needed, so that the `?` operator works
let page_size_bytes: [u8; 2] = bytes[16..18].try_into().map_err(|_| {
Error::InvalidPageSize(format!("expected a 2 byte slice, found: {:?}", bytes))
Error::InvalidPageSize(format!("expected a 2 byte slice, found: {:?}", bytes))
})?;
u16::from_be_bytes(page_size_bytes).try_into()
}
}

View File

@ -1,2 +1,2 @@
pub mod error;
pub mod header;
pub mod header;

View File

@ -1,5 +1,5 @@
use sqlite_parser::{
header::{validate_magic_string, parse_page_size},
header::parse_header,
error::Error,
};
use std::fs::read;
@ -7,13 +7,12 @@ use std::fs::read;
fn main() -> Result<(), Error> {
// first, read in all the bytes of our file
// using unwrap to just panic if this fails
let contents = read("data.sqlite").unwrap();
let contents = read("data.sqlite")
.expect("Failed to read data.sqlite");
validate_magic_string(&contents)?;
let (page_size, write_format, read_format) = parse_header(&contents[0..100])?;
let page_size = parse_page_size(&contents)?;
println!("{:?}", page_size);
println!("page_size {:?}, write_format {:?}, read_format {:?}", page_size, write_format, read_format);
Ok(())
}