1use std::{fs::File, ops::Range, path::Path};
2
3use fwob_core::{
4 FileInfo, FormatVersion, Key, Maintenance, OwnedFrame, Reader as CoreReader, ReaderBackend,
5 ReaderOptions, Result as CoreResult, Schema, VerificationReport, Writer as CoreWriter,
6 WriterBackend, WriterFactory,
7};
8
9use crate::{CodecSelection, EncodingSelection, Reader, Writer, WriterOptions, FILE_HEADER_LEN};
10
11pub struct ReaderAdapter {
12 reader: Reader<File>,
13}
14
15impl ReaderAdapter {
16 pub fn open(path: impl AsRef<Path>) -> crate::Result<(Self, CompatibleWriterFactory)> {
17 let mut reader = Reader::open(path)?;
18 let header = reader.header().clone();
19 let mut options = WriterOptions::new("");
20 options.page_size = header.page_size;
21 if header.page_count > 0 {
22 let first = reader.read_page_header(0)?;
23 options.codec = first.codec;
24 options.codec_selection = CodecSelection::Fixed(first.codec);
25 options.encoding = first.encoding;
26 options.encoding_selection = EncodingSelection::Fixed(first.encoding);
27 }
28 Ok((
29 Self { reader },
30 CompatibleWriterFactory {
31 schema: header.schema,
32 options,
33 },
34 ))
35 }
36}
37
38impl FileInfo for ReaderAdapter {
39 fn format_version(&self) -> FormatVersion {
40 FormatVersion::V2
41 }
42
43 fn schema(&self) -> &Schema {
44 &self.reader.header().schema
45 }
46
47 fn title(&self) -> &str {
48 &self.reader.header().title
49 }
50
51 fn frame_count(&self) -> u64 {
52 self.reader.header().frame_count
53 }
54
55 fn string_table(&self) -> &[String] {
56 &self.reader.header().string_table
57 }
58}
59
60impl ReaderBackend for ReaderAdapter {
61 fn read_frame(&mut self, index: u64) -> CoreResult<Option<OwnedFrame>> {
62 self.reader
63 .read_frame_at(index)
64 .map_err(fwob_core::FwobError::backend)
65 }
66
67 fn read_key(&mut self, index: u64) -> CoreResult<Option<Key>> {
68 self.reader
69 .read_key_at(index)
70 .map_err(fwob_core::FwobError::backend)
71 }
72
73 fn first_frame(&mut self) -> CoreResult<Option<OwnedFrame>> {
74 self.reader
75 .first_frame()
76 .map_err(fwob_core::FwobError::backend)
77 }
78
79 fn last_frame(&mut self) -> CoreResult<Option<OwnedFrame>> {
80 self.reader
81 .last_frame()
82 .map_err(fwob_core::FwobError::backend)
83 }
84
85 fn first_key(&mut self) -> CoreResult<Option<Key>> {
86 self.reader
87 .first_key()
88 .map_err(fwob_core::FwobError::backend)
89 }
90
91 fn last_key(&mut self) -> CoreResult<Option<Key>> {
92 self.reader
93 .last_key()
94 .map_err(fwob_core::FwobError::backend)
95 }
96
97 fn lower_bound(&mut self, key: Key) -> CoreResult<u64> {
98 self.reader
99 .lower_bound(key)
100 .map_err(fwob_core::FwobError::backend)
101 }
102
103 fn upper_bound(&mut self, key: Key) -> CoreResult<u64> {
104 self.reader
105 .upper_bound(key)
106 .map_err(fwob_core::FwobError::backend)
107 }
108
109 fn equal_range(&mut self, key: Key) -> CoreResult<Range<u64>> {
110 let (start, end) = self
111 .reader
112 .equal_range(key)
113 .map_err(fwob_core::FwobError::backend)?;
114 Ok(start..end)
115 }
116}
117
118pub struct CompatibleWriterFactory {
119 schema: Schema,
120 options: WriterOptions,
121}
122
123impl WriterFactory for CompatibleWriterFactory {
124 fn create(
125 &mut self,
126 path: &Path,
127 title: &str,
128 string_table: &[String],
129 ) -> CoreResult<CoreWriter> {
130 let mut options = self.options.clone();
131 options.title = title.to_owned();
132 options.string_table = string_table.to_vec();
133 create_writer(path, self.schema.clone(), options).map_err(fwob_core::FwobError::backend)
134 }
135}
136
137pub struct WriterAdapter {
138 writer: Writer<File>,
139}
140
141impl WriterAdapter {
142 pub fn create(
143 path: impl AsRef<Path>,
144 schema: Schema,
145 options: WriterOptions,
146 ) -> crate::Result<Self> {
147 Ok(Self {
148 writer: Writer::create(path, schema, options)?,
149 })
150 }
151
152 pub fn open_append(path: impl AsRef<Path>, options: WriterOptions) -> crate::Result<Self> {
153 Ok(Self {
154 writer: Writer::open_append(path, options)?,
155 })
156 }
157}
158
159impl FileInfo for WriterAdapter {
160 fn format_version(&self) -> FormatVersion {
161 FormatVersion::V2
162 }
163
164 fn schema(&self) -> &Schema {
165 self.writer.schema()
166 }
167
168 fn title(&self) -> &str {
169 &self.writer.header().title
170 }
171
172 fn frame_count(&self) -> u64 {
173 self.writer.frame_count()
174 }
175
176 fn string_table(&self) -> &[String] {
177 &self.writer.header().string_table
178 }
179}
180
181impl WriterBackend for WriterAdapter {
182 fn append_frame(&mut self, frame: &[u8]) -> CoreResult<()> {
183 self.writer
184 .append_frame(frame)
185 .map_err(fwob_core::FwobError::backend)
186 }
187
188 fn append_presorted_frames(&mut self, frames: &[u8]) -> CoreResult<()> {
189 self.writer
190 .append_presorted_raw_frames(frames)
191 .map_err(fwob_core::FwobError::backend)
192 }
193
194 fn append_frames_transactional(&mut self, frames: &[u8]) -> CoreResult<()> {
195 self.writer
196 .append_raw_frames(frames)
197 .map_err(fwob_core::FwobError::backend)
198 }
199
200 fn finish(self: Box<Self>) -> CoreResult<()> {
201 self.writer.finish().map_err(fwob_core::FwobError::backend)
202 }
203}
204
205pub fn open_reader(path: impl AsRef<Path>) -> crate::Result<CoreReader> {
206 let (reader, factory) = ReaderAdapter::open(path)?;
207 Ok(CoreReader::from_parts(reader, factory))
208}
209
210pub fn create_writer(
211 path: impl AsRef<Path>,
212 schema: Schema,
213 options: WriterOptions,
214) -> crate::Result<CoreWriter> {
215 Ok(CoreWriter::from_backend(WriterAdapter::create(
216 path, schema, options,
217 )?))
218}
219
220pub fn open_writer(path: impl AsRef<Path>, options: WriterOptions) -> crate::Result<CoreWriter> {
221 Ok(CoreWriter::from_backend(WriterAdapter::open_append(
222 path, options,
223 )?))
224}
225
226#[derive(Debug, Default, Clone, Copy)]
227pub struct MaintenanceService;
228
229impl Maintenance for MaintenanceService {
230 fn format_version(&self) -> FormatVersion {
231 FormatVersion::V2
232 }
233
234 fn light_verify(&self, path: &Path, _options: ReaderOptions) -> CoreResult<VerificationReport> {
235 let metadata_len = std::fs::metadata(path)?.len();
236 let mut reader = Reader::open(path).map_err(fwob_core::FwobError::backend)?;
237 let header = reader.header().clone();
238 let expected_len = FILE_HEADER_LEN + header.page_count * u64::from(header.page_size);
239 if metadata_len != expected_len {
240 return Err(fwob_core::FwobError::backend(
241 crate::V2Error::InvalidFileHeader,
242 ));
243 }
244 if header.page_count > 0 {
245 reader
246 .read_page_header(header.page_count - 1)
247 .map_err(fwob_core::FwobError::backend)?;
248 }
249 Ok(VerificationReport {
250 format_version: FormatVersion::V2,
251 frame_count: header.frame_count,
252 string_count: header.string_table.len() as u32,
253 file_length: metadata_len,
254 })
255 }
256
257 fn verify(&self, path: &Path, options: ReaderOptions) -> CoreResult<VerificationReport> {
258 let report = self.light_verify(path, options)?;
259 Reader::open(path)
260 .map_err(fwob_core::FwobError::backend)?
261 .verify()
262 .map_err(fwob_core::FwobError::backend)?;
263 Ok(report)
264 }
265
266 fn repair(&self, path: &Path, options: ReaderOptions) -> CoreResult<VerificationReport> {
267 crate::repair_committed_tail(path).map_err(fwob_core::FwobError::backend)?;
268 self.verify(path, options)
269 }
270}