Encapsulate header parsing logic
This commit is contained in:
parent
6cb42ec21b
commit
2c96172e9d
@ -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,7 +92,7 @@ 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()
|
||||
|
11
src/main.rs
11
src/main.rs
@ -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(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user