• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Devin-Yeung / ailurus-kv / 18439259126

12 Oct 2025 04:40AM UTC coverage: 87.863% (-0.3%) from 88.158%
18439259126

push

github

web-flow
Chore(deps): Bump the dependencies group across 1 directory with 6 updates (#77)

* Chore(deps): Bump the dependencies group across 1 directory with 6 updates

Bumps the dependencies group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [anyhow](https://github.com/dtolnay/anyhow) | `1.0.99` | `1.0.100` |
| [error-stack](https://github.com/hashintel/hash) | `0.5.0` | `0.6.0` |
| [log](https://github.com/rust-lang/log) | `0.4.27` | `0.4.28` |
| [parking_lot](https://github.com/Amanieu/parking_lot) | `0.12.4` | `0.12.5` |
| [tempfile](https://github.com/Stebalien/tempfile) | `3.21.0` | `3.23.0` |
| [thiserror](https://github.com/dtolnay/thiserror) | `2.0.16` | `2.0.17` |



Updates `anyhow` from 1.0.99 to 1.0.100
- [Release notes](https://github.com/dtolnay/anyhow/releases)
- [Commits](https://github.com/dtolnay/anyhow/compare/1.0.99...1.0.100)

Updates `error-stack` from 0.5.0 to 0.6.0
- [Release notes](https://github.com/hashintel/hash/releases)
- [Commits](https://github.com/hashintel/hash/compare/error-stack@0.5.0...error-stack@0.6.0)

Updates `log` from 0.4.27 to 0.4.28
- [Release notes](https://github.com/rust-lang/log/releases)
- [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rust-lang/log/compare/0.4.27...0.4.28)

Updates `parking_lot` from 0.12.4 to 0.12.5
- [Release notes](https://github.com/Amanieu/parking_lot/releases)
- [Changelog](https://github.com/Amanieu/parking_lot/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Amanieu/parking_lot/compare/parking_lot-v0.12.4...parking_lot-v0.12.5)

Updates `tempfile` from 3.21.0 to 3.23.0
- [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Stebalien/tempfile/compare/v3.21.0...v3.23.0)

Updates `thiserror` from 2.0.16 to 2.0.17
- [Release notes](https://github.com/dtolnay/thiserror/releases)
- [Commits](https://github.com/dtolnay/thiserror/compare/2.0... (continued)

1 of 2 new or added lines in 2 files covered. (50.0%)

2 existing lines in 2 files now uncovered.

333 of 379 relevant lines covered (87.86%)

1.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

69.57
/src/data/data_file.rs
1
use crate::data::log_record::LogRecord;
2
use crate::errors::{Errors, Result};
3
use crate::fio;
4
use crate::fio::io_manager;
5
use bytes::{Buf, BytesMut};
6
use error_stack::Report;
7
use log::error;
8
use prost::{decode_length_delimiter, length_delimiter_len};
9
use std::fmt::{Debug, Formatter};
10
use std::path::Path;
11

12
pub const DATAFILE_SUFFIX: &str = ".data";
13
pub const INITIAL_DATAFILE_ID: u32 = 0;
14

15
pub struct DataFile {
16
    id: u32,
17
    offset: u64,
18
    io_manager: Box<dyn fio::IOManager>,
19
}
20

21
impl Debug for DataFile {
22
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
23
        f.debug_struct("DataFile")
×
24
            .field("id", &self.id)
×
25
            .field("offset", &self.offset)
×
26
            .finish()
27
    }
28
}
29

30
impl DataFile {
31
    pub fn new<P: AsRef<Path>>(path: P, id: u32) -> Result<DataFile> {
3✔
32
        let fname = path.as_ref().to_path_buf();
7✔
33
        let fname = match fname.is_dir() {
7✔
34
            true => {
×
35
                let datafile = std::format!("{:09}{}", id, DATAFILE_SUFFIX);
7✔
36
                fname.join(datafile)
7✔
37
            }
38
            false => {
×
39
                error!("Database dir {:?} Not exist", fname);
×
40
                return Err(Report::new(Errors::DatafileNotFound));
×
41
            }
42
        };
43

44
        // Check the existence of Datafile, if not exist, create one
45
        if !fname.is_file() {
7✔
46
            let _ = std::fs::File::create(&fname).map_err(|e| {
3✔
47
                error!("{}", e);
×
48
                Errors::CreateDbFileFail
×
49
            })?;
50
        }
51

52
        let offset = match std::fs::File::open(&fname) {
6✔
53
            Ok(f) => f
9✔
54
                .metadata()
55
                .map_err(|e| {
3✔
56
                    error!("{}", e);
×
57
                    Errors::InternalError
×
58
                })?
59
                .len(),
60
            Err(e) => {
×
61
                error!("{}", e);
×
62
                return Err(Report::new(Errors::FailToOpenFile));
×
63
            }
64
        };
65

66
        let io_manager = Box::new(io_manager(fname)?);
3✔
67

68
        Ok(DataFile {
3✔
UNCOV
69
            id,
×
70
            offset,
×
71
            io_manager,
×
72
        })
73
    }
74

75
    pub fn offset(&self) -> u64 {
1✔
76
        self.offset
1✔
77
    }
78

79
    pub fn id(&self) -> u32 {
1✔
80
        self.id
1✔
81
    }
82

83
    pub fn write(&mut self, buf: &[u8]) -> Result<usize> {
1✔
84
        let bytes_read = self.io_manager.write(buf)?;
1✔
85
        self.offset += bytes_read as u64;
1✔
86
        Ok(bytes_read)
1✔
87
    }
88

89
    pub fn sync(&self) -> Result<()> {
1✔
90
        self.io_manager.sync()
1✔
91
    }
92

93
    pub fn read(&self, offset: u64) -> Result<Option<LogRecord>> {
1✔
94
        // TODO: design decision, return Err(EOF) or Ok(None) when EOF reached
95
        // Layout of LogRecord
96
        // +-------+--------+-----------+-------------+-----------+-------------+
97
        // |  4B   |   1B   |    mut    |     mut     |    mut    |     mut     |
98
        // +-------+--------+-----------+-------------+-----------+-------------+
99
        // |  CRC  |  Type  |  KeySize  |  ValueSize  |    Key    |    Value    |
100
        // +-------+--------+-----------+-------------+-----------+-------------+
101

102
        let max_header_sz = std::mem::size_of::<u32>() /* size of CRC */
4✔
103
            + std::mem::size_of::<u8>() /* size of Type */
1✔
104
            + length_delimiter_len(u32::MAX as usize) * 2 /* variable key size and value size */;
1✔
105

106
        // if remaining bytes is zero, means EOF reached
107
        let mut header = match (self.io_manager.size()? - offset) as usize {
1✔
108
            remaining if remaining == 0 => return Ok(None),
2✔
109
            remaining if remaining < max_header_sz => BytesMut::zeroed(remaining),
2✔
110
            remaining if remaining > max_header_sz => BytesMut::zeroed(max_header_sz),
2✔
111
            _ => unreachable!(),
112
        };
113

114
        self.io_manager.read(&mut header, offset)?;
2✔
115

116
        let crc = header.get_u32();
1✔
117
        let record_type = header.get_u8();
1✔
118

119
        // bytes will advance automatically
120
        let key_size =
1✔
121
            decode_length_delimiter(&mut header).map_err(|_| Errors::DatafileCorrupted)?;
122
        let value_size =
1✔
123
            decode_length_delimiter(&mut header).map_err(|_| Errors::DatafileCorrupted)?;
124

125
        // EOF reached
126
        if key_size == 0 && value_size == 0 {
1✔
127
            return Ok(None);
×
128
        }
129

130
        let header_size = std::mem::size_of::<u32>() /* size of CRC */
4✔
131
            + std::mem::size_of::<u8>() /* size of Type */
1✔
132
            + length_delimiter_len(key_size) /* length of key size */
1✔
133
            + length_delimiter_len(value_size) /* length of key size */;
1✔
134

135
        let mut kv_buf = BytesMut::zeroed(key_size + value_size);
2✔
136
        self.io_manager
3✔
137
            .read(&mut kv_buf, offset + header_size as u64)?;
3✔
138

139
        let log_record = LogRecord {
140
            key: kv_buf.get(..key_size).unwrap().to_vec(),
1✔
141
            value: kv_buf.get(key_size..kv_buf.len()).unwrap().to_vec(),
2✔
142
            record_type: record_type.try_into()?,
2✔
143
        };
144

145
        if crc != log_record.crc() {
2✔
146
            error!("CRC does not match");
×
147
            return Err(Report::new(Errors::DatafileCorrupted));
×
148
        }
149

150
        Ok(Some(log_record))
1✔
151
    }
152
}
153

154
#[cfg(test)]
155
mod tests {
156
    use crate::data::log_record::{LogRecord, LogRecordType};
157
    use crate::mock::datafile_wrapper::DataFileWrapper;
158

159
    #[test]
160
    fn get_one_key() {
161
        let mut df = DataFileWrapper::default();
162
        let record = LogRecord {
163
            key: "ailurus-kv".as_bytes().to_vec(),
164
            value: "is Awesome".as_bytes().to_vec(),
165
            record_type: LogRecordType::Normal,
166
        };
167
        df.write(&record.encode()).unwrap();
168
        assert_eq!(df.read(0).unwrap().unwrap(), record);
169
    }
170
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc