pub struct GffStruct {
pub struct_id: i32,
pub fields: Vec<GffField>,
}Expand description
One GFF struct node.
Fields§
§struct_id: i32Struct ID from the binary table.
fields: Vec<GffField>Ordered fields for this struct.
Implementations§
Source§impl GffStruct
impl GffStruct
Sourcepub fn get(&self, path: &GffPath) -> Result<&GffValue, GffPathError>
pub fn get(&self, path: &GffPath) -> Result<&GffValue, GffPathError>
Reads the value a path names.
The read counterpart to Self::set, and the way to do a
read-modify-write over a large file without building a typed view per
field.
§Errors
Every operation here walks the path the same way and can fail the same
way partway along it: GffPathError::EmptyPath for a path with no
segments, GffPathError::IndexAtRoot for one starting with an index,
GffPathError::NotAStruct or GffPathError::NotAList when a
segment’s value is not the kind the next segment needs,
GffPathError::IndexOutOfRange when a list is shorter than the index,
GffPathError::NoSuchField when a struct does not carry the label,
and GffPathError::AmbiguousLabel when it carries it more than once.
That last one is a refusal rather than a first match; see the module
docs.
On top of those, GffPathError::WrongSegmentKind when path ends in
an index. That lands on a bare GffStruct, which is not a
GffValue; Self::struct_id is the call that takes such a path.
Sourcepub fn set(
&mut self,
path: &GffPath,
value: GffValue,
) -> Result<GffValue, GffPathError>
pub fn set( &mut self, path: &GffPath, value: GffValue, ) -> Result<GffValue, GffPathError>
Replaces the value a path names, returning what was there.
Refuses a label the struct does not already carry, and refuses a replacement of a different type from the one stored. Both refusals exist because the alternative is a file that looks edited and is not: an appended duplicate loses to the original on lookup, and a widened integer shifts every byte after it.
To change a field’s type on purpose, remove it and
insert it back.
§Errors
The walk failures Self::get lists, plus
GffPathError::TypeMismatch when value is not the type the field
already holds. GffPathError::NoSuchField is the ordinary way a
mistyped label arrives here rather than becoming a new field.
Sourcepub fn insert(
&mut self,
path: &GffPath,
value: GffValue,
) -> Result<(), GffPathError>
pub fn insert( &mut self, path: &GffPath, value: GffValue, ) -> Result<(), GffPathError>
Adds a field a path names, which must not already be there.
The value’s own type decides the field’s type, there being nothing to preserve.
On disk a struct holding one field stores that field’s index directly and a struct holding more stores an offset into the shared index array, so adding a second field to a one-field struct changes the struct’s encoded shape. Nothing here has to arrange that: the writer picks the shape from the field count when it encodes.
§Errors
The walk failures Self::get lists for the path’s parent, plus
GffPathError::AlreadyExists when the struct already carries that
label. Adding a second copy is refused rather than done, because the
original would go on winning every lookup.
Sourcepub fn insert_at_position(
&mut self,
path: &GffPath,
value: GffValue,
position: usize,
) -> Result<(), GffPathError>
pub fn insert_at_position( &mut self, path: &GffPath, value: GffValue, position: usize, ) -> Result<(), GffPathError>
Adds a field among its siblings rather than after them.
position is clamped to the end, so usize::MAX appends and a position
from a struct that has since lost fields still lands somewhere valid.
This exists because putting a field back is not the same operation as adding one. A GFF struct stores its fields in order and the writer emits them in that order, so restoring a removed field by appending produces a file that differs from the original everywhere after the hole, which is what an undo of a deletion did before.
§Errors
The same as Self::insert. position is clamped rather than
checked, so it is never the reason this fails.
Sourcepub fn position(&self, path: &GffPath) -> Option<usize>
pub fn position(&self, path: &GffPath) -> Option<usize>
Where the field path names sits among its siblings.
None when the parent does not resolve, the path does not end in a
label, or no such field is there.
Sourcepub fn remove(&mut self, path: &GffPath) -> Result<GffValue, GffPathError>
pub fn remove(&mut self, path: &GffPath) -> Result<GffValue, GffPathError>
Removes what a path names, returning it.
A path ending in a label removes that field. A path ending in an index
removes that list element and returns it wrapped in a
GffValue::Struct.
Removing an element does not renumber its siblings. No list in the engine reads an element’s struct id as a position, so the ids that remain still mean what they meant.
§Errors
The walk failures Self::get lists. Nothing is removed when any of
them fires, so a failed remove leaves the tree as it was.
Sourcepub fn push_element(
&mut self,
path: &GffPath,
element: GffStruct,
) -> Result<usize, GffPathError>
pub fn push_element( &mut self, path: &GffPath, element: GffStruct, ) -> Result<usize, GffPathError>
Appends element to the list a path names, returning its index.
The element carries its own struct_id, and choosing it is the caller’s
job. Several lists have the engine compare that id against a per-list
constant and silently skip an element that does not match, and which
constant belongs to which list is per-format knowledge that lives above
this crate.
§Errors
The walk failures Self::get lists, with
GffPathError::NotAList the one that means path named a field
that is not a list. The element’s struct_id is not checked against
anything, since nothing here knows which list wants which id.
Sourcepub fn struct_id(&self, path: &GffPath) -> Result<i32, GffPathError>
pub fn struct_id(&self, path: &GffPath) -> Result<i32, GffPathError>
Reads the struct id of the list element a path names.
An element’s id is not a field and not a GffValue, so get cannot
reach it: a path ending in an index lands on a bare GffStruct. A
nested struct field needs nothing special, since get hands back the
GffValue::Struct and the id is on the struct inside it.
Some formats carry meaning there. A UTC’s equipment slot is its
Equip_ItemList element’s struct id and appears in no field, so reading
which slot an item occupies is exactly this call.
§Errors
The walk failures Self::get lists, plus
GffPathError::WrongSegmentKind when path ends in a label rather
than an index. That is the mirror of get’s refusal: this call takes
exactly the paths that one will not.
Sourcepub fn set_struct_id(
&mut self,
path: &GffPath,
struct_id: i32,
) -> Result<i32, GffPathError>
pub fn set_struct_id( &mut self, path: &GffPath, struct_id: i32, ) -> Result<i32, GffPathError>
Sets the struct id of the list element a path names, returning the old.
The counterpart to struct_id, and the way to move a
UTC equipment item between slots: the slot is the id, so re-equipping is
a change to an existing element rather than a removal and an append.
Which ids a list accepts is the caller’s to know. Several lists have the engine compare the id against a per-list constant and silently skip an element that does not match, and that mapping is per-format knowledge living above this crate.
§Errors
The same as Self::struct_id. struct_id itself is not validated,
so an id no list accepts is written without complaint.
Source§impl GffStruct
impl GffStruct
Sourcepub fn walk(&self) -> GffWalk<'_> ⓘ
pub fn walk(&self) -> GffWalk<'_> ⓘ
Every value beneath this struct, with the address it sits at.
Depth first in the order the file holds its fields, so a consumer printing the sequence gets the tree as it is laid out rather than sorted into something else. A struct-typed field is yielded before what is inside it, and a list before its elements.
§What the paths are good for
Every one of them ends at a field, so every one is a path
get accepts. That is not incidental: a path ending at a
list element names a struct rather than a value, which get refuses,
so the walk never produces one. The addresses are the same ones a
document’s change map and a lint finding use.
§Where a path does not resolve
A struct may carry one label more than once, and vanilla content does:
each EntryList and ReplyList node of some dialogues carries
SoundExists six times. Every copy is yielded, since dropping one
would make the walk lie about what the file holds, and their paths are
identical and name no single field. get refuses such an
address, deliberately, so those paths come back
AmbiguousLabel rather than
resolving to a guess.
The alternative was an occurrence number inside the address itself. That was not taken because the address type is shared with the linter, the change map and the diff, and this is not a shared problem: across every GFF in an install it happens for one label, in two lists, in one resource type. Across every save reachable from here, including the per-module resources inside their nested archives, it does not happen at all. A consumer that reads dialogue needs to expect it; a save editor never meets it.
Source§impl GffStruct
impl GffStruct
Sourcepub fn with_fields(struct_id: i32, fields: Vec<GffField>) -> Self
pub fn with_fields(struct_id: i32, fields: Vec<GffField>) -> Self
Creates a struct with pre-populated fields.
Sourcepub fn push_field(&mut self, label: GffLabel, value: GffValue)
pub fn push_field(&mut self, label: GffLabel, value: GffValue)
Appends a new field.
Takes a built GffLabel rather than converting one, so the decision
about an unrepresentable label belongs to whoever supplied it. A source
literal goes through gff_label! and fails the
build; a label read out of a file or a user’s input goes through
GffLabel::new and carries a Result the caller has to answer.
Sourcepub fn field(&self, label: &str) -> Option<&GffValue>
pub fn field(&self, label: &str) -> Option<&GffValue>
Returns the first field value that matches label, or None when the
struct carries no field under that label.
A struct can hold one label more than once, and vanilla content does:
every EntryList and ReplyList node in a .dlg carries SoundExists
six times over. Across every GFF file in the install the copies agree in
value, so taking the first is not a choice between different answers.
It is still a choice, and a caller that wants to see the rest has to
walk Self::fields itself.