1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
[]
= "meterstore"
= "0.12.0"
= "2024"
= ["hupe1980"]
= "Hot/cold tiered store for metering time series — PostgreSQL for the recent window, Apache Iceberg for history."
= "MIT OR Apache-2.0"
= "https://github.com/hupe1980/meterstore"
= "https://hupe1980.github.io/meterstore"
= "https://docs.rs/meterstore"
= ["metering", "iceberg", "timeseries", "postgres"]
= ["database", "data-structures"]
= "README.md"
# Set by the highest dependency floor (metering and iceberg both want
# 1.94), not by this crate's own syntax, which needs only 1.88.
= "1.94"
# docs.rs otherwise builds default features only, which would document neither
# Flight SQL nor the catalog facade nor the testkit — all three of which the
# crate-level documentation talks about. A published crate whose docs describe
# surfaces the docs do not contain is worse than one that says less.
[]
= true
= ["--cfg", "docsrs"]
# Debug info, and why it is turned down rather than left at the default.
#
# This crate's dependency graph is DataFusion + Arrow + Iceberg + sqlx + tonic +
# axum, and every one of ~20 integration test binaries links the whole of it
# statically. At the default `debug = true` each binary is **~420 MB**, almost
# all of it DWARF for code this crate did not write. Twenty of those exhaust a
# CI runner's disk, and the failure is not a tidy "no space left": `rust-lld`
# takes a **bus error** writing an output it cannot extend, which reads as a
# linker bug and is not one.
#
# `line-tables-only` for this crate keeps `file:line` in a panic backtrace —
# which is the whole of what a failing test needs — and dropping it entirely for
# dependencies removes the bulk. Set `debug = true` locally when stepping
# through a dependency in a debugger.
[]
= "line-tables-only"
[]
= false
# Dependencies are compiled once and executed constantly by the integration
# suites, which drive a real PostgreSQL and a real Iceberg warehouse through
# DataFusion. `opt-level = 1` on them alone leaves this crate unoptimised and
# fully debuggable while removing the worst of the debug-build tax on the code
# doing the actual work.
= 1
[]
# Large dependency trees (DataFusion + Iceberg) produce test binaries whose
# unwind tables exceed the 16 MB the macOS linker can encode compactly. The
# resulting note is about debug-info layout, not about this crate's code, and
# there is no action to take — but `-D warnings` would otherwise fail the build
# on developer machines while passing in CI.
= "allow"
[]
# --- Query Engine ---
# Single-sourced: every DataFusion-dependent crate must track this same major.
# A duplicate `datafusion` in `cargo tree -d` is a build failure, not a warning:
# two majors give mutually incompatible `TableProvider` and `RecordBatch` types.
= "53.1"
= "0.1"
# The same `arrow` DataFusion already pulls (re-exported as `crate::arrow`),
# declared only to switch on the `json` feature so `QueryResult::to_json` can walk
# a `RecordBatch` into JSON rows. Pinned to the shared 58 major; the duplicate gate
# fails the build if it ever diverges from DataFusion's arrow.
= { = "58.4", = false, = ["json"] }
# --- Cold Tier: Apache Iceberg ---
= "0.10"
= "0.10"
# The catalogue behind `IcebergSqlCatalog`. Optional but in `default`, so only a
# deployment that opts out notices: it reaches `sqlx`'s optional SQLite driver,
# and `libsqlite3-sys` declares `links = "sqlite3"`, which cargo enforces over the
# whole resolve graph — feature on or not. A workspace that also links an embedded
# SQLite therefore cannot resolve until this is turned off.
= { = "0.10", = true }
# `file://` and `memory://` are the always-on baseline (local dev, tests, and a
# single-node file warehouse). The cloud object stores are opt-in features so a
# file-only deployment does not compile the AWS/GCS/Azure SDKs — see the
# `object-store-*` features below.
= { = "0.10", = false, = ["opendal-fs", "opendal-memory"] }
# The REST catalogue client behind `IcebergRestCatalog`, which is the cold tier on
# Polaris, Lakekeeper, Nessie or Gravitino. On by default because a REST catalogue
# is the shape a deployment picks when it has not picked one: it is what every
# engine already speaks, so such a deployment needs no facade at all.
#
# It has nothing to do with `catalog-facade`, which serves the REST *protocol*
# over whatever catalogue is in use and is built on axum.
= { = "0.10", = true }
# AWS S3 Tables. Not the Iceberg REST endpoint — that one authenticates with
# SigV4, which `iceberg-catalog-rest` cannot sign — but the **native** S3 Tables
# API through the AWS SDK, which signs for itself. Optional because it pulls the
# AWS SDK, which a non-AWS deployment has no reason to compile.
= { = "0.10", = true }
# --- Hot Tier: PostgreSQL ---
# `default-features = false`: the defaults are `["any", "macros", "migrate",
# "json"]` and this crate uses none of them — every statement is a runtime
# `sqlx::query`. They also carry the optional SQLite driver, hence the note on
# `iceberg-catalog-sql` above.
= { = "0.8", = false, = ["postgres", "runtime-tokio"] }
# `time` and `rust_decimal` are asked of the driver rather than of the `sqlx`
# facade. `sqlx/time` names `sqlx-sqlite?/time`, and naming an optional dependency
# is enough to put it in the resolve graph; `sqlx-postgres/time` is `["dep:time",
# "sqlx-core/time"]` and mentions no SQLite. Feature unification makes the two
# identical for this crate's code.
= { = "0.8", = false, = ["time", "rust_decimal"] }
# --- Domain Layer ---
# `metering` owns the domain: MeterInterval, MeasurementSeries, QualityFlag,
# MeasurementUnit, IntervalResolution, DST-aware billing aggregation, gas
# conversion, Ersatzwertbildung. MeterStore persists and queries those types —
# it does not redefine them. Part of the mako platform (hupe1980/mako).
#
# The floor is a floor rather than a preference: several of the things this crate
# stores are its types rather than strings it checks itself. All four of
# `ids::{BdewCode, Eic, MaloId, MeloId}` are what a `check =` column parses with,
# and their check character/check digit is the whole reason such a column is not
# a plain string; `BdewCode` is also the operator half of a `VersionScope`, where
# a wrong-but-plausible value is not an error but a *different scope*.
# `wire::rfc3339` fixes the shape `ProvenanceEntry::occurred_at` is written in,
# which `time`'s own feature-conditional serde impl would otherwise leave to
# feature unification.
#
# 0.22 is the floor because it completes `TryFrom<String>` on all four of those
# identifiers. A generic `impl TryInto<…>` bound is satisfied by the caller's own
# type and not by a deref of it, so every read entry point here — `series`,
# `readings`, both `melo(..)`, `VersionScope::for_interval` — would otherwise
# refuse the `String` an MSCONS parser hands back.
#
# 0.21 moved `ObisCode::is_import`/`is_export` to a by-value receiver, being
# derived from the new `direction()` — which `session::udf` exposes as
# `obis_direction`, the three-valued primitive the two booleans cannot express.
= { = "0.23", = ["serde"] }
# Timestamps and DST follow `metering`'s choice (time + time-tz), NOT chrono.
# Mixing the two would force a conversion at every row boundary. time-tz is
# load-bearing: Europe/Berlin DST gives 92- and 100-interval days, and every
# daily aggregate depends on getting that right.
#
# Deliberately **not** `serde-human-readable`. `time`'s serde impl is
# feature-conditional — an `OffsetDateTime` goes into JSON as a nine-element
# ordinal-date array without it and as a `time`-formatted string with it — which
# would make the on-disk shape of this crate's `provenance` column a function of
# what else happens to be in the binary. Enabling it here would only move that
# decision rather than remove it, and would impose a global feature on every
# other crate in the graph. `encode::encode_provenance` writes that column
# explicitly instead, so the stored form depends on nothing but this crate.
= { = "0.3", = ["macros"] }
= "2"
# Settlement is money — never f64.
= "1"
# --- Erasure ---
# Pseudonymous references must be unpredictable: a reference an attacker can
# guess or recompute is a re-identification path that survives erasure. This is
# the OS CSPRNG, not `std`'s hash seed, which is explicitly documented as not
# cryptographically secure.
= "0.4"
# Keyed hash of an erased identifier, so a replaying pipeline can be refused
# without the audit trail retaining the identifier it just erased.
= "0.12"
= "0.10"
# The erasure key is the one secret this crate copies and holds for the life of
# the process, and `SubjectRegistry` is `Clone` — so every derived session would
# otherwise leave another copy of it on the heap at drop. Already in the graph
# through `rustls`; declared so the key can be wiped rather than merely redacted.
= "1.9"
# --- Runtime ---
= { = "1.0", = ["full"] }
= "0.3"
# --- Optional ---
# The command-line front end (`cli` feature). Optional because a library
# consumer has no use for an argument parser, and `clap`'s derive machinery is
# not free to compile.
= { = "4.5", = ["derive", "env", "wrap_help"], = true }
# A `tracing` subscriber, for the CLI alone. The library deliberately installs
# none — a library that captures the global subscriber takes the decision away
# from the application embedding it — but a binary *is* the application.
= { = "0.3", = ["env-filter"], = true }
= { = "0.27", = true }
= { = "0.15", = ["postgres"], = true }
= { = "3.27", = true }
= { = "0.8", = true }
= "2.0.19"
= "0.1.44"
= "1.0.151"
= { = "1.0.229", = ["derive"] }
# Pinned to the same major as `arrow` (itself sourced via datafusion). The CI
# duplicate-dependency gate fails the build if these ever diverge, because two
# parquet versions mean two incompatible `WriterProperties` types.
= { = "58.4", = false, = ["arrow", "zstd", "snap"] }
= "0.3.6"
= "0.3.33"
= { = "0.32.0", = false, = ["metrics"] }
= "1.1"
# --- Serving: Flight SQL (§13.7.3) ---
# Pinned to the same major as `arrow`, which is itself sourced via datafusion.
# The duplicate-dependency gate covers `arrow`, so a Flight version pulling a
# second one fails the build rather than producing two mutually unusable
# `RecordBatch` types. `tonic` and `prost` follow arrow-flight's own floors.
= { = "58.4", = ["flight-sql"], = true }
= { = "0.14", = true }
= { = "0.14", = true }
[]
# The planner's safety properties (§17.1). `time_range` may only ever be wrong in
# the widening direction and `split` must be exhaustive and disjoint; both are
# silent when broken, and the shapes that break them are the ones nobody writes a
# case for.
= "1.11"
= "1.0.151"
= "3.27.0"
= "0.27"
= { = "0.15", = ["postgres"] }
= { = "0.8", = ["async_tokio"] }
# Turns a bound `TcpListener` into the connection stream tonic serves, so the
# Flight tests can take an ephemeral port instead of racing on a fixed one.
= { = "0.1", = ["net"] }
# `ServiceExt::oneshot`, so the catalog façade's routes are exercised as HTTP —
# a method and a path in, a status and a parsed error envelope out — without
# binding a port. Already in the graph via axum; declared for the `util` feature.
= { = "0.5", = ["util"] }
# Collects an `axum::body::Body` back into bytes in those tests.
= "0.1"
[]
= ["rest-catalog", "sql-catalog"]
# Cold-tier object-store backends for `IcebergSqlCatalog`. `file://`/`memory://`
# are always available; these add the cloud stores (and their SDKs) on demand.
= ["iceberg-storage-opendal/opendal-s3"]
= ["iceberg-storage-opendal/opendal-gcs"]
= ["iceberg-storage-opendal/opendal-azdls"]
= ["object-store-s3", "object-store-gcs", "object-store-azure"]
# The cold tier on an Iceberg REST catalogue — Polaris, Lakekeeper, Nessie,
# Gravitino. Default, because `CatalogKind::Rest` is what a configuration file
# that says nothing selects, and a default naming a catalogue the crate could not
# construct is a default nobody can take.
= ["dep:iceberg-catalog-rest"]
# The cold tier on a PostgreSQL-backed Iceberg SQL catalogue: one database to
# operate. Default; turn it off in a workspace that also links SQLite.
= ["dep:iceberg-catalog-sql"]
# Read-only Iceberg REST endpoint, for deployments on the SQL catalog (§13.7.1).
= ["dep:axum"]
# AWS S3 Tables as the cold-tier catalogue (§12.1). Optional because it pulls the
# AWS SDK, which a non-AWS deployment has no reason to compile.
#
# The `object-store-s3` implication is stated rather than relied upon:
# `iceberg-catalog-s3tables` hard-enables `opendal-s3` today, so feature
# unification already turns it on, but that is *its* implementation detail. A
# table bucket's data files are in S3 either way, and a catalogue that compiled
# but could not open a file would be a trap.
= ["dep:iceberg-catalog-s3tables", "object-store-s3"]
# Arrow Flight SQL over the unified hot + cold view (§13.7.3). The one surface
# an external client cannot assemble for itself.
= ["dep:arrow-flight", "dep:tonic", "dep:prost"]
# The reference suite drives a real warehouse through the SQL catalogue, so it
# names that feature rather than assuming the default set is on.
= ["dep:testcontainers", "dep:testcontainers-modules", "dep:tempfile", "sql-catalog"]
# The `meterstore` command-line tool: create tables, archive, query, serve.
#
# Off by default because this crate is a library first and a binary second — a
# service embedding it should not compile an argument parser. `cargo install
# meterstore --features cli` is the other half of that trade.
#
# It pulls both serving surfaces, because `meterstore serve` is the one
# subcommand a caller cannot assemble from the others, and a CLI whose most
# useful verb is half missing from the obvious install line is a CLI nobody
# finds. The two answer different questions: Flight SQL is the unified hot + cold
# view, and the façade is what lets an external engine read the history without
# this process in the data path at all.
#
# It names `sql-catalog` too, because a binary has to be able to open a cold tier:
# a library caller brings its own `Arc<dyn Catalog>`, and the CLI has only the
# configuration file. Built with `--no-default-features --features cli` it would
# otherwise compile and then refuse every `catalog =` value at runtime.
= ["dep:clap", "dep:tracing-subscriber", "flight", "catalog-facade", "sql-catalog"]
# The command-line tool. `required-features` rather than a separate crate: it is
# a thin front end over the same public API, and splitting it into a workspace
# member would mean a second version number to keep in step with the library it
# can only ever be used with.
[[]]
= "meterstore"
= "src/bin/meterstore.rs"
= ["cli"]
# Benchmarks run under `cargo bench`, not under `cargo test`: criterion's own
# harness collects samples, and the default one would treat each benchmark as a
# test and report nothing useful.
[[]]
= "encoding"
= false