pub struct Save {
pub character: Character,
pub quests: Quests,
pub waypoints: Waypoints,
pub npcs: Placeholder,
pub attributes: Attributes,
pub skills: SkillPoints,
pub items: Placeholder,
/* private fields */
}Expand description
Full in-memory save model.
Unknown payloads for unmodeled sections are preserved in placeholder structs. Invariants:
format()andversion()are synchronized throughset_format.expansion_type()is canonical and must be changed throughset_expansion_type.- Character/attribute level must be changed through
set_level.
Fields§
§character: CharacterCharacter section.
quests: QuestsQuests section.
waypoints: WaypointsWaypoints section.
npcs: PlaceholderNPC section (placeholder model).
attributes: AttributesAttributes section.
skills: SkillPointsSkills section.
items: PlaceholderItems section (placeholder model).
Implementations§
Source§impl Save
impl Save
Sourcepub fn new(format: FormatId, class: Class) -> Save
pub fn new(format: FormatId, class: Class) -> Save
Build a new blank save for a target format/class.
V99 defaults to Expansion; V105 and unknown formats default to RotW.
Call set_expansion_type afterward if you need Classic.
Sourcepub fn format(&self) -> FormatId
pub fn format(&self) -> FormatId
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 // Start from a known good file.
5 let bytes = std::fs::read("assets/test/Joe.d2s")?;
6
7 // Strict mode fails fast on invalid data.
8 let strict = Save::parse(&bytes, Strictness::Strict)?;
9 println!(
10 "Strict parse OK: format={:?}, name={}",
11 strict.save.format(),
12 strict.save.character.name
13 );
14
15 // Validation is separate from parsing, so check the save on its own.
16 let validation = strict.save.validate();
17 println!("\nValidation issues: {}", validation.issues.len());
18 for (index, issue) in validation.issues.iter().enumerate() {
19 println!("#{index} [{:?}] {}", issue.code, issue.message);
20 }
21
22 // Break the payload on purpose so we can inspect non-fatal issues.
23 // - byte 0 breaks signature
24 // - truncation simulates a damaged or incomplete file
25 let mut damaged = bytes.clone();
26 damaged[0] = 0x00;
27 damaged.truncate(220);
28
29 // Lax mode keeps going and collects issues.
30 let lax = Save::parse(&damaged, Strictness::Lax)?;
31 println!("\nLax parse issues: {}", lax.issues.len());
32 for (index, issue) in lax.issues.iter().enumerate() {
33 println!(
34 "#{index} [{:?}/{:?}] section={:?} offset={:?} expected={:?} found={:?}\n {}",
35 issue.severity,
36 issue.kind,
37 issue.section,
38 issue.offset,
39 issue.expected,
40 issue.found,
41 issue.message
42 );
43 }
44
45 // Strict mode should reject the damaged payload.
46 match Save::parse(&damaged, Strictness::Strict) {
47 Ok(_) => println!("\nUnexpected: strict parse accepted damaged payload."),
48 Err(error) => println!("\nStrict parse error: {error}"),
49 }
50
51 Ok(())
52}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let input_path = "assets/test/Warlock_v105.d2s";
6 let output_name = "WarlockDemo";
7 let output_dir = "target/example-output";
8 let output_path = format!("{output_dir}/{output_name}.d2s");
9
10 let bytes = std::fs::read(input_path)?;
11 let parsed = Save::parse(&bytes, Strictness::Strict)?;
12 if !parsed.issues.is_empty() {
13 return Err(format!("fixture parsed with issues: {:?}", parsed.issues).into());
14 }
15
16 let mut save = parsed.save;
17 let target_format = save.format();
18
19 println!(
20 "Loaded {:?} / {} / lvl {}",
21 target_format,
22 save.character.name,
23 save.character.level()
24 );
25
26 // Keep the file name and in-game name aligned.
27 save.character.name = output_name.to_string();
28
29 // Keep character.level and attributes.level in sync.
30 save.set_level(75);
31
32 // Main stats.
33 save.attributes.strength.value = 220;
34 save.attributes.dexterity.value = 175;
35 save.attributes.vitality.value = 260;
36 save.attributes.energy.value = 110;
37 save.attributes.statpts.value = 25;
38 save.attributes.newskills.value = 10;
39 save.attributes.gold.value = 200_000;
40 save.attributes.goldbank.value = 2_500_000;
41
42 // These stats are fixed-point in the save file (Q8), so the helpers take in-game values.
43 save.attributes.set_max_hp(2200);
44 save.attributes.set_hp(2200);
45 save.attributes.set_max_mana(900);
46 save.attributes.set_mana(900);
47 save.attributes.set_max_stamina(1200);
48 save.attributes.set_stamina(1200);
49
50 // Use the D2R name lookup when it exists.
51 // Fall back to raw slot indexes when it does not.
52 if save.skills.set_by_name_d2r(save.character.class, "Bash", 20).is_err() {
53 save.skills.set(0, 20);
54 }
55 if save.skills.set_by_name_d2r(save.character.class, "Battle Orders", 20).is_err() {
56 save.skills.set(23, 20);
57 }
58 if save.skills.set_by_name_d2r(save.character.class, "Whirlwind", 20).is_err() {
59 save.skills.set(25, 20);
60 }
61
62 // Give Act I all waypoints.
63 save.waypoints.normal.act1.set_all(true);
64
65 // Edit one quest state.
66 save.quests.normal.act1.q1.state.insert(QuestFlag::Started);
67
68 let act1_catacombs = save.waypoints.normal.act1.get_by_index(8)?;
69 let quest_started = save.quests.normal.act1.q1.state.contains(&QuestFlag::Started);
70
71 // Write back to the same detected format.
72 let output_bytes = save.encode_for(target_format, CompatibilityChecks::Enforce)?;
73 std::fs::create_dir_all(output_dir)?;
74 std::fs::write(&output_path, output_bytes)?;
75
76 println!("Wrote {output_path}");
77 println!("Now: {} / lvl {}", save.character.name, save.character.level());
78 println!(
79 "Stats: str={} dex={} vit={} ene={}",
80 save.attributes.strength.value,
81 save.attributes.dexterity.value,
82 save.attributes.vitality.value,
83 save.attributes.energy.value
84 );
85 println!(
86 "Resources: hp={} mana={} stamina={}",
87 save.attributes.get_max_hp(),
88 save.attributes.get_max_mana(),
89 save.attributes.get_max_stamina()
90 );
91 println!("Act I / Catacombs waypoint: {act1_catacombs}");
92 println!("Quest q1 started: {quest_started}");
93
94 Ok(())
95}Sourcepub fn set_format(&mut self, format: FormatId)
pub fn set_format(&mut self, format: FormatId)
Set output format and synchronize the numeric version field.
pub fn game_edition(&self) -> Option<GameEdition>
Sourcepub fn set_level(&mut self, level: u8)
pub fn set_level(&mut self, level: u8)
Set both character level fields kept in separate sections.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let input_path = "assets/test/Warlock_v105.d2s";
6 let output_name = "WarlockDemo";
7 let output_dir = "target/example-output";
8 let output_path = format!("{output_dir}/{output_name}.d2s");
9
10 let bytes = std::fs::read(input_path)?;
11 let parsed = Save::parse(&bytes, Strictness::Strict)?;
12 if !parsed.issues.is_empty() {
13 return Err(format!("fixture parsed with issues: {:?}", parsed.issues).into());
14 }
15
16 let mut save = parsed.save;
17 let target_format = save.format();
18
19 println!(
20 "Loaded {:?} / {} / lvl {}",
21 target_format,
22 save.character.name,
23 save.character.level()
24 );
25
26 // Keep the file name and in-game name aligned.
27 save.character.name = output_name.to_string();
28
29 // Keep character.level and attributes.level in sync.
30 save.set_level(75);
31
32 // Main stats.
33 save.attributes.strength.value = 220;
34 save.attributes.dexterity.value = 175;
35 save.attributes.vitality.value = 260;
36 save.attributes.energy.value = 110;
37 save.attributes.statpts.value = 25;
38 save.attributes.newskills.value = 10;
39 save.attributes.gold.value = 200_000;
40 save.attributes.goldbank.value = 2_500_000;
41
42 // These stats are fixed-point in the save file (Q8), so the helpers take in-game values.
43 save.attributes.set_max_hp(2200);
44 save.attributes.set_hp(2200);
45 save.attributes.set_max_mana(900);
46 save.attributes.set_mana(900);
47 save.attributes.set_max_stamina(1200);
48 save.attributes.set_stamina(1200);
49
50 // Use the D2R name lookup when it exists.
51 // Fall back to raw slot indexes when it does not.
52 if save.skills.set_by_name_d2r(save.character.class, "Bash", 20).is_err() {
53 save.skills.set(0, 20);
54 }
55 if save.skills.set_by_name_d2r(save.character.class, "Battle Orders", 20).is_err() {
56 save.skills.set(23, 20);
57 }
58 if save.skills.set_by_name_d2r(save.character.class, "Whirlwind", 20).is_err() {
59 save.skills.set(25, 20);
60 }
61
62 // Give Act I all waypoints.
63 save.waypoints.normal.act1.set_all(true);
64
65 // Edit one quest state.
66 save.quests.normal.act1.q1.state.insert(QuestFlag::Started);
67
68 let act1_catacombs = save.waypoints.normal.act1.get_by_index(8)?;
69 let quest_started = save.quests.normal.act1.q1.state.contains(&QuestFlag::Started);
70
71 // Write back to the same detected format.
72 let output_bytes = save.encode_for(target_format, CompatibilityChecks::Enforce)?;
73 std::fs::create_dir_all(output_dir)?;
74 std::fs::write(&output_path, output_bytes)?;
75
76 println!("Wrote {output_path}");
77 println!("Now: {} / lvl {}", save.character.name, save.character.level());
78 println!(
79 "Stats: str={} dex={} vit={} ene={}",
80 save.attributes.strength.value,
81 save.attributes.dexterity.value,
82 save.attributes.vitality.value,
83 save.attributes.energy.value
84 );
85 println!(
86 "Resources: hp={} mana={} stamina={}",
87 save.attributes.get_max_hp(),
88 save.attributes.get_max_mana(),
89 save.attributes.get_max_stamina()
90 );
91 println!("Act I / Catacombs waypoint: {act1_catacombs}");
92 println!("Quest q1 started: {quest_started}");
93
94 Ok(())
95}pub fn expansion_type(&self) -> ExpansionType
pub fn set_expansion_type(&mut self, expansion_type: ExpansionType)
Sourcepub fn parse(
byte_slice: &[u8],
strictness: Strictness,
) -> Result<ParsedSave, ParseHardError>
pub fn parse( byte_slice: &[u8], strictness: Strictness, ) -> Result<ParsedSave, ParseHardError>
Parse a save with explicit strictness.
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 // Start from a known good file.
5 let bytes = std::fs::read("assets/test/Joe.d2s")?;
6
7 // Strict mode fails fast on invalid data.
8 let strict = Save::parse(&bytes, Strictness::Strict)?;
9 println!(
10 "Strict parse OK: format={:?}, name={}",
11 strict.save.format(),
12 strict.save.character.name
13 );
14
15 // Validation is separate from parsing, so check the save on its own.
16 let validation = strict.save.validate();
17 println!("\nValidation issues: {}", validation.issues.len());
18 for (index, issue) in validation.issues.iter().enumerate() {
19 println!("#{index} [{:?}] {}", issue.code, issue.message);
20 }
21
22 // Break the payload on purpose so we can inspect non-fatal issues.
23 // - byte 0 breaks signature
24 // - truncation simulates a damaged or incomplete file
25 let mut damaged = bytes.clone();
26 damaged[0] = 0x00;
27 damaged.truncate(220);
28
29 // Lax mode keeps going and collects issues.
30 let lax = Save::parse(&damaged, Strictness::Lax)?;
31 println!("\nLax parse issues: {}", lax.issues.len());
32 for (index, issue) in lax.issues.iter().enumerate() {
33 println!(
34 "#{index} [{:?}/{:?}] section={:?} offset={:?} expected={:?} found={:?}\n {}",
35 issue.severity,
36 issue.kind,
37 issue.section,
38 issue.offset,
39 issue.expected,
40 issue.found,
41 issue.message
42 );
43 }
44
45 // Strict mode should reject the damaged payload.
46 match Save::parse(&damaged, Strictness::Strict) {
47 Ok(_) => println!("\nUnexpected: strict parse accepted damaged payload."),
48 Err(error) => println!("\nStrict parse error: {error}"),
49 }
50
51 Ok(())
52}More examples
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let bytes = std::fs::read("assets/test/Joe.d2s")?;
6 let parsed = Save::parse(&bytes, Strictness::Strict)?;
7 let mut save = parsed.save;
8
9 // Read by index.
10 let catacombs_was_unlocked = save.waypoints.hell.act1.get_by_index(8)?;
11 println!("Hell Act I / Catacombs unlocked: {catacombs_was_unlocked}");
12
13 // Set by waypoint id.
14 save.waypoints.hell.act1.set(Waypoint::Catacombs, true)?;
15 let catacombs_is_unlocked = save.waypoints.hell.act1.get(Waypoint::Catacombs)?;
16 println!("Hell Act I / Catacombs now unlocked: {catacombs_is_unlocked}");
17
18 // Set by index.
19 save.waypoints.normal.act4.set_by_index(2, true)?;
20 println!(
21 "Normal Act IV / River of Flames unlocked: {}",
22 save.waypoints.normal.act4.get_by_index(2)?
23 );
24
25 // Bulk update one difficulty.
26 save.waypoints.nightmare.set_all(true);
27 println!(
28 "Nightmare Act II / Sewers unlocked after set_all: {}",
29 save.waypoints.nightmare.act2.get_by_index(1)?
30 );
31
32 // Wrong-act usage returns an explicit error.
33 match save.waypoints.normal.act1.set(Waypoint::LutGholein, true) {
34 Err(WaypointError::WrongAct { waypoint, expected, actual }) => {
35 println!("WrongAct: {waypoint:?} belongs to {actual:?}, expected {expected:?}.")
36 }
37 Ok(_) => println!("Unexpected: wrong-act set was accepted."),
38 Err(error) => println!("Unexpected waypoint error: {error}"),
39 }
40
41 // Out-of-range reads return an explicit error.
42 match save.waypoints.normal.act4.get_by_index(3) {
43 Err(WaypointError::IndexOutOfRange { act, index, max_index }) => {
44 println!("IndexOutOfRange: {act:?} index {index} (max {max_index}).")
45 }
46 Ok(_) => println!("Unexpected: out-of-range read was accepted."),
47 Err(error) => println!("Unexpected waypoint error: {error}"),
48 }
49
50 Ok(())
51}4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let input_path = "assets/test/Warlock_v105.d2s";
6 let output_name = "WarlockDemo";
7 let output_dir = "target/example-output";
8 let output_path = format!("{output_dir}/{output_name}.d2s");
9
10 let bytes = std::fs::read(input_path)?;
11 let parsed = Save::parse(&bytes, Strictness::Strict)?;
12 if !parsed.issues.is_empty() {
13 return Err(format!("fixture parsed with issues: {:?}", parsed.issues).into());
14 }
15
16 let mut save = parsed.save;
17 let target_format = save.format();
18
19 println!(
20 "Loaded {:?} / {} / lvl {}",
21 target_format,
22 save.character.name,
23 save.character.level()
24 );
25
26 // Keep the file name and in-game name aligned.
27 save.character.name = output_name.to_string();
28
29 // Keep character.level and attributes.level in sync.
30 save.set_level(75);
31
32 // Main stats.
33 save.attributes.strength.value = 220;
34 save.attributes.dexterity.value = 175;
35 save.attributes.vitality.value = 260;
36 save.attributes.energy.value = 110;
37 save.attributes.statpts.value = 25;
38 save.attributes.newskills.value = 10;
39 save.attributes.gold.value = 200_000;
40 save.attributes.goldbank.value = 2_500_000;
41
42 // These stats are fixed-point in the save file (Q8), so the helpers take in-game values.
43 save.attributes.set_max_hp(2200);
44 save.attributes.set_hp(2200);
45 save.attributes.set_max_mana(900);
46 save.attributes.set_mana(900);
47 save.attributes.set_max_stamina(1200);
48 save.attributes.set_stamina(1200);
49
50 // Use the D2R name lookup when it exists.
51 // Fall back to raw slot indexes when it does not.
52 if save.skills.set_by_name_d2r(save.character.class, "Bash", 20).is_err() {
53 save.skills.set(0, 20);
54 }
55 if save.skills.set_by_name_d2r(save.character.class, "Battle Orders", 20).is_err() {
56 save.skills.set(23, 20);
57 }
58 if save.skills.set_by_name_d2r(save.character.class, "Whirlwind", 20).is_err() {
59 save.skills.set(25, 20);
60 }
61
62 // Give Act I all waypoints.
63 save.waypoints.normal.act1.set_all(true);
64
65 // Edit one quest state.
66 save.quests.normal.act1.q1.state.insert(QuestFlag::Started);
67
68 let act1_catacombs = save.waypoints.normal.act1.get_by_index(8)?;
69 let quest_started = save.quests.normal.act1.q1.state.contains(&QuestFlag::Started);
70
71 // Write back to the same detected format.
72 let output_bytes = save.encode_for(target_format, CompatibilityChecks::Enforce)?;
73 std::fs::create_dir_all(output_dir)?;
74 std::fs::write(&output_path, output_bytes)?;
75
76 println!("Wrote {output_path}");
77 println!("Now: {} / lvl {}", save.character.name, save.character.level());
78 println!(
79 "Stats: str={} dex={} vit={} ene={}",
80 save.attributes.strength.value,
81 save.attributes.dexterity.value,
82 save.attributes.vitality.value,
83 save.attributes.energy.value
84 );
85 println!(
86 "Resources: hp={} mana={} stamina={}",
87 save.attributes.get_max_hp(),
88 save.attributes.get_max_mana(),
89 save.attributes.get_max_stamina()
90 );
91 println!("Act I / Catacombs waypoint: {act1_catacombs}");
92 println!("Quest q1 started: {quest_started}");
93
94 Ok(())
95}Sourcepub fn summarize(
byte_slice: &[u8],
strictness: Strictness,
) -> Result<SaveSummary, ParseHardError>
pub fn summarize( byte_slice: &[u8], strictness: Strictness, ) -> Result<SaveSummary, ParseHardError>
Summarize only top-level header + character fields.
Sourcepub fn encode_for(
&self,
format: FormatId,
compatibility_checks: CompatibilityChecks,
) -> Result<Vec<u8>, EncodeError>
pub fn encode_for( &self, format: FormatId, compatibility_checks: CompatibilityChecks, ) -> Result<Vec<u8>, EncodeError>
Encode to a specific output format with an explicit compatibility policy.
Examples found in repository?
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5 let input_path = "assets/test/Warlock_v105.d2s";
6 let output_name = "WarlockDemo";
7 let output_dir = "target/example-output";
8 let output_path = format!("{output_dir}/{output_name}.d2s");
9
10 let bytes = std::fs::read(input_path)?;
11 let parsed = Save::parse(&bytes, Strictness::Strict)?;
12 if !parsed.issues.is_empty() {
13 return Err(format!("fixture parsed with issues: {:?}", parsed.issues).into());
14 }
15
16 let mut save = parsed.save;
17 let target_format = save.format();
18
19 println!(
20 "Loaded {:?} / {} / lvl {}",
21 target_format,
22 save.character.name,
23 save.character.level()
24 );
25
26 // Keep the file name and in-game name aligned.
27 save.character.name = output_name.to_string();
28
29 // Keep character.level and attributes.level in sync.
30 save.set_level(75);
31
32 // Main stats.
33 save.attributes.strength.value = 220;
34 save.attributes.dexterity.value = 175;
35 save.attributes.vitality.value = 260;
36 save.attributes.energy.value = 110;
37 save.attributes.statpts.value = 25;
38 save.attributes.newskills.value = 10;
39 save.attributes.gold.value = 200_000;
40 save.attributes.goldbank.value = 2_500_000;
41
42 // These stats are fixed-point in the save file (Q8), so the helpers take in-game values.
43 save.attributes.set_max_hp(2200);
44 save.attributes.set_hp(2200);
45 save.attributes.set_max_mana(900);
46 save.attributes.set_mana(900);
47 save.attributes.set_max_stamina(1200);
48 save.attributes.set_stamina(1200);
49
50 // Use the D2R name lookup when it exists.
51 // Fall back to raw slot indexes when it does not.
52 if save.skills.set_by_name_d2r(save.character.class, "Bash", 20).is_err() {
53 save.skills.set(0, 20);
54 }
55 if save.skills.set_by_name_d2r(save.character.class, "Battle Orders", 20).is_err() {
56 save.skills.set(23, 20);
57 }
58 if save.skills.set_by_name_d2r(save.character.class, "Whirlwind", 20).is_err() {
59 save.skills.set(25, 20);
60 }
61
62 // Give Act I all waypoints.
63 save.waypoints.normal.act1.set_all(true);
64
65 // Edit one quest state.
66 save.quests.normal.act1.q1.state.insert(QuestFlag::Started);
67
68 let act1_catacombs = save.waypoints.normal.act1.get_by_index(8)?;
69 let quest_started = save.quests.normal.act1.q1.state.contains(&QuestFlag::Started);
70
71 // Write back to the same detected format.
72 let output_bytes = save.encode_for(target_format, CompatibilityChecks::Enforce)?;
73 std::fs::create_dir_all(output_dir)?;
74 std::fs::write(&output_path, output_bytes)?;
75
76 println!("Wrote {output_path}");
77 println!("Now: {} / lvl {}", save.character.name, save.character.level());
78 println!(
79 "Stats: str={} dex={} vit={} ene={}",
80 save.attributes.strength.value,
81 save.attributes.dexterity.value,
82 save.attributes.vitality.value,
83 save.attributes.energy.value
84 );
85 println!(
86 "Resources: hp={} mana={} stamina={}",
87 save.attributes.get_max_hp(),
88 save.attributes.get_max_mana(),
89 save.attributes.get_max_stamina()
90 );
91 println!("Act I / Catacombs waypoint: {act1_catacombs}");
92 println!("Quest q1 started: {quest_started}");
93
94 Ok(())
95}Sourcepub fn check_compatibility(&self, target: FormatId) -> Vec<CompatibilityIssue>
pub fn check_compatibility(&self, target: FormatId) -> Vec<CompatibilityIssue>
Return compatibility findings for encoding this save to target.
Sourcepub fn title_d2r(&self) -> Option<&'static str>
pub fn title_d2r(&self) -> Option<&'static str>
Return the default D2R title for this save using the canonical expansion mode.
Sourcepub fn validate(&self) -> ValidationReport
pub fn validate(&self) -> ValidationReport
Validate the current save using backend-owned canonical rules.
Examples found in repository?
3fn main() -> Result<(), Box<dyn std::error::Error>> {
4 // Start from a known good file.
5 let bytes = std::fs::read("assets/test/Joe.d2s")?;
6
7 // Strict mode fails fast on invalid data.
8 let strict = Save::parse(&bytes, Strictness::Strict)?;
9 println!(
10 "Strict parse OK: format={:?}, name={}",
11 strict.save.format(),
12 strict.save.character.name
13 );
14
15 // Validation is separate from parsing, so check the save on its own.
16 let validation = strict.save.validate();
17 println!("\nValidation issues: {}", validation.issues.len());
18 for (index, issue) in validation.issues.iter().enumerate() {
19 println!("#{index} [{:?}] {}", issue.code, issue.message);
20 }
21
22 // Break the payload on purpose so we can inspect non-fatal issues.
23 // - byte 0 breaks signature
24 // - truncation simulates a damaged or incomplete file
25 let mut damaged = bytes.clone();
26 damaged[0] = 0x00;
27 damaged.truncate(220);
28
29 // Lax mode keeps going and collects issues.
30 let lax = Save::parse(&damaged, Strictness::Lax)?;
31 println!("\nLax parse issues: {}", lax.issues.len());
32 for (index, issue) in lax.issues.iter().enumerate() {
33 println!(
34 "#{index} [{:?}/{:?}] section={:?} offset={:?} expected={:?} found={:?}\n {}",
35 issue.severity,
36 issue.kind,
37 issue.section,
38 issue.offset,
39 issue.expected,
40 issue.found,
41 issue.message
42 );
43 }
44
45 // Strict mode should reject the damaged payload.
46 match Save::parse(&damaged, Strictness::Strict) {
47 Ok(_) => println!("\nUnexpected: strict parse accepted damaged payload."),
48 Err(error) => println!("\nStrict parse error: {error}"),
49 }
50
51 Ok(())
52}