Skip to main content

oxigdal_gpkg/
metadata.rs

1//! GeoPackage metadata structures.
2//!
3//! Implements the `gpkg_metadata` and `gpkg_metadata_reference` system tables
4//! defined in OGC GeoPackage Encoding Standard v1.3.1, §10.8.
5//!
6//! * [`GpkgMetadata`] — a row from `gpkg_metadata` (Table 16)
7//! * [`GpkgMetadataReference`] — a row from `gpkg_metadata_reference` (Table 18)
8//! * [`MetadataScope`] — MD_ScopeCode enumeration per ISO 19115
9//! * [`ReferenceScope`] — scope constants for `gpkg_metadata_reference`
10
11use std::str::FromStr;
12
13// ─────────────────────────────────────────────────────────────────────────────
14// MetadataScope — OGC §10.8, Table 16 (MD_ScopeCode)
15// ─────────────────────────────────────────────────────────────────────────────
16
17/// MD_ScopeCode enumeration as required by OGC GeoPackage §10.8 (Table 16).
18///
19/// Each variant corresponds to the string stored in the `md_scope` column of
20/// the `gpkg_metadata` table.  Unknown strings map to [`MetadataScope::Undefined`].
21#[derive(Debug, Clone, PartialEq)]
22pub enum MetadataScope {
23    /// Scope is not defined or not known.
24    Undefined,
25    /// A single session of data capture in the field.
26    FieldSession,
27    /// A collection of field sessions.
28    CollectionSession,
29    /// A series of datasets sharing the same product specification.
30    Series,
31    /// A geographic dataset.
32    Dataset,
33    /// Metadata describing a feature type.
34    FeatureType,
35    /// Metadata describing a single feature instance.
36    Feature,
37    /// Metadata describing an attribute type.
38    AttributeType,
39    /// Metadata describing a single attribute value.
40    Attribute,
41    /// A tile or a collection of tiles.
42    Tile,
43    /// A copy or imitation of an existing or hypothetical object.
44    Model,
45    /// A catalog of resources.
46    Catalog,
47    /// A conceptual schema or description language.
48    Schema,
49    /// A structured system of concepts or terms in a domain.
50    Taxonomy,
51    /// A computer program, instruction, or set of instructions.
52    Software,
53    /// A capability provided by a system for a set of inputs (OGC service).
54    Service,
55    /// A hardware item.
56    CollectionHardware,
57    /// A dataset that does not have geographic extent.
58    NonGeographicDataset,
59    /// A named array dimension of a coverage result.
60    DimensionGroup,
61}
62
63impl MetadataScope {
64    /// Return the canonical OGC string for this scope code.
65    ///
66    /// The returned string is suitable for direct insertion into the `md_scope`
67    /// column of `gpkg_metadata`.
68    pub fn as_str(&self) -> &'static str {
69        match self {
70            Self::Undefined => "undefined",
71            Self::FieldSession => "fieldSession",
72            Self::CollectionSession => "collectionSession",
73            Self::Series => "series",
74            Self::Dataset => "dataset",
75            Self::FeatureType => "featureType",
76            Self::Feature => "feature",
77            Self::AttributeType => "attributeType",
78            Self::Attribute => "attribute",
79            Self::Tile => "tile",
80            Self::Model => "model",
81            Self::Catalog => "catalog",
82            Self::Schema => "schema",
83            Self::Taxonomy => "taxonomy",
84            Self::Software => "software",
85            Self::Service => "service",
86            Self::CollectionHardware => "collectionHardware",
87            Self::NonGeographicDataset => "nonGeographicDataset",
88            Self::DimensionGroup => "dimensionGroup",
89        }
90    }
91}
92
93impl FromStr for MetadataScope {
94    /// Parsing is infallible: unknown strings map to [`MetadataScope::Undefined`].
95    type Err = std::convert::Infallible;
96
97    /// Parse the `md_scope` column value from `gpkg_metadata`.
98    ///
99    /// Follows the exact case-sensitive string values mandated by the OGC
100    /// GeoPackage specification.  Any unrecognised value maps to
101    /// [`MetadataScope::Undefined`].
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        let scope = match s {
104            "undefined" => Self::Undefined,
105            "fieldSession" => Self::FieldSession,
106            "collectionSession" => Self::CollectionSession,
107            "series" => Self::Series,
108            "dataset" => Self::Dataset,
109            "featureType" => Self::FeatureType,
110            "feature" => Self::Feature,
111            "attributeType" => Self::AttributeType,
112            "attribute" => Self::Attribute,
113            "tile" => Self::Tile,
114            "model" => Self::Model,
115            "catalog" => Self::Catalog,
116            "schema" => Self::Schema,
117            "taxonomy" => Self::Taxonomy,
118            "software" => Self::Software,
119            "service" => Self::Service,
120            "collectionHardware" => Self::CollectionHardware,
121            "nonGeographicDataset" => Self::NonGeographicDataset,
122            "dimensionGroup" => Self::DimensionGroup,
123            _ => Self::Undefined,
124        };
125        Ok(scope)
126    }
127}
128
129// ─────────────────────────────────────────────────────────────────────────────
130// GpkgMetadata — OGC §10.8 Table 16
131// ─────────────────────────────────────────────────────────────────────────────
132
133/// A row from the `gpkg_metadata` table (OGC GeoPackage §10.8, Table 16).
134///
135/// | Column            | Type    | Notes                                  |
136/// |-------------------|---------|----------------------------------------|
137/// | `id`              | INTEGER | Primary key, auto-increment            |
138/// | `md_scope`        | TEXT    | MD_ScopeCode string                    |
139/// | `md_standard_uri` | TEXT    | URI identifying the metadata standard  |
140/// | `mime_type`       | TEXT    | MIME type of the metadata content      |
141/// | `metadata`        | TEXT    | Metadata content (XML, JSON, etc.)     |
142#[derive(Debug, Clone, PartialEq)]
143pub struct GpkgMetadata {
144    /// Primary-key identifier.  Corresponds to the `id` column.
145    pub id: i64,
146    /// Scope code for this metadata document.
147    pub md_scope: MetadataScope,
148    /// URI of the metadata standard referenced by this record.
149    pub md_standard_uri: String,
150    /// MIME type of the metadata content (e.g. `"text/xml"`, `"application/json"`).
151    pub mime_type: String,
152    /// The raw metadata content as a UTF-8 string.
153    pub metadata: String,
154}
155
156// ─────────────────────────────────────────────────────────────────────────────
157// ReferenceScope — OGC §10.8.5, Table 18
158// ─────────────────────────────────────────────────────────────────────────────
159
160/// Reference scope constants for the `reference_scope` column of
161/// `gpkg_metadata_reference` (OGC GeoPackage §10.8.5, Table 18).
162///
163/// The scope determines which combination of `table_name`, `column_name`, and
164/// `row_id_value` columns carry meaningful values.
165#[derive(Debug, Clone, PartialEq)]
166pub enum ReferenceScope {
167    /// Metadata applies to the GeoPackage as a whole.
168    GeoPackage,
169    /// Metadata applies to a specific user-data table.
170    Table,
171    /// Metadata applies to a specific column within a table.
172    Column,
173    /// Metadata applies to a specific row (identified by `row_id_value`).
174    Row,
175    /// Metadata applies to a specific cell (row + column combination).
176    RowCol,
177}
178
179impl ReferenceScope {
180    /// Return the canonical OGC string for this reference scope.
181    ///
182    /// The returned string matches the values stored in the `reference_scope`
183    /// column of `gpkg_metadata_reference`.
184    pub fn as_str(&self) -> &'static str {
185        match self {
186            Self::GeoPackage => "geopackage",
187            Self::Table => "table",
188            Self::Column => "column",
189            Self::Row => "row",
190            Self::RowCol => "row/col",
191        }
192    }
193}
194
195impl FromStr for ReferenceScope {
196    /// Parsing is infallible: unknown strings map to [`ReferenceScope::GeoPackage`].
197    type Err = std::convert::Infallible;
198
199    /// Parse the `reference_scope` column value from `gpkg_metadata_reference`.
200    ///
201    /// Comparison is case-sensitive against the OGC-specified lower-case string
202    /// values.  Unknown strings fall back to [`ReferenceScope::GeoPackage`].
203    fn from_str(s: &str) -> Result<Self, Self::Err> {
204        let scope = match s {
205            "geopackage" => Self::GeoPackage,
206            "table" => Self::Table,
207            "column" => Self::Column,
208            "row" => Self::Row,
209            "row/col" => Self::RowCol,
210            _ => Self::GeoPackage,
211        };
212        Ok(scope)
213    }
214}
215
216// ─────────────────────────────────────────────────────────────────────────────
217// GpkgMetadataReference — OGC §10.8.5 Table 18
218// ─────────────────────────────────────────────────────────────────────────────
219
220/// A row from the `gpkg_metadata_reference` table (OGC GeoPackage §10.8.5, Table 18).
221///
222/// Links a [`GpkgMetadata`] record (via `md_file_id`) to the database
223/// object it describes.  Nullable columns are represented as `Option<_>`.
224///
225/// Column layout:
226///
227/// | # | Column            | Type              | Nullable |
228/// |---|-------------------|-------------------|----------|
229/// | 0 | `reference_scope` | TEXT              | No       |
230/// | 1 | `table_name`      | TEXT              | Yes      |
231/// | 2 | `column_name`     | TEXT              | Yes      |
232/// | 3 | `row_id_value`    | INTEGER           | Yes      |
233/// | 4 | `timestamp`       | DATETIME (TEXT)   | No       |
234/// | 5 | `md_file_id`      | INTEGER           | No (FK)  |
235/// | 6 | `md_parent_id`    | INTEGER           | Yes (FK) |
236#[derive(Debug, Clone, PartialEq)]
237pub struct GpkgMetadataReference {
238    /// Scope of the reference (geopackage / table / column / row / row/col).
239    pub reference_scope: ReferenceScope,
240    /// User-data table name; `None` when `reference_scope` is `"geopackage"`.
241    pub table_name: Option<String>,
242    /// Column name within `table_name`; `None` unless scope is `"column"` or
243    /// `"row/col"`.
244    pub column_name: Option<String>,
245    /// Row identifier within `table_name`; `None` unless scope is `"row"` or
246    /// `"row/col"`.
247    pub row_id_value: Option<i64>,
248    /// ISO 8601 timestamp of when the reference was last updated.
249    pub timestamp: String,
250    /// Foreign key into `gpkg_metadata.id` — the referenced metadata record.
251    pub md_file_id: i64,
252    /// Optional foreign key into `gpkg_metadata.id` — the parent metadata
253    /// record in a hierarchical metadata graph.
254    pub md_parent_id: Option<i64>,
255}