First commit, first part of the tutorial
This commit is contained in:
commit
6cb42ec21b
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/target
|
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
[package]
|
||||||
|
name = "sqlite-parser"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Timothy Warren <tim@timshomepage.net>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
BIN
data.sqlite
Normal file
BIN
data.sqlite
Normal file
Binary file not shown.
24
src/error.rs
Normal file
24
src/error.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Representation of our possible errors.
|
||||||
|
/// Each variant will contain a string for more
|
||||||
|
/// detailed information
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// An error related to the first 16 bytes
|
||||||
|
HeaderString(String),
|
||||||
|
/// An error parsing the page size
|
||||||
|
InvalidPageSize(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
/// This will be called whenever we print
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::HeaderString(v) => write!(f, "Unexpected bytes at start of file, expected the magic string 'SQLite format 3\u{0}', found {:?}", v),
|
||||||
|
Self::InvalidPageSize(msg) => write!(f, "Invalid page size, {}", msg),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error {}
|
61
src/header.rs
Normal file
61
src/header.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
use crate::error::Error;
|
||||||
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
|
static HEADER_STRING: &[u8] = &[
|
||||||
|
//S q l i t e ` ` f o r m a t ` ` 3 \u{0}
|
||||||
|
83, 81, 76, 105, 116, 101, 32, 102, 111, 114, 109, 97, 116, 32, 51, 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
/// This struct will wrap our 32bit number allowing us
|
||||||
|
/// to control how it gets used later.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PageSize(u32);
|
||||||
|
|
||||||
|
impl TryFrom<u16> for PageSize {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(v: u16) -> Result<PageSize, Self::Error> {
|
||||||
|
match v {
|
||||||
|
// Special case for the largest page size
|
||||||
|
1 => Ok(PageSize(65_536u32)),
|
||||||
|
|
||||||
|
// < 512, != 1
|
||||||
|
0 | 2..=511 => Err(Error::InvalidPageSize(format!(
|
||||||
|
"value must be >= 512, found: {}",
|
||||||
|
v
|
||||||
|
))),
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
if v.is_power_of_two() {
|
||||||
|
Ok(PageSize(v as u32))
|
||||||
|
} else {
|
||||||
|
Err(Error::InvalidPageSize(format!(
|
||||||
|
"value must be a power of 2, found: {}",
|
||||||
|
v
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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()));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to generate the appropriate `PageSize` struct
|
||||||
|
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))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
u16::from_be_bytes(page_size_bytes).try_into()
|
||||||
|
}
|
2
src/lib.rs
Normal file
2
src/lib.rs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
pub mod error;
|
||||||
|
pub mod header;
|
19
src/main.rs
Normal file
19
src/main.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use sqlite_parser::{
|
||||||
|
header::{validate_magic_string, parse_page_size},
|
||||||
|
error::Error,
|
||||||
|
};
|
||||||
|
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();
|
||||||
|
|
||||||
|
validate_magic_string(&contents)?;
|
||||||
|
|
||||||
|
let page_size = parse_page_size(&contents)?;
|
||||||
|
|
||||||
|
println!("{:?}", page_size);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user