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}