Initial Commit
Will write more useful commit messages in the, the start of this project was extremely rushed.
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
39
Cargo.lock
generated
Normal file
39
Cargo.lock
generated
Normal file
@@ -0,0 +1,39 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.82"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vim_undo_extractor"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"nom",
|
||||
]
|
||||
10
Cargo.toml
Normal file
10
Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "vim_undo_extractor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.82"
|
||||
nom = "7.1.3"
|
||||
202
layout.txt
Normal file
202
layout.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
START_MAGIC: 56 69 6D 9F 55 6E 44 6F E5 "Vim.UnDoF"
|
||||
|
||||
Version: 00 03
|
||||
|
||||
SHA-256: A5 B4 88 3B 3A AB FB 50 30 CF 8E 57 58 30 AE 44 38 25 A8 A3 DF B2 BD DF D6 FB E6 F2 EB E8 14 45
|
||||
|
||||
Line Count: 00 00 00 7C
|
||||
|
||||
Line Length : 00 00 00 16
|
||||
Line: 20 20 20 20 73 74 64 3A 3A 73 74 72 69 6E 67 20 69 6E 70 75 74 3B " std::string input;"
|
||||
Line Number: 00 00 00 2B
|
||||
Column Number: 00 00 00 15
|
||||
|
||||
Old Head Sequence or 0 if null: 00 00 00 01
|
||||
New Head Sequence or 0 if null: 00 00 00 83
|
||||
Current Head Sequence or 0 if null: 00 00 00 83
|
||||
|
||||
Numhead: 00 00 00 83
|
||||
Sequence Last: 00 00 00 83
|
||||
Sequence Current: 00 00 00 82
|
||||
|
||||
|
||||
Time: 00 00 00 00 66 10 CE 39
|
||||
|
||||
Optional fields
|
||||
04
|
||||
01
|
||||
00 00 00 40
|
||||
|
||||
End Marker: 00
|
||||
|
||||
Header Magic: 5F D0
|
||||
|
||||
Next Pointer: 00 00 00 00
|
||||
Previous Pointer: 00 00 00 02
|
||||
Alt Next Pointer: 00 00 00 00
|
||||
Alt Previous Pointer: 00 00 00 00
|
||||
|
||||
Sequence: 00 00 00 01
|
||||
|
||||
Position
|
||||
Line Number: 00 00 00 54
|
||||
Column: 00 00 00 00
|
||||
Coladd: 00 00 00 00
|
||||
|
||||
Cursor VCol: FF FF FF FF
|
||||
Flags: 00 01
|
||||
00 00
|
||||
|
||||
Marks
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
|
||||
Visual Info:
|
||||
Start Position:
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
End Position:
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
00 00 00 00
|
||||
|
||||
Vi Mode: 00 00 00 00
|
||||
Vi_curswant: 00 00 00 00
|
||||
|
||||
Time: 00 00 00 00 66 10 BC DC
|
||||
|
||||
Optional Fields:
|
||||
04
|
||||
01
|
||||
00 00 00 00
|
||||
|
||||
End Marker: 00
|
||||
|
||||
Entry Magic: F5 18
|
||||
|
||||
Entry Type: 00 00 00 00
|
||||
Entry Data: 54 00 00 00 00 00 00 00 00 05 00 00 00 00 00 00 00
|
||||
|
||||
Entry Magic: F5 18
|
||||
|
||||
Entry Type: 00 00 00 00
|
||||
Entry Data: 55 00 00 00 04 00 00 00 00 00 00 00 05
|
||||
00 00 00 00 00 00 00 05 00 00 00 71 06 00 00 00
|
||||
00 00 00 05 00 00 00 00 00 00 00 05 00 00 00 00
|
||||
00 00 00
|
||||
|
||||
Entry End Magic: 35 81
|
||||
|
||||
Header Magic: 5F D0
|
||||
00 00 00 01 00 00 00 03 00
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
28
src/main.rs
Normal file
28
src/main.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use anyhow::Result;
|
||||
|
||||
|
||||
mod undo;
|
||||
mod parse;
|
||||
|
||||
use undo::UndoFile;
|
||||
|
||||
|
||||
fn main() -> Result<()> {
|
||||
|
||||
let undo_file = UndoFile::from_path("./undo_file")?;
|
||||
|
||||
for header in undo_file.headers {
|
||||
for entry in header.entries {
|
||||
let section: String = entry.section
|
||||
.iter()
|
||||
.map(|b| char::from_u32(*b as u32).unwrap_or(' '))
|
||||
.collect();
|
||||
|
||||
println!("{}", section);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
42
src/parse.rs
Normal file
42
src/parse.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use nom::{
|
||||
bytes::complete::take,
|
||||
combinator::map_res,
|
||||
IResult,
|
||||
error::Error,
|
||||
};
|
||||
|
||||
pub(crate) mod start_header;
|
||||
pub(crate) mod header;
|
||||
pub(crate) mod entry;
|
||||
|
||||
pub fn bytes_u32(input: &[u8]) -> IResult<&[u8], u32> {
|
||||
map_res(
|
||||
take(4usize),
|
||||
|b: &[u8]| Ok::<u32, Error<&[u8]>>(u32::from_be_bytes(b.try_into().unwrap()))
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn time(input: &[u8]) -> IResult<&[u8], u64> {
|
||||
map_res(
|
||||
take(8usize),
|
||||
|b: &[u8]| Ok::<u64, Error<&[u8]>>(u64::from_be_bytes(b.try_into().unwrap()))
|
||||
)(input)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct OptionalFields((u8, u8, u32));
|
||||
|
||||
fn optional_fields(input: &[u8]) -> IResult<&[u8], OptionalFields> {
|
||||
map_res(
|
||||
take(6usize),
|
||||
|b: &[u8]| {
|
||||
Ok::<OptionalFields, Error<&[u8]>>(
|
||||
OptionalFields ((
|
||||
b[0],
|
||||
b[1],
|
||||
u32::from_be_bytes(b[2..6].try_into().unwrap())
|
||||
))
|
||||
)
|
||||
}
|
||||
)(input)
|
||||
}
|
||||
97
src/parse/entry.rs
Normal file
97
src/parse/entry.rs
Normal file
@@ -0,0 +1,97 @@
|
||||
use nom::{
|
||||
IResult,
|
||||
Parser,
|
||||
Or,
|
||||
error::Error,
|
||||
combinator::map_res,
|
||||
sequence::{tuple, pair},
|
||||
multi::many0,
|
||||
bytes::complete::{tag, take, take_until},
|
||||
};
|
||||
|
||||
use super::bytes_u32;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub(crate) struct Entry {
|
||||
pub(crate) entry_type: EntryType,
|
||||
pub(crate) section: Vec<u8>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum EntryType {
|
||||
Unknown(u32),
|
||||
}
|
||||
|
||||
fn magic(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag(b"\xf5\x18")(input)
|
||||
}
|
||||
|
||||
fn entry(input: &[u8]) -> IResult<&[u8], Entry> {
|
||||
let (input, section_type) = take(4usize)(input)?;
|
||||
let (input, section) = take_until(b"\xf5\x18".as_ref())(input)?;
|
||||
let (input, _) = tag(b"\xf5\x18")(input)?;
|
||||
|
||||
let entry_type = match section_type {
|
||||
num => EntryType::Unknown(u32::from_be_bytes(num.try_into().unwrap())),
|
||||
};
|
||||
|
||||
println!("{:#?}", section);
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Entry {
|
||||
entry_type,
|
||||
section: section.to_vec(),
|
||||
}
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
fn sections(input: &[u8]) -> IResult<&[u8], Vec<Entry>> {
|
||||
let (input, sections) = take_until(b"\x35\x81".as_ref())(input)?;
|
||||
let (last_entry, mut entries) = many0(entry)(sections)?;
|
||||
entries.push(
|
||||
Entry {
|
||||
entry_type: match last_entry[0..4] {
|
||||
_ => EntryType::Unknown(u32::from_be_bytes(last_entry[0..4].try_into().unwrap())),
|
||||
},
|
||||
section: last_entry[4..].to_vec(),
|
||||
});
|
||||
Ok((input, entries))
|
||||
}
|
||||
|
||||
pub(crate) fn parse(input: &[u8]) -> IResult<&[u8], Vec<Entry>> {
|
||||
let (input, (
|
||||
_,
|
||||
entries,
|
||||
_,
|
||||
)) = tuple((
|
||||
magic,
|
||||
sections,
|
||||
tag(b"\x35\x81".as_ref())
|
||||
))(input).unwrap();
|
||||
|
||||
Ok((
|
||||
input,
|
||||
entries,
|
||||
))
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::{sections, Entry, EntryType};
|
||||
|
||||
#[test]
|
||||
fn test_sections() {
|
||||
let test_str = b"\x00\x00\x00\x00\xaa\xaa\xf5\x18\x00\x00\x00\x00\xaa\xaa\xf5\x18\x00\x00\x00\x00\xaa\xaa\x35\x81";
|
||||
|
||||
assert_eq!(sections(test_str).unwrap().1, vec![
|
||||
Entry {
|
||||
entry_type: EntryType::Unknown,
|
||||
section: vec![b'\xaa', b'\xaa'],
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
175
src/parse/header.rs
Normal file
175
src/parse/header.rs
Normal file
@@ -0,0 +1,175 @@
|
||||
use nom::{
|
||||
IResult,
|
||||
error::Error,
|
||||
combinator::map_res,
|
||||
sequence::tuple,
|
||||
bytes::complete::{tag, take},
|
||||
};
|
||||
|
||||
use super::{bytes_u32, time, optional_fields, OptionalFields};
|
||||
|
||||
use crate::parse::entry::Entry;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Header {
|
||||
pub(crate) next: u32,
|
||||
pub(crate) previous: u32,
|
||||
pub(crate) alt_next: u32,
|
||||
pub(crate) alt_previous: u32,
|
||||
pub(crate) sequence: u32,
|
||||
pub(crate) position: Position,
|
||||
pub(crate) cursor_vcol: u32,
|
||||
pub(crate) flags: Vec<Flag>,
|
||||
pub(crate) marks: Vec<u8>,
|
||||
pub(crate) visual_info: VisualInfo,
|
||||
pub(crate) time: u64,
|
||||
pub(crate) optional_fields: OptionalFields,
|
||||
pub(crate) entries: Vec<Entry>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Position {
|
||||
line_number: u32,
|
||||
column_number: u32,
|
||||
coladd: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Flag {
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VisualInfo {
|
||||
start: Position,
|
||||
end: Position,
|
||||
mode: u32,
|
||||
curswant: u32,
|
||||
}
|
||||
|
||||
fn magic(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag(b"\x5f\xd0".as_ref())(input)
|
||||
}
|
||||
|
||||
fn position(input: &[u8]) -> IResult<&[u8], Position> {
|
||||
map_res(
|
||||
tuple((
|
||||
bytes_u32,
|
||||
bytes_u32,
|
||||
bytes_u32,
|
||||
)),
|
||||
|(line_number, column_number, coladd)| {
|
||||
Ok::<Position, Error<&[u8]>>(Position {
|
||||
line_number,
|
||||
column_number,
|
||||
coladd,
|
||||
})
|
||||
}
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn visual_info(input: &[u8]) -> IResult<&[u8], VisualInfo> {
|
||||
map_res(
|
||||
tuple((
|
||||
position,
|
||||
position,
|
||||
bytes_u32,
|
||||
bytes_u32,
|
||||
)),
|
||||
|(start, end, mode, curswant)| {
|
||||
Ok::<VisualInfo, Error<&[u8]>>(
|
||||
VisualInfo {
|
||||
start,
|
||||
end,
|
||||
mode,
|
||||
curswant,
|
||||
}
|
||||
)
|
||||
}
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn flags(input: &[u8]) -> IResult<&[u8], Vec<Flag>> {
|
||||
map_res(
|
||||
take(4usize),
|
||||
|flags: &[u8]| {
|
||||
Ok::<Vec<Flag>, Error<&[u8]>>(
|
||||
flags.into_iter().map(|b| {
|
||||
match b {
|
||||
_ => Flag::Unknown
|
||||
}
|
||||
}).collect()
|
||||
)
|
||||
}
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn marks(input: &[u8]) ->IResult<&[u8], &[u8]> {
|
||||
take(310usize)(input)
|
||||
}
|
||||
|
||||
fn end_marker(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag(b"\x00")(input)
|
||||
}
|
||||
|
||||
pub(crate) fn parse(input: &[u8]) -> IResult<&[u8], Header> {
|
||||
let next = bytes_u32;
|
||||
let previous = bytes_u32;
|
||||
let alt_next = bytes_u32;
|
||||
let alt_previous = bytes_u32;
|
||||
let sequence = bytes_u32;
|
||||
let cursor_vcol = bytes_u32;
|
||||
let time = time;
|
||||
|
||||
let (input, (
|
||||
_,
|
||||
next,
|
||||
previous,
|
||||
alt_next,
|
||||
alt_previous,
|
||||
sequence,
|
||||
position,
|
||||
cursor_vcol,
|
||||
flags,
|
||||
marks,
|
||||
visual_info,
|
||||
time,
|
||||
optional_fields,
|
||||
_,
|
||||
)) = tuple((
|
||||
magic,
|
||||
next,
|
||||
previous,
|
||||
alt_next,
|
||||
alt_previous,
|
||||
sequence,
|
||||
position,
|
||||
cursor_vcol,
|
||||
flags,
|
||||
marks,
|
||||
visual_info,
|
||||
time,
|
||||
optional_fields,
|
||||
end_marker,
|
||||
))(input).unwrap();
|
||||
|
||||
Ok((
|
||||
|
||||
input,
|
||||
Header {
|
||||
next,
|
||||
previous,
|
||||
alt_next,
|
||||
alt_previous,
|
||||
sequence,
|
||||
position,
|
||||
visual_info,
|
||||
cursor_vcol,
|
||||
flags,
|
||||
marks: marks.to_vec(),
|
||||
time,
|
||||
optional_fields,
|
||||
entries: Vec::new(),
|
||||
}
|
||||
))
|
||||
}
|
||||
154
src/parse/start_header.rs
Normal file
154
src/parse/start_header.rs
Normal file
@@ -0,0 +1,154 @@
|
||||
use nom::{
|
||||
IResult,
|
||||
error::Error,
|
||||
combinator::map_res,
|
||||
sequence::tuple,
|
||||
bytes::complete::{tag, take},
|
||||
};
|
||||
|
||||
use super::{bytes_u32, time, optional_fields, OptionalFields};
|
||||
|
||||
|
||||
fn magic(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag(b"\x56\x69\x6D\x9F\x55\x6E\x44\x6F\xE5")(input)
|
||||
}
|
||||
|
||||
fn version(input: &[u8]) -> IResult<&[u8], u16> {
|
||||
map_res(
|
||||
take(2usize),
|
||||
|b: &[u8]| Ok::<u16, Error<&[u8]>>(u16::from_be_bytes(b.try_into().unwrap()))
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn hash(input: &[u8]) -> IResult<&[u8], &[u8; 32]> {
|
||||
map_res(
|
||||
take(32usize),
|
||||
|b: &[u8]| Ok::<&[u8; 32], Error<&[u8]>>(b.try_into().unwrap())
|
||||
)(input)
|
||||
}
|
||||
|
||||
fn line_with_length(input: &[u8]) -> IResult<&[u8], String> {
|
||||
let (input, line_length) = bytes_u32(input)?;
|
||||
map_res(
|
||||
take(line_length),
|
||||
|b: &[u8]| Ok::<String, Error<&[u8]>>(String::from_utf8(b.to_vec()).expect("Invalid UTF-8"))
|
||||
)(input)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn end_marker(input: &[u8]) -> IResult<&[u8], &[u8]> {
|
||||
tag(b"\x00".as_ref())(input)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct StartHeader {
|
||||
version: u16,
|
||||
hash: Vec<u8>,
|
||||
line_count: u32,
|
||||
line: String,
|
||||
line_number: u32,
|
||||
column_number: u32,
|
||||
old_head_sequence: u32,
|
||||
new_head_sequence: u32,
|
||||
current_head_sequence: u32,
|
||||
numhead: u32,
|
||||
last_sequence: u32,
|
||||
current_sequence: u32,
|
||||
time: u64,
|
||||
optional_fields: OptionalFields,
|
||||
}
|
||||
|
||||
pub(crate) fn parse(input: &[u8]) -> IResult<&[u8], StartHeader> {
|
||||
let line_count = bytes_u32;
|
||||
let line_number = bytes_u32;
|
||||
let column_number = bytes_u32;
|
||||
let old_head_sequence = bytes_u32;
|
||||
let new_head_sequence = bytes_u32;
|
||||
let current_head_sequence = bytes_u32;
|
||||
let numhead = bytes_u32;
|
||||
let last_sequence = bytes_u32;
|
||||
let current_sequence = bytes_u32;
|
||||
|
||||
|
||||
let (input, (
|
||||
_,
|
||||
version,
|
||||
hash,
|
||||
line_count,
|
||||
line,
|
||||
line_number,
|
||||
column_number,
|
||||
old_head_sequence,
|
||||
new_head_sequence,
|
||||
current_head_sequence,
|
||||
numhead,
|
||||
last_sequence,
|
||||
current_sequence,
|
||||
time,
|
||||
optional_fields,
|
||||
_,
|
||||
)) = tuple ((
|
||||
magic,
|
||||
version,
|
||||
hash,
|
||||
line_count,
|
||||
line_with_length,
|
||||
line_number,
|
||||
column_number,
|
||||
old_head_sequence,
|
||||
new_head_sequence,
|
||||
current_head_sequence,
|
||||
numhead,
|
||||
last_sequence,
|
||||
current_sequence,
|
||||
time,
|
||||
optional_fields,
|
||||
end_marker,
|
||||
))(&input).unwrap();
|
||||
|
||||
Ok(
|
||||
(
|
||||
input,
|
||||
StartHeader {
|
||||
version,
|
||||
hash: hash.to_vec(),
|
||||
line_count,
|
||||
line,
|
||||
line_number,
|
||||
column_number,
|
||||
old_head_sequence,
|
||||
new_head_sequence,
|
||||
current_head_sequence,
|
||||
numhead,
|
||||
last_sequence,
|
||||
current_sequence,
|
||||
time,
|
||||
optional_fields,
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{magic, version};
|
||||
|
||||
#[test]
|
||||
fn test_magic() {
|
||||
let test_str = b"\x56\x69\x6d\x9f\x55\x6e\x44\x6f\xe5\x00\x03";
|
||||
|
||||
let (_, magic) = magic(test_str).unwrap();
|
||||
|
||||
assert_eq!(magic, b"\x56\x69\x6d\x9f\x55\x6e\x44\x6f\xe5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_version() {
|
||||
let test_str = b"\x00\x03\xa5\xb4";
|
||||
|
||||
let (_, version) = version(test_str).unwrap();
|
||||
|
||||
assert_eq!(version, 3);
|
||||
}
|
||||
}
|
||||
50
src/undo.rs
Normal file
50
src/undo.rs
Normal file
@@ -0,0 +1,50 @@
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
use anyhow::{Result, anyhow};
|
||||
|
||||
use crate::parse::start_header::{self, StartHeader};
|
||||
use crate::parse::header::{self, Header};
|
||||
use crate::parse::entry::{self, Entry};
|
||||
|
||||
use nom::{
|
||||
sequence::tuple,
|
||||
multi::many0,
|
||||
bytes::complete::{take_until, tag},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct UndoFile {
|
||||
pub(crate) start_header: StartHeader,
|
||||
pub(crate) headers: Vec<Header>,
|
||||
}
|
||||
|
||||
impl UndoFile {
|
||||
pub(crate) fn from_path(path: &str) -> Result<Self> {
|
||||
let mut buffer: Vec<u8> = Vec::new();
|
||||
let mut file = File::open(path)?;
|
||||
|
||||
let _ = match file.read_to_end(&mut buffer) {
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(anyhow!(e)),
|
||||
};
|
||||
|
||||
let (input, start_header) = start_header::parse(&buffer).unwrap();
|
||||
let (_, out) = many0(tuple((header::parse, entry::parse, take_until(b"\x5f\xd0".as_ref()))))(input).unwrap();
|
||||
|
||||
let headers = out
|
||||
.into_iter()
|
||||
.map(|(mut header, entries, _)| {
|
||||
entries.into_iter().for_each(|entry| header.entries.push(entry));
|
||||
header
|
||||
}).collect();
|
||||
|
||||
Ok(
|
||||
Self {
|
||||
start_header,
|
||||
headers,
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user