Skip to main content

spiral/api/usage/
objects.rs

1// Generated by tools/service-contracts/rust.py. Do not edit.
2#![doc = "Generated transport-independent contract objects."]
3
4use std::collections::BTreeMap;
5use std::error::Error as StdError;
6use std::fmt::{self, Display, Formatter};
7
8use serde::{Deserialize, Deserializer, Serialize, Serializer};
9use serde_json::Value;
10use spiral_contract::error::{ErrorEnvelope, ServiceError};
11use spiral_contract::safelong::{SafeLong, SafeUnsignedLong};
12use spiral_contract::strict::{DeserializeStrict, Strict};
13use uuid::Uuid;
14
15/// Counters of one execution of an Arbor Table scan, as the executor reported them. Fragments and
16/// files are Arbor concepts, so a scan of another table format takes its own arm. `resourceId` and
17/// `versionId` of the record name the Table version that was read. An execution that ended before
18/// its last row (`finished` false) counts only the splits that it read to their end.
19#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
20pub struct ArborScanUsage {
21    /// Contract field filesConsidered.
22    #[serde(rename = "filesConsidered")]
23    pub files_considered: SafeUnsignedLong,
24    /// Contract field filesPruned.
25    #[serde(rename = "filesPruned")]
26    pub files_pruned: SafeUnsignedLong,
27    /// Contract field finished.
28    #[serde(rename = "finished")]
29    pub finished: bool,
30    /// Contract field fragmentsConsidered.
31    #[serde(rename = "fragmentsConsidered")]
32    pub fragments_considered: SafeUnsignedLong,
33    /// Contract field fragmentsPruned.
34    #[serde(rename = "fragmentsPruned")]
35    pub fragments_pruned: SafeUnsignedLong,
36    /// Contract field outputRows.
37    #[serde(rename = "outputRows")]
38    pub output_rows: SafeUnsignedLong,
39    /// Contract field storage.
40    #[serde(rename = "storage")]
41    #[serde(default, skip_serializing_if = "Option::is_none")]
42    pub storage: Option<StorageReads>,
43}
44impl<'de> DeserializeStrict<'de> for ArborScanUsage {
45    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
46        #[derive(Deserialize)]
47        #[serde(deny_unknown_fields)]
48        struct __Wire {
49            #[serde(rename = "filesConsidered")]
50            files_considered: Strict<SafeUnsignedLong>,
51            #[serde(rename = "filesPruned")]
52            files_pruned: Strict<SafeUnsignedLong>,
53            #[serde(rename = "finished")]
54            finished: Strict<bool>,
55            #[serde(rename = "fragmentsConsidered")]
56            fragments_considered: Strict<SafeUnsignedLong>,
57            #[serde(rename = "fragmentsPruned")]
58            fragments_pruned: Strict<SafeUnsignedLong>,
59            #[serde(rename = "outputRows")]
60            output_rows: Strict<SafeUnsignedLong>,
61            #[serde(rename = "storage")]
62            #[serde(default)]
63            storage: Strict<Option<StorageReads>>,
64        }
65        let wire = __Wire::deserialize(deserializer)?;
66        Ok(Self {
67            files_considered: wire.files_considered.0,
68            files_pruned: wire.files_pruned.0,
69            finished: wire.finished.0,
70            fragments_considered: wire.fragments_considered.0,
71            fragments_pruned: wire.fragments_pruned.0,
72            output_rows: wire.output_rows.0,
73            storage: wire.storage.0,
74        })
75    }
76}
77
78/// Identifier of the project whose usage a request reports.
79#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
80#[serde(transparent)]
81pub struct ProjectId(pub String);
82impl std::fmt::Display for ProjectId {
83    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84        self.0.fmt(formatter)
85    }
86}
87impl<'de> DeserializeStrict<'de> for ProjectId {
88    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
89        Ok(Self(Strict::<String>::deserialize(deserializer)?.0))
90    }
91}
92
93/// At most 1,000 records of one project. The service drops and counts a record that occurred more
94/// than 30 days before it receives the request, or more than five minutes after.
95#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
96pub struct ReportUsageRecordsRequest {
97    /// Contract field records.
98    #[serde(rename = "records")]
99    pub records: Vec<UsageRecord>,
100}
101impl<'de> DeserializeStrict<'de> for ReportUsageRecordsRequest {
102    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
103        #[derive(Deserialize)]
104        #[serde(deny_unknown_fields)]
105        struct __Wire {
106            #[serde(rename = "records")]
107            records: Strict<Vec<UsageRecord>>,
108        }
109        let wire = __Wire::deserialize(deserializer)?;
110        Ok(Self {
111            records: wire.records.0,
112        })
113    }
114}
115
116/// Events scoped to exactly one legacy table or project. The service rejects requests that provide
117/// both scope identifiers or neither identifier.
118#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
119pub struct ReportUsageRequest {
120    /// Contract field events.
121    #[serde(rename = "events")]
122    pub events: Vec<UsageEvent>,
123    /// Contract field projectId.
124    #[serde(rename = "projectId")]
125    #[serde(default, skip_serializing_if = "Option::is_none")]
126    pub project_id: Option<String>,
127    /// Contract field tableId.
128    #[serde(rename = "tableId")]
129    #[serde(default, skip_serializing_if = "Option::is_none")]
130    pub table_id: Option<String>,
131}
132impl<'de> DeserializeStrict<'de> for ReportUsageRequest {
133    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
134        #[derive(Deserialize)]
135        #[serde(deny_unknown_fields)]
136        struct __Wire {
137            #[serde(rename = "events")]
138            events: Strict<Vec<UsageEvent>>,
139            #[serde(rename = "projectId")]
140            #[serde(default)]
141            project_id: Strict<Option<String>>,
142            #[serde(rename = "tableId")]
143            #[serde(default)]
144            table_id: Strict<Option<String>>,
145        }
146        let wire = __Wire::deserialize(deserializer)?;
147        Ok(Self {
148            events: wire.events.0,
149            project_id: wire.project_id.0,
150            table_id: wire.table_id.0,
151        })
152    }
153}
154
155/// Whether the storage counters include every read of the scan or only some. A scan reads storage
156/// in two ways. It reads some objects itself. It reads payload files through a file reader that all
157/// scans in the process share, and that reader does not know which scan asked for which bytes.
158/// `complete` means the counters include both, so `bytes` is everything the scan read. `direct`
159/// means the counters include only the reads that the scan made itself. Reads through the shared
160/// reader are missing, so the counters are a lower bound. Do not add `direct` counters to
161/// `complete` counters.
162#[derive(Clone, Debug, PartialEq, Eq)]
163pub enum StorageReadScope {
164    /// A known contract value.
165    Complete,
166    /// A known contract value.
167    Direct,
168    /// A value introduced by a newer server.
169    Unknown(String),
170}
171impl Serialize for StorageReadScope {
172    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
173        serializer.serialize_str(match self {
174            Self::Complete => "complete",
175            Self::Direct => "direct",
176            Self::Unknown(value) => value,
177        })
178    }
179}
180impl<'de> Deserialize<'de> for StorageReadScope {
181    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
182        Ok(match String::deserialize(deserializer)?.as_str() {
183            "complete" => Self::Complete,
184            "direct" => Self::Direct,
185            value => Self::Unknown(value.to_owned()),
186        })
187    }
188}
189impl<'de> DeserializeStrict<'de> for StorageReadScope {
190    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
191        #[derive(Deserialize)]
192        enum __Known {
193            #[serde(rename = "complete")]
194            Complete,
195            #[serde(rename = "direct")]
196            Direct,
197        }
198        Ok(match __Known::deserialize(deserializer)? {
199            __Known::Complete => Self::Complete,
200            __Known::Direct => Self::Direct,
201        })
202    }
203}
204
205/// Successful object reads of one scan execution. `scope` says which of the scan's reads the
206/// counters include.
207#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
208pub struct StorageReads {
209    /// Contract field bytes.
210    #[serde(rename = "bytes")]
211    pub bytes: SafeUnsignedLong,
212    /// Contract field readTimeNs.
213    #[serde(rename = "readTimeNs")]
214    pub read_time_ns: SafeUnsignedLong,
215    /// Contract field requests.
216    #[serde(rename = "requests")]
217    pub requests: SafeUnsignedLong,
218    /// Contract field scope.
219    #[serde(rename = "scope")]
220    pub scope: StorageReadScope,
221}
222impl<'de> DeserializeStrict<'de> for StorageReads {
223    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
224        #[derive(Deserialize)]
225        #[serde(deny_unknown_fields)]
226        struct __Wire {
227            #[serde(rename = "bytes")]
228            bytes: Strict<SafeUnsignedLong>,
229            #[serde(rename = "readTimeNs")]
230            read_time_ns: Strict<SafeUnsignedLong>,
231            #[serde(rename = "requests")]
232            requests: Strict<SafeUnsignedLong>,
233            #[serde(rename = "scope")]
234            scope: Strict<StorageReadScope>,
235        }
236        let wire = __Wire::deserialize(deserializer)?;
237        Ok(Self {
238            bytes: wire.bytes.0,
239            read_time_ns: wire.read_time_ns.0,
240            requests: wire.requests.0,
241            scope: wire.scope.0,
242        })
243    }
244}
245
246/// One schema-agnostic metering event. The server overwrites project_id and org_id with
247/// authoritative values before delivery.
248#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
249pub struct UsageEvent {
250    /// Contract field eventType.
251    #[serde(rename = "eventType")]
252    pub event_type: String,
253    /// Contract field properties.
254    #[serde(rename = "properties")]
255    pub properties: BTreeMap<String, Value>,
256}
257impl<'de> DeserializeStrict<'de> for UsageEvent {
258    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
259        #[derive(Deserialize)]
260        #[serde(deny_unknown_fields)]
261        struct __Wire {
262            #[serde(rename = "eventType")]
263            event_type: Strict<String>,
264            #[serde(rename = "properties")]
265            properties: Strict<BTreeMap<String, Value>>,
266        }
267        let wire = __Wire::deserialize(deserializer)?;
268        Ok(Self {
269            event_type: wire.event_type.0,
270            properties: wire.properties.0,
271        })
272    }
273}
274
275/// What a record measured. The arm name is the versioned schema of the measurement. A change in
276/// meaning takes a new arm, such as `arborScanV2`, and never changes an existing one. A server
277/// rejects an arm that it does not know, so a server that accepts a new arm deploys before a host
278/// that sends it.
279#[derive(Clone, Debug, PartialEq, Eq)]
280pub enum UsageMeasurement {
281    /// A known union arm.
282    ArborScanV1(ArborScanUsage),
283    /// An unknown discriminator and its complete wire object.
284    Unknown { tag: String, value: Value },
285}
286impl Serialize for UsageMeasurement {
287    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
288        match self {
289            Self::ArborScanV1(value) => {
290                let mut object = serde_json::Map::new();
291                object.insert("type".into(), Value::String("arborScanV1".into()));
292                object.insert(
293                    "arborScanV1".into(),
294                    serde_json::to_value(value).map_err(serde::ser::Error::custom)?,
295                );
296                Value::Object(object).serialize(serializer)
297            }
298            Self::Unknown { tag, value } => {
299                if value.get("type").and_then(Value::as_str) != Some(tag.as_str()) {
300                    return Err(serde::ser::Error::custom(
301                        "unknown union discriminator mismatch",
302                    ));
303                }
304                value.serialize(serializer)
305            }
306        }
307    }
308}
309impl<'de> Deserialize<'de> for UsageMeasurement {
310    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
311        let value = Value::deserialize(deserializer)?;
312        let tag = value
313            .get("type")
314            .and_then(Value::as_str)
315            .ok_or_else(|| serde::de::Error::custom("union requires type"))?;
316        match tag {
317            "arborScanV1" => Ok(Self::ArborScanV1(
318                serde_json::from_value(
319                    value
320                        .get("arborScanV1")
321                        .cloned()
322                        .ok_or_else(|| serde::de::Error::custom("union requires matching arm"))?,
323                )
324                .map_err(serde::de::Error::custom)?,
325            )),
326            tag => Ok(Self::Unknown {
327                tag: tag.to_owned(),
328                value,
329            }),
330        }
331    }
332}
333impl<'de> DeserializeStrict<'de> for UsageMeasurement {
334    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
335        #[derive(Deserialize)]
336        enum __Tag0 {
337            #[serde(rename = "arborScanV1")]
338            Value,
339        }
340        #[derive(Deserialize)]
341        #[serde(deny_unknown_fields)]
342        struct __Arm0 {
343            #[serde(rename = "type")]
344            _tag: __Tag0,
345            #[serde(rename = "arborScanV1")]
346            value: Strict<ArborScanUsage>,
347        }
348        #[derive(Deserialize)]
349        #[serde(untagged)]
350        enum __Known {
351            ArborScanV1(Box<__Arm0>),
352        }
353        Ok(match __Known::deserialize(deserializer)? {
354            __Known::ArborScanV1(arm) => Self::ArborScanV1(arm.value.0),
355        })
356    }
357}
358
359/// One raw measurement of work that a host ran for a project, such as one Table scan. A record
360/// states what happened and never carries a price. The host sets `id` once and keeps it when it
361/// sends the record again. `occurredAtMicros` is when the work ended, in microseconds since the
362/// Unix epoch. `source` names the host software, for example `rust-sdk`, and `emitterVersion` its
363/// version. The service adds the organization, the caller and the time it received the record.
364#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
365pub struct UsageRecord {
366    /// Contract field emitterVersion.
367    #[serde(rename = "emitterVersion")]
368    pub emitter_version: String,
369    /// Contract field id.
370    #[serde(rename = "id")]
371    pub id: Uuid,
372    /// Contract field measurement.
373    #[serde(rename = "measurement")]
374    pub measurement: UsageMeasurement,
375    /// Contract field occurredAtMicros.
376    #[serde(rename = "occurredAtMicros")]
377    pub occurred_at_micros: SafeLong,
378    /// Contract field resourceId.
379    #[serde(rename = "resourceId")]
380    #[serde(default, skip_serializing_if = "Option::is_none")]
381    pub resource_id: Option<Uuid>,
382    /// Contract field source.
383    #[serde(rename = "source")]
384    pub source: String,
385    /// Contract field versionId.
386    #[serde(rename = "versionId")]
387    #[serde(default, skip_serializing_if = "Option::is_none")]
388    pub version_id: Option<Uuid>,
389}
390impl<'de> DeserializeStrict<'de> for UsageRecord {
391    fn deserialize_strict<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
392        #[derive(Deserialize)]
393        #[serde(deny_unknown_fields)]
394        struct __Wire {
395            #[serde(rename = "emitterVersion")]
396            emitter_version: Strict<String>,
397            #[serde(rename = "id")]
398            id: Strict<Uuid>,
399            #[serde(rename = "measurement")]
400            measurement: Strict<UsageMeasurement>,
401            #[serde(rename = "occurredAtMicros")]
402            occurred_at_micros: Strict<SafeLong>,
403            #[serde(rename = "resourceId")]
404            #[serde(default)]
405            resource_id: Strict<Option<Uuid>>,
406            #[serde(rename = "source")]
407            source: Strict<String>,
408            #[serde(rename = "versionId")]
409            #[serde(default)]
410            version_id: Strict<Option<Uuid>>,
411        }
412        let wire = __Wire::deserialize(deserializer)?;
413        Ok(Self {
414            emitter_version: wire.emitter_version.0,
415            id: wire.id.0,
416            measurement: wire.measurement.0,
417            occurred_at_micros: wire.occurred_at_micros.0,
418            resource_id: wire.resource_id.0,
419            source: wire.source.0,
420            version_id: wire.version_id.0,
421        })
422    }
423}
424
425/// Typed errors declared by this service contract.
426#[derive(Clone, Debug, PartialEq, Eq)]
427pub enum UsageError {
428    /// Usage:Conflict.
429    Conflict { reason: String },
430    /// Usage:InvalidArgument.
431    InvalidArgument { reason: String },
432    /// Usage:NotFound.
433    NotFound { resource: String },
434}
435impl UsageError {
436    /// HTTP status for this declared error.
437    pub fn status(&self) -> u16 {
438        match self {
439            Self::Conflict { .. } => 409,
440            Self::InvalidArgument { .. } => 400,
441            Self::NotFound { .. } => 404,
442        }
443    }
444    /// Serialize only contract-declared error parameters.
445    pub fn envelope(&self, instance: String) -> ErrorEnvelope {
446        match self {
447            Self::Conflict { reason } => {
448                let mut envelope = ErrorEnvelope::new("CONFLICT", "Usage:Conflict", instance);
449                envelope
450                    .parameters
451                    .insert("reason".into(), reason.to_string());
452                envelope
453            }
454            Self::InvalidArgument { reason } => {
455                let mut envelope =
456                    ErrorEnvelope::new("INVALID_ARGUMENT", "Usage:InvalidArgument", instance);
457                envelope
458                    .parameters
459                    .insert("reason".into(), reason.to_string());
460                envelope
461            }
462            Self::NotFound { resource } => {
463                let mut envelope = ErrorEnvelope::new("NOT_FOUND", "Usage:NotFound", instance);
464                envelope
465                    .parameters
466                    .insert("resource".into(), resource.to_string());
467                envelope
468            }
469        }
470    }
471    /// Decode a declared error and its typed error parameters.
472    pub fn decode_envelope(envelope: &ErrorEnvelope) -> Option<Self> {
473        if envelope.error_instance_id.len() != 36
474            || Uuid::parse_str(&envelope.error_instance_id).is_err()
475        {
476            return None;
477        }
478        match envelope.error_name.as_str() {
479            "Usage:Conflict" => {
480                if envelope.error_code != "CONFLICT" {
481                    return None;
482                }
483                let reason = envelope.parameters.get("reason")?.clone();
484                Some(Self::Conflict { reason })
485            }
486            "Usage:InvalidArgument" => {
487                if envelope.error_code != "INVALID_ARGUMENT" {
488                    return None;
489                }
490                let reason = envelope.parameters.get("reason")?.clone();
491                Some(Self::InvalidArgument { reason })
492            }
493            "Usage:NotFound" => {
494                if envelope.error_code != "NOT_FOUND" {
495                    return None;
496                }
497                let resource = envelope.parameters.get("resource")?.clone();
498                Some(Self::NotFound { resource })
499            }
500            _ => None,
501        }
502    }
503}
504impl Display for UsageError {
505    fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
506        match self {
507            Self::Conflict { reason } => write!(formatter, "Usage:Conflict (reason: {})", reason),
508            Self::InvalidArgument { reason } => {
509                write!(formatter, "Usage:InvalidArgument (reason: {})", reason)
510            }
511            Self::NotFound { resource } => {
512                write!(formatter, "Usage:NotFound (resource: {})", resource)
513            }
514        }
515    }
516}
517impl StdError for UsageError {}
518impl ServiceError for UsageError {
519    fn to_envelope(&self, instance: String) -> ErrorEnvelope {
520        Self::envelope(self, instance)
521    }
522    fn from_envelope(envelope: &ErrorEnvelope) -> Option<Self> {
523        Self::decode_envelope(envelope)
524    }
525}