1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use super::traits::{Name, ParseBytes};
use crate::{
  field_parsing_error, impl_name,
  result::{SQLiteError, SQLiteResult},
};
use core::fmt::Display;

/// # File format version numbers (2 Bytes)
///
///  The file format write version and file format read version at offsets 18
/// and 19 are intended to allow for enhancements of the file format in future
/// versions of SQLite. In current versions of SQLite, both of these values
/// are:
///   - `1` for rollback journalling modes; and
///   - `2` for WAL journalling mode.
///
///  If a version of SQLite coded to the current file format specification
/// encounters a database file where the read version is 1 or 2 but the write
/// version is greater than 2, then the database file must be treated as
/// read-only. If a database file with a read version greater than 2 is
/// encountered, then that database cannot be read or written.
#[derive(Debug)]
pub struct FileFormatVersionNumbers {
  /// File format write version. 1 for legacy; 2 for WAL.
  write_version: FileFormatWriteVersion,
  /// File format read version. 1 for legacy; 2 for WAL.
  read_version: FileFormatReadVersion,
}

impl FileFormatVersionNumbers {
  pub fn write_version(&self) -> &FileFormatWriteVersion {
    &self.write_version
  }

  pub fn read_version(&self) -> &FileFormatReadVersion {
    &self.read_version
  }
}
impl_name! {FileFormatVersionNumbers}
impl ParseBytes for FileFormatVersionNumbers {
  const LENGTH_BYTES: usize = 2;

  fn parsing_handler(bytes: &[u8]) -> SQLiteResult<Self> {
    let write_version = FileFormatWriteVersion::parsing_handler(&[bytes[0]])?;
    let read_version = FileFormatReadVersion::parsing_handler(&[bytes[1]])?;
    Ok(Self {
      write_version,
      read_version,
    })
  }
}

#[derive(Debug)]
pub enum FileFormatWriteVersion {
  Legacy,
  /// Write-Ahead Log
  ///
  /// Reference: https://www.sqlite.org/wal.html
  WAL,
}

impl From<&FileFormatWriteVersion> for u8 {
  fn from(value: &FileFormatWriteVersion) -> Self {
    match value {
      FileFormatWriteVersion::Legacy => 1,
      FileFormatWriteVersion::WAL => 2,
    }
  }
}

impl_name! {FileFormatWriteVersion}

impl ParseBytes for FileFormatWriteVersion {
  const LENGTH_BYTES: usize = 1;

  fn parsing_handler(bytes: &[u8]) -> SQLiteResult<Self> {
    let one_byte = *bytes.first().ok_or(field_parsing_error! {Self::NAME})?;
    match one_byte {
      1 => Ok(Self::Legacy),
      2 => Ok(Self::WAL),
      _ => Err(field_parsing_error! {Self::NAME}),
    }
  }
}

impl Display for FileFormatWriteVersion {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    write!(f, "{}", u8::from(self))
  }
}

#[derive(Debug)]
pub enum FileFormatReadVersion {
  Legacy,
  /// Write-Ahead Log
  ///
  /// Reference: https://www.sqlite.org/wal.html
  WAL,
}

impl From<&FileFormatReadVersion> for u8 {
  fn from(value: &FileFormatReadVersion) -> Self {
    match value {
      FileFormatReadVersion::Legacy => 1,
      FileFormatReadVersion::WAL => 2,
    }
  }
}

impl_name! {FileFormatReadVersion}

impl ParseBytes for FileFormatReadVersion {
  const LENGTH_BYTES: usize = 1;

  fn parsing_handler(bytes: &[u8]) -> SQLiteResult<Self> {
    let one_byte = *bytes.first().ok_or(field_parsing_error! {Self::NAME})?;
    match one_byte {
      1 => Ok(Self::Legacy),
      2 => Ok(Self::WAL),
      _ => Err(field_parsing_error! {Self::NAME}),
    }
  }
}

impl Display for FileFormatReadVersion {
  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
    write!(f, "{}", u8::from(self))
  }
}