Files
craft-libs/crates/dxf/tests/recovering.rs
64a8c7f74c craft-dxf: faster ASCII writing and parse_recovering (ports of open CADCraft PRs) (#924)
* dxf: write group codes, reals and plain text without the formatting machinery

write_ascii and its code page forms wrote every group code through
write!("{:>3}"), every real through a String of its own and every string
character by character. A large drawing writes millions of each. Codes 0 to
9999 and reals are now appended in place, and text with no line break or
backslash that is ASCII (or goes out as UTF-8) is copied as it is. Text for a
code page still takes the long path, since a code page may write an ASCII
character another way. The output is byte for byte what it was.

Ported from storytold/cadcraft#1938 (the crates/dxf part).

* dxf: parse_recovering reads a damaged or truncated ASCII file up to the damage

parse fails a file at the first line that is no group code, and at a last
group code with no value. parse_recovering returns the tags before that point
and the line where reading stopped, so a caller can open what the file still
holds and say where it ends. Reading stops at the damage: lines after it are
not paired up by guesswork. Damage before the first group, a truncated binary
file and the group-code limit are still errors.

section_slices ends a section at EOF as well as ENDSEC, so the EOF of a file
cut inside a section is not one of that section's records.

Ported from storytold/cadcraft#1565 (the crates/dxf part).

---------

Co-authored-by: Francesco BV <francesco.esposito@bvitalia.net>
Co-authored-by: Alex Ortiz <76929676+aljormon84@users.noreply.github.com>
2026-11-11 18:41:05 -05:00

52 lines
2.2 KiB
Rust

//! `parse_recovering`: what a damaged or truncated file still holds.
use craft_dxf::{DxfError, parse, parse_recovering, section_slices, write_binary};
const WHOLE: &str = "0\nSECTION\n2\nENTITIES\n0\nLINE\n8\nA\n0\nENDSEC\n0\nEOF\n";
#[test]
fn an_intact_file_reads_as_parse_reads_it() {
let (tags, damaged) = parse_recovering(WHOLE.as_bytes()).expect("reads");
assert_eq!(damaged, None);
assert_eq!(tags, parse(WHOLE.as_bytes()).expect("reads"));
// Binary files are read as before.
let binary = write_binary(&tags, true);
assert_eq!(parse_recovering(&binary).expect("reads"), (tags, None));
}
#[test]
fn a_line_that_is_no_group_code_ends_the_reading() {
let text = "0\nSECTION\n2\nENTITIES\n0\nLINE\n8\nA\n<<garbage>>\nmore\n0\nCIRCLE\n";
assert!(matches!(parse(text.as_bytes()), Err(DxfError::BadCode { line: 9, .. })));
let (tags, damaged) = parse_recovering(text.as_bytes()).expect("what came before is kept");
assert_eq!(damaged, Some(9));
assert_eq!(tags.len(), 4, "nothing after the damage is guessed at");
assert_eq!(tags.last().map(|t| (t.code, t.str())), Some((8, "A".to_string())));
}
#[test]
fn a_file_cut_after_a_group_code_keeps_the_groups_before_it() {
let text = "0\nSECTION\n2\nENTITIES\n0\nLINE\n8";
assert!(matches!(parse(text.as_bytes()), Err(DxfError::Eof)));
let (tags, damaged) = parse_recovering(text.as_bytes()).expect("what came before is kept");
assert_eq!((tags.len(), damaged), (3, Some(7)));
// The section it was cut in still has its records.
let s = section_slices(&tags);
assert_eq!((s.len(), s[0].name, s[0].tags.len()), (1, "ENTITIES", 1));
}
#[test]
fn damage_before_the_first_group_is_still_an_error() {
assert!(matches!(parse_recovering(b"not a drawing\n"), Err(DxfError::BadCode { line: 1, .. })));
assert!(matches!(parse_recovering(b"0"), Err(DxfError::Eof)));
assert!(matches!(parse_recovering(b""), Err(DxfError::NotDxf)));
assert!(matches!(parse_recovering(b"\n\n"), Err(DxfError::NotDxf)));
}
#[test]
fn a_section_without_endsec_ends_at_eof() {
let tags = parse(b"0\nSECTION\n2\nENTITIES\n0\nLINE\n0\nEOF\n").expect("reads");
let s = section_slices(&tags);
assert_eq!(s[0].tags.len(), 1, "the EOF is not a record of the section");
}