Skip to main content

nwnrs_gff/
types.rs

1use std::{fmt, io};
2
3use nwnrs_io::prelude::*;
4use nwnrs_localization::prelude::*;
5
6type GffByte = u8;
7type GffChar = i8;
8type GffWord = u16;
9type GffShort = i16;
10type GffDword = u32;
11type GffInt = i32;
12type GffFloat = f32;
13type GffDword64 = u64;
14type GffInt64 = i64;
15type GffDouble = f64;
16type GffCExoString = String;
17type GffResRef = String;
18type GffVoid = Vec<u8>;
19type GffList = Vec<GffStruct>;
20
21pub(crate) const HEADER_SIZE: usize = 56;
22
23/// A `CExoLocString` value.
24#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffCExoLocString {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f,
            "GffCExoLocString", "str_ref", &self.str_ref, "entries",
            &&self.entries)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffCExoLocString {
    #[inline]
    fn clone(&self) -> GffCExoLocString {
        GffCExoLocString {
            str_ref: ::core::clone::Clone::clone(&self.str_ref),
            entries: ::core::clone::Clone::clone(&self.entries),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffCExoLocString {
    #[inline]
    fn eq(&self, other: &GffCExoLocString) -> bool {
        self.str_ref == other.str_ref && self.entries == other.entries
    }
}PartialEq)]
25/// A localized string may either reference a TLK entry via
26/// [`str_ref`](Self::str_ref) or carry inline language-specific overrides in
27/// [`entries`](Self::entries).
28pub struct GffCExoLocString {
29    /// The fallback TLK string reference.
30    pub str_ref: StrRef,
31    /// The inline language-specific strings.
32    pub entries: Vec<(i32, String)>,
33}
34
35impl Default for GffCExoLocString {
36    fn default() -> Self {
37        Self {
38            str_ref: BAD_STRREF,
39            entries: Vec::new(),
40        }
41    }
42}
43
44/// The primitive and compound value kinds supported by GFF.
45#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffFieldKind {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f,
            match self {
                GffFieldKind::Byte => "Byte",
                GffFieldKind::Char => "Char",
                GffFieldKind::Word => "Word",
                GffFieldKind::Short => "Short",
                GffFieldKind::Dword => "Dword",
                GffFieldKind::Int => "Int",
                GffFieldKind::Dword64 => "Dword64",
                GffFieldKind::Int64 => "Int64",
                GffFieldKind::Float => "Float",
                GffFieldKind::Double => "Double",
                GffFieldKind::CExoString => "CExoString",
                GffFieldKind::ResRef => "ResRef",
                GffFieldKind::CExoLocString => "CExoLocString",
                GffFieldKind::Void => "Void",
                GffFieldKind::Struct => "Struct",
                GffFieldKind::List => "List",
            })
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffFieldKind {
    #[inline]
    fn clone(&self) -> GffFieldKind { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for GffFieldKind { }Copy, #[automatically_derived]
impl ::core::cmp::PartialEq for GffFieldKind {
    #[inline]
    fn eq(&self, other: &GffFieldKind) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr
    }
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for GffFieldKind {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {}
}Eq, #[automatically_derived]
impl ::core::hash::Hash for GffFieldKind {
    #[inline]
    fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        ::core::hash::Hash::hash(&__self_discr, state)
    }
}Hash)]
46#[repr(u32)]
47/// These correspond directly to the numeric field type ids stored in the binary
48/// format.
49pub enum GffFieldKind {
50    /// An unsigned 8-bit integer.
51    Byte = 0,
52    /// A signed 8-bit integer.
53    Char = 1,
54    /// An unsigned 16-bit integer.
55    Word = 2,
56    /// A signed 16-bit integer.
57    Short = 3,
58    /// An unsigned 32-bit integer.
59    Dword = 4,
60    /// A signed 32-bit integer.
61    Int = 5,
62    /// An unsigned 64-bit integer.
63    Dword64 = 6,
64    /// A signed 64-bit integer.
65    Int64 = 7,
66    /// A 32-bit float.
67    Float = 8,
68    /// A 64-bit float.
69    Double = 9,
70    /// A counted string.
71    CExoString = 10,
72    /// A resource reference string.
73    ResRef = 11,
74    /// A localized string table.
75    CExoLocString = 12,
76    /// An opaque byte blob.
77    Void = 13,
78    /// A nested structure.
79    Struct = 14,
80    /// A list of nested structures.
81    List = 15,
82}
83
84impl GffFieldKind {
85    /// Returns `true` if this kind is stored out-of-line in the binary format.
86    #[must_use]
87    pub fn is_complex(self) -> bool {
88        #[allow(non_exhaustive_omitted_patterns)] match self {
    Self::Dword64 | Self::Int64 | Self::Double | Self::CExoString |
        Self::ResRef | Self::CExoLocString | Self::Void | Self::Struct |
        Self::List => true,
    _ => false,
}matches!(
89            self,
90            Self::Dword64
91                | Self::Int64
92                | Self::Double
93                | Self::CExoString
94                | Self::ResRef
95                | Self::CExoLocString
96                | Self::Void
97                | Self::Struct
98                | Self::List
99        )
100    }
101
102    pub(crate) fn from_u32(value: u32) -> Option<Self> {
103        Some(match value {
104            0 => Self::Byte,
105            1 => Self::Char,
106            2 => Self::Word,
107            3 => Self::Short,
108            4 => Self::Dword,
109            5 => Self::Int,
110            6 => Self::Dword64,
111            7 => Self::Int64,
112            8 => Self::Float,
113            9 => Self::Double,
114            10 => Self::CExoString,
115            11 => Self::ResRef,
116            12 => Self::CExoLocString,
117            13 => Self::Void,
118            14 => Self::Struct,
119            15 => Self::List,
120            _ => return None,
121        })
122    }
123}
124
125/// A typed GFF field value.
126#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffValue {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            GffValue::Byte(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Byte",
                    &__self_0),
            GffValue::Char(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Char",
                    &__self_0),
            GffValue::Word(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Word",
                    &__self_0),
            GffValue::Short(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Short",
                    &__self_0),
            GffValue::Dword(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Dword",
                    &__self_0),
            GffValue::Int(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Int",
                    &__self_0),
            GffValue::Float(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Float",
                    &__self_0),
            GffValue::Dword64(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Dword64", &__self_0),
            GffValue::Int64(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Int64",
                    &__self_0),
            GffValue::Double(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Double",
                    &__self_0),
            GffValue::CExoString(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "CExoString", &__self_0),
            GffValue::ResRef(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "ResRef",
                    &__self_0),
            GffValue::CExoLocString(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "CExoLocString", &__self_0),
            GffValue::Void(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Void",
                    &__self_0),
            GffValue::Struct(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Struct",
                    &__self_0),
            GffValue::List(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "List",
                    &__self_0),
        }
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffValue {
    #[inline]
    fn clone(&self) -> GffValue {
        match self {
            GffValue::Byte(__self_0) =>
                GffValue::Byte(::core::clone::Clone::clone(__self_0)),
            GffValue::Char(__self_0) =>
                GffValue::Char(::core::clone::Clone::clone(__self_0)),
            GffValue::Word(__self_0) =>
                GffValue::Word(::core::clone::Clone::clone(__self_0)),
            GffValue::Short(__self_0) =>
                GffValue::Short(::core::clone::Clone::clone(__self_0)),
            GffValue::Dword(__self_0) =>
                GffValue::Dword(::core::clone::Clone::clone(__self_0)),
            GffValue::Int(__self_0) =>
                GffValue::Int(::core::clone::Clone::clone(__self_0)),
            GffValue::Float(__self_0) =>
                GffValue::Float(::core::clone::Clone::clone(__self_0)),
            GffValue::Dword64(__self_0) =>
                GffValue::Dword64(::core::clone::Clone::clone(__self_0)),
            GffValue::Int64(__self_0) =>
                GffValue::Int64(::core::clone::Clone::clone(__self_0)),
            GffValue::Double(__self_0) =>
                GffValue::Double(::core::clone::Clone::clone(__self_0)),
            GffValue::CExoString(__self_0) =>
                GffValue::CExoString(::core::clone::Clone::clone(__self_0)),
            GffValue::ResRef(__self_0) =>
                GffValue::ResRef(::core::clone::Clone::clone(__self_0)),
            GffValue::CExoLocString(__self_0) =>
                GffValue::CExoLocString(::core::clone::Clone::clone(__self_0)),
            GffValue::Void(__self_0) =>
                GffValue::Void(::core::clone::Clone::clone(__self_0)),
            GffValue::Struct(__self_0) =>
                GffValue::Struct(::core::clone::Clone::clone(__self_0)),
            GffValue::List(__self_0) =>
                GffValue::List(::core::clone::Clone::clone(__self_0)),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffValue {
    #[inline]
    fn eq(&self, other: &GffValue) -> bool {
        let __self_discr = ::core::intrinsics::discriminant_value(self);
        let __arg1_discr = ::core::intrinsics::discriminant_value(other);
        __self_discr == __arg1_discr &&
            match (self, other) {
                (GffValue::Byte(__self_0), GffValue::Byte(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Char(__self_0), GffValue::Char(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Word(__self_0), GffValue::Word(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Short(__self_0), GffValue::Short(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Dword(__self_0), GffValue::Dword(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Int(__self_0), GffValue::Int(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Float(__self_0), GffValue::Float(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Dword64(__self_0), GffValue::Dword64(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Int64(__self_0), GffValue::Int64(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Double(__self_0), GffValue::Double(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::CExoString(__self_0),
                    GffValue::CExoString(__arg1_0)) => __self_0 == __arg1_0,
                (GffValue::ResRef(__self_0), GffValue::ResRef(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::CExoLocString(__self_0),
                    GffValue::CExoLocString(__arg1_0)) => __self_0 == __arg1_0,
                (GffValue::Void(__self_0), GffValue::Void(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::Struct(__self_0), GffValue::Struct(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (GffValue::List(__self_0), GffValue::List(__arg1_0)) =>
                    __self_0 == __arg1_0,
                _ => unsafe { ::core::intrinsics::unreachable() }
            }
    }
}PartialEq)]
127/// The enum variants mirror the canonical `GFF V3.2` field kinds.
128pub enum GffValue {
129    /// An unsigned 8-bit integer.
130    Byte(GffByte),
131    /// A signed 8-bit integer.
132    Char(GffChar),
133    /// An unsigned 16-bit integer.
134    Word(GffWord),
135    /// A signed 16-bit integer.
136    Short(GffShort),
137    /// An unsigned 32-bit integer.
138    Dword(GffDword),
139    /// A signed 32-bit integer.
140    Int(GffInt),
141    /// A 32-bit float.
142    Float(GffFloat),
143    /// An unsigned 64-bit integer.
144    Dword64(GffDword64),
145    /// A signed 64-bit integer.
146    Int64(GffInt64),
147    /// A 64-bit float.
148    Double(GffDouble),
149    /// A counted string.
150    CExoString(GffCExoString),
151    /// A resource reference string.
152    ResRef(GffResRef),
153    /// A localized string table.
154    CExoLocString(GffCExoLocString),
155    /// An opaque byte blob.
156    Void(GffVoid),
157    /// A nested structure.
158    Struct(GffStruct),
159    /// A list of nested structures.
160    List(GffList),
161}
162
163impl GffValue {
164    /// Returns the field kind for this value.
165    #[must_use]
166    pub fn kind(&self) -> GffFieldKind {
167        match self {
168            Self::Byte(_) => GffFieldKind::Byte,
169            Self::Char(_) => GffFieldKind::Char,
170            Self::Word(_) => GffFieldKind::Word,
171            Self::Short(_) => GffFieldKind::Short,
172            Self::Dword(_) => GffFieldKind::Dword,
173            Self::Int(_) => GffFieldKind::Int,
174            Self::Float(_) => GffFieldKind::Float,
175            Self::Dword64(_) => GffFieldKind::Dword64,
176            Self::Int64(_) => GffFieldKind::Int64,
177            Self::Double(_) => GffFieldKind::Double,
178            Self::CExoString(_) => GffFieldKind::CExoString,
179            Self::ResRef(_) => GffFieldKind::ResRef,
180            Self::CExoLocString(_) => GffFieldKind::CExoLocString,
181            Self::Void(_) => GffFieldKind::Void,
182            Self::Struct(_) => GffFieldKind::Struct,
183            Self::List(_) => GffFieldKind::List,
184        }
185    }
186}
187
188/// A labeled GFF field.
189#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffField {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field2_finish(f, "GffField",
            "value", &self.value, "provenance", &&self.provenance)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffField {
    #[inline]
    fn clone(&self) -> GffField {
        GffField {
            value: ::core::clone::Clone::clone(&self.value),
            provenance: ::core::clone::Clone::clone(&self.provenance),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffField {
    #[inline]
    fn eq(&self, other: &GffField) -> bool {
        self.value == other.value && self.provenance == other.provenance
    }
}PartialEq)]
190/// Labels are stored on the containing [`GffStruct`]; this type only wraps the
191/// typed value.
192pub struct GffField {
193    pub(crate) value:      GffValue,
194    pub(crate) provenance: Option<GffFieldProvenance>,
195}
196
197impl GffField {
198    /// Creates a field from a typed value.
199    #[must_use]
200    pub fn new(value: GffValue) -> Self {
201        Self {
202            value,
203            provenance: None,
204        }
205    }
206
207    pub(crate) fn with_provenance(value: GffValue, provenance: GffFieldProvenance) -> Self {
208        Self {
209            value,
210            provenance: Some(provenance),
211        }
212    }
213
214    /// Returns the kind of the stored value.
215    #[must_use]
216    pub fn kind(&self) -> GffFieldKind {
217        self.value.kind()
218    }
219
220    /// Returns the stored field value.
221    #[must_use]
222    pub fn value(&self) -> &GffValue {
223        &self.value
224    }
225}
226
227#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffFieldProvenance {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f,
            "GffFieldProvenance", "label_bytes", &self.label_bytes,
            "original_value", &self.original_value, "raw_data_or_offset",
            &self.raw_data_or_offset, "raw_field_data", &&self.raw_field_data)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffFieldProvenance {
    #[inline]
    fn clone(&self) -> GffFieldProvenance {
        GffFieldProvenance {
            label_bytes: ::core::clone::Clone::clone(&self.label_bytes),
            original_value: ::core::clone::Clone::clone(&self.original_value),
            raw_data_or_offset: ::core::clone::Clone::clone(&self.raw_data_or_offset),
            raw_field_data: ::core::clone::Clone::clone(&self.raw_field_data),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffFieldProvenance {
    #[inline]
    fn eq(&self, other: &GffFieldProvenance) -> bool {
        self.raw_data_or_offset == other.raw_data_or_offset &&
                    self.label_bytes == other.label_bytes &&
                self.original_value == other.original_value &&
            self.raw_field_data == other.raw_field_data
    }
}PartialEq)]
228pub(crate) struct GffFieldProvenance {
229    pub(crate) label_bytes:        [u8; 16],
230    pub(crate) original_value:     GffValue,
231    pub(crate) raw_data_or_offset: i32,
232    pub(crate) raw_field_data:     Option<Vec<u8>>,
233}
234
235/// A GFF structure containing labeled fields.
236///
237/// A structure is the fundamental record unit in `GFF V3.2`. Field labels are
238/// unique within one structure, and field order is preserved explicitly because
239/// authored order matters for stable rewriting and inspection.
240#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffStruct {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f, "GffStruct",
            "id", &self.id, "fields", &self.fields, "provenance",
            &&self.provenance)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffStruct {
    #[inline]
    fn clone(&self) -> GffStruct {
        GffStruct {
            id: ::core::clone::Clone::clone(&self.id),
            fields: ::core::clone::Clone::clone(&self.fields),
            provenance: ::core::clone::Clone::clone(&self.provenance),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffStruct {
    #[inline]
    fn eq(&self, other: &GffStruct) -> bool {
        self.id == other.id && self.fields == other.fields &&
            self.provenance == other.provenance
    }
}PartialEq)]
241/// Fields preserve insertion order and labels are unique within a structure.
242pub struct GffStruct {
243    /// The structure id stored in the document.
244    pub id:                i32,
245    pub(crate) fields:     Vec<(String, GffField)>,
246    pub(crate) provenance: Option<GffStructProvenance>,
247}
248
249#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffStructProvenance {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "GffStructProvenance", "field_labels", &&self.field_labels)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffStructProvenance {
    #[inline]
    fn clone(&self) -> GffStructProvenance {
        GffStructProvenance {
            field_labels: ::core::clone::Clone::clone(&self.field_labels),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffStructProvenance {
    #[inline]
    fn eq(&self, other: &GffStructProvenance) -> bool {
        self.field_labels == other.field_labels
    }
}PartialEq)]
250pub(crate) struct GffStructProvenance {
251    pub(crate) field_labels: Vec<String>,
252}
253
254impl GffStruct {
255    /// Creates an empty structure with the given id.
256    #[must_use]
257    pub fn new(id: i32) -> Self {
258        Self {
259            id,
260            fields: Vec::new(),
261            provenance: None,
262        }
263    }
264
265    /// Returns the fields in their stored order.
266    #[must_use]
267    pub fn fields(&self) -> &[(String, GffField)] {
268        self.fields.as_slice()
269    }
270
271    /// Inserts or replaces a labeled field.
272    ///
273    /// # Errors
274    ///
275    /// Returns [`GffError`] if `label` is not a valid GFF label.
276    pub fn put_field(&mut self, label: impl Into<String>, field: GffField) -> GffResult<()> {
277        let label = label.into();
278        ensure_label(&label)?;
279
280        if let Some((_, existing)) = self.fields.iter_mut().find(|(name, _)| *name == label) {
281            *existing = field;
282        } else {
283            self.fields.push((label, field));
284        }
285
286        Ok(())
287    }
288
289    /// Inserts or replaces a labeled value.
290    ///
291    /// # Errors
292    ///
293    /// Returns [`GffError`] if `label` is not a valid GFF label.
294    pub fn put_value(&mut self, label: impl Into<String>, value: GffValue) -> GffResult<()> {
295        self.put_field(label, GffField::new(value))
296    }
297
298    /// Returns a field by label.
299    #[must_use]
300    pub fn get_field(&self, label: &str) -> Option<&GffField> {
301        self.fields
302            .iter()
303            .find_map(|(name, field)| (name == label).then_some(field))
304    }
305
306    /// Removes a field by label.
307    pub fn remove(&mut self, label: &str) -> Option<GffField> {
308        let idx = self.fields.iter().position(|(name, _)| name == label)?;
309        Some(self.fields.remove(idx).1)
310    }
311}
312
313/// A complete GFF document.
314///
315/// `GffRoot` pairs the top-level document header with the root structure. NWN
316/// conventionally uses root structure id `-1`, but the type keeps that policy
317/// explicit rather than implicit.
318#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffRoot {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field5_finish(f, "GffRoot",
            "file_type", &self.file_type, "file_version", &self.file_version,
            "root", &self.root, "source_bytes", &self.source_bytes,
            "source_snapshot", &&self.source_snapshot)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffRoot {
    #[inline]
    fn clone(&self) -> GffRoot {
        GffRoot {
            file_type: ::core::clone::Clone::clone(&self.file_type),
            file_version: ::core::clone::Clone::clone(&self.file_version),
            root: ::core::clone::Clone::clone(&self.root),
            source_bytes: ::core::clone::Clone::clone(&self.source_bytes),
            source_snapshot: ::core::clone::Clone::clone(&self.source_snapshot),
        }
    }
}Clone)]
319/// NWN conventionally stores the root structure with id `-1`.
320pub struct GffRoot {
321    /// The four-byte document type tag.
322    pub file_type:              String,
323    /// The four-byte document version tag.
324    pub file_version:           String,
325    /// The root structure.
326    pub root:                   GffStruct,
327    pub(crate) source_bytes:    Option<Vec<u8>>,
328    pub(crate) source_snapshot: Option<GffRootSnapshot>,
329}
330
331#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffRootSnapshot {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "GffRootSnapshot", "file_type", &self.file_type, "file_version",
            &self.file_version, "root", &&self.root)
    }
}Debug, #[automatically_derived]
impl ::core::clone::Clone for GffRootSnapshot {
    #[inline]
    fn clone(&self) -> GffRootSnapshot {
        GffRootSnapshot {
            file_type: ::core::clone::Clone::clone(&self.file_type),
            file_version: ::core::clone::Clone::clone(&self.file_version),
            root: ::core::clone::Clone::clone(&self.root),
        }
    }
}Clone, #[automatically_derived]
impl ::core::cmp::PartialEq for GffRootSnapshot {
    #[inline]
    fn eq(&self, other: &GffRootSnapshot) -> bool {
        self.file_type == other.file_type &&
                self.file_version == other.file_version &&
            self.root == other.root
    }
}PartialEq)]
332pub(crate) struct GffRootSnapshot {
333    pub(crate) file_type:    String,
334    pub(crate) file_version: String,
335    pub(crate) root:         GffStruct,
336}
337
338impl GffRoot {
339    /// Creates a new root document with version `V3.2`.
340    ///
341    /// The returned root structure starts with id `-1`, which is the NWN
342    /// convention for top-level GFF documents.
343    pub fn new(file_type: impl Into<String>) -> Self {
344        Self {
345            file_type:       file_type.into(),
346            file_version:    "V3.2".to_string(),
347            root:            GffStruct::new(-1),
348            source_bytes:    None,
349            source_snapshot: None,
350        }
351    }
352
353    /// Returns the fields on the root structure.
354    #[must_use]
355    pub fn fields(&self) -> &[(String, GffField)] {
356        self.root.fields()
357    }
358
359    /// Inserts or replaces a labeled value on the root structure.
360    ///
361    /// # Errors
362    ///
363    /// Returns [`GffError`] if `label` is not a valid GFF label.
364    pub fn put_value(&mut self, label: impl Into<String>, value: GffValue) -> GffResult<()> {
365        self.root.put_value(label, value)
366    }
367
368    pub(crate) fn snapshot(&self) -> GffRootSnapshot {
369        GffRootSnapshot {
370            file_type:    self.file_type.clone(),
371            file_version: self.file_version.clone(),
372            root:         self.root.clone(),
373        }
374    }
375}
376
377/// Errors returned by GFF readers and writers.
378#[derive(#[automatically_derived]
impl ::core::fmt::Debug for GffError {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            GffError::Io(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f, "Io",
                    &__self_0),
            GffError::Expectation(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Expectation", &__self_0),
            GffError::Message(__self_0) =>
                ::core::fmt::Formatter::debug_tuple_field1_finish(f,
                    "Message", &__self_0),
        }
    }
}Debug)]
379pub enum GffError {
380    /// An underlying IO error occurred.
381    Io(io::Error),
382    /// A format invariant was violated.
383    Expectation(ExpectationError),
384    /// The document could not be interpreted or encoded.
385    Message(String),
386}
387
388impl GffError {
389    pub(crate) fn msg(message: impl Into<String>) -> Self {
390        Self::Message(message.into())
391    }
392}
393
394impl fmt::Display for GffError {
395    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396        match self {
397            Self::Io(error) => error.fmt(f),
398            Self::Expectation(error) => error.fmt(f),
399            Self::Message(message) => f.write_str(message),
400        }
401    }
402}
403
404impl std::error::Error for GffError {}
405
406impl From<io::Error> for GffError {
407    fn from(value: io::Error) -> Self {
408        Self::Io(value)
409    }
410}
411
412impl From<ExpectationError> for GffError {
413    fn from(value: ExpectationError) -> Self {
414        Self::Expectation(value)
415    }
416}
417
418/// A result alias for GFF operations.
419pub type GffResult<T> = Result<T, GffError>;
420
421pub(crate) fn ensure_label(label: &str) -> GffResult<()> {
422    nwnrs_io::expect(
423        label.len() <= 16,
424        ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("invalid GFF label length for {0:?}",
                label))
    })format!("invalid GFF label length for {label:?}"),
425    )?;
426    Ok(())
427}
428
429impl PartialEq for GffRoot {
430    fn eq(&self, other: &Self) -> bool {
431        self.file_type == other.file_type
432            && self.file_version == other.file_version
433            && self.root == other.root
434    }
435}