Skip to main content

rocketmq_error/
field.rs

1// Copyright 2023 The RocketMQ Rust Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::marker::PhantomData;
16
17/// Maximum number of bytes retained for a text context field.
18pub(crate) const MAX_TEXT_FIELD_BYTES: usize = 256;
19
20/// Controls which error projections may observe a context field.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub enum ContextVisibility {
23    /// The field is safe for public error projections.
24    Public,
25    /// The field is available only to controlled diagnostic projections.
26    Diagnostic,
27    /// Only the presence of secret-bearing input is recorded.
28    SecretPresenceOnly,
29}
30
31/// Closed vocabulary of values supported by structured error context.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
33pub enum FieldValueKind {
34    /// Bounded, normalized UTF-8 text.
35    Text,
36    /// A signed 64-bit integer.
37    I64,
38    /// An unsigned 64-bit integer.
39    U64,
40    /// A Boolean value.
41    Bool,
42    /// A value-free presence marker.
43    Presence,
44}
45
46/// Immutable schema for one catalog-owned context field.
47///
48/// Schemas define the external name, value shape, visibility, and text bound.
49/// Their fields and constructor are private so callers can inspect catalog
50/// policy but cannot create or alter it.
51///
52/// ```compile_fail,E0451
53/// use rocketmq_error::{ContextVisibility, FieldSchema, FieldValueKind};
54///
55/// let _ = FieldSchema {
56///     name: "external_name",
57///     visibility: ContextVisibility::Public,
58///     value_kind: FieldValueKind::Text,
59///     text_byte_limit: Some(64),
60/// };
61/// ```
62///
63/// ```compile_fail,E0624
64/// use rocketmq_error::{ContextVisibility, FieldSchema, FieldValueKind};
65///
66/// let _ = FieldSchema::try_new(
67///     "external_name",
68///     ContextVisibility::Public,
69///     FieldValueKind::Text,
70///     Some(64),
71/// );
72/// ```
73#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
74pub struct FieldSchema {
75    name: &'static str,
76    visibility: ContextVisibility,
77    value_kind: FieldValueKind,
78    text_byte_limit: Option<usize>,
79}
80
81impl FieldSchema {
82    pub(crate) const fn try_new(
83        name: &'static str,
84        visibility: ContextVisibility,
85        value_kind: FieldValueKind,
86        text_byte_limit: Option<usize>,
87    ) -> Option<Self> {
88        if !is_valid_field_name(name) || !is_valid_text_limit(value_kind, text_byte_limit) {
89            return None;
90        }
91        if matches!(visibility, ContextVisibility::SecretPresenceOnly)
92            && !matches!(value_kind, FieldValueKind::Presence)
93        {
94            return None;
95        }
96        Some(Self {
97            name,
98            visibility,
99            value_kind,
100            text_byte_limit,
101        })
102    }
103
104    /// Returns the lower-snake-case external field name.
105    #[inline]
106    pub const fn name(self) -> &'static str {
107        self.name
108    }
109
110    /// Returns the visibility policy for this field.
111    #[inline]
112    pub const fn visibility(self) -> ContextVisibility {
113        self.visibility
114    }
115
116    /// Returns the field's closed value shape.
117    #[inline]
118    pub const fn value_kind(self) -> FieldValueKind {
119        self.value_kind
120    }
121
122    /// Returns the maximum retained text size in bytes.
123    ///
124    /// Non-text fields always return [`None`].
125    #[inline]
126    pub const fn text_byte_limit(self) -> Option<usize> {
127        self.text_byte_limit
128    }
129}
130
131/// Opaque catalog key for a context field of marker type `T`.
132///
133/// Keys are copyable handles to immutable schemas. They can be obtained only
134/// from [`fields`], preventing callers from inventing names or changing field
135/// visibility.
136///
137/// ```compile_fail,E0451
138/// use std::marker::PhantomData;
139///
140/// use rocketmq_error::{fields, FieldKey, TextField};
141///
142/// let _: FieldKey<TextField> = FieldKey {
143///     schema: fields::TOPIC.schema(),
144///     marker: PhantomData,
145/// };
146/// ```
147///
148/// ```compile_fail,E0624
149/// use rocketmq_error::{fields, FieldKey, TextField};
150///
151/// let _: FieldKey<TextField> = FieldKey::new(fields::TOPIC.schema());
152/// ```
153#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
154pub struct FieldKey<T> {
155    schema: FieldSchema,
156    marker: PhantomData<fn() -> T>,
157}
158
159impl<T> Copy for FieldKey<T> {}
160
161impl<T> Clone for FieldKey<T> {
162    fn clone(&self) -> Self {
163        *self
164    }
165}
166
167impl<T> std::fmt::Debug for FieldKey<T> {
168    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        formatter.debug_tuple("FieldKey").field(&self.schema).finish()
170    }
171}
172
173impl<T> FieldKey<T> {
174    const fn new(schema: FieldSchema) -> Self {
175        Self {
176            schema,
177            marker: PhantomData,
178        }
179    }
180
181    /// Returns the immutable schema carried by this key.
182    #[inline]
183    pub const fn schema(self) -> FieldSchema {
184        self.schema
185    }
186}
187
188mod marker_seal {
189    pub trait Sealed {}
190}
191
192/// Marker for bounded text fields.
193#[derive(Debug)]
194pub struct TextField {
195    _private: (),
196}
197
198/// Marker for signed integer fields.
199#[derive(Debug)]
200pub struct I64Field {
201    _private: (),
202}
203
204/// Marker for unsigned integer fields.
205#[derive(Debug)]
206pub struct U64Field {
207    _private: (),
208}
209
210/// Marker for Boolean fields.
211#[derive(Debug)]
212pub struct BoolField {
213    _private: (),
214}
215
216/// Marker for value-free secret-presence fields.
217#[derive(Debug)]
218pub struct SecretPresenceField {
219    _private: (),
220}
221
222impl marker_seal::Sealed for TextField {}
223impl marker_seal::Sealed for I64Field {}
224impl marker_seal::Sealed for U64Field {}
225impl marker_seal::Sealed for BoolField {}
226impl marker_seal::Sealed for SecretPresenceField {}
227
228trait FieldMarker: marker_seal::Sealed {
229    const KIND: FieldValueKind;
230}
231
232impl FieldMarker for TextField {
233    const KIND: FieldValueKind = FieldValueKind::Text;
234}
235
236impl FieldMarker for I64Field {
237    const KIND: FieldValueKind = FieldValueKind::I64;
238}
239
240impl FieldMarker for U64Field {
241    const KIND: FieldValueKind = FieldValueKind::U64;
242}
243
244impl FieldMarker for BoolField {
245    const KIND: FieldValueKind = FieldValueKind::Bool;
246}
247
248impl FieldMarker for SecretPresenceField {
249    const KIND: FieldValueKind = FieldValueKind::Presence;
250}
251
252const fn checked_key<T: FieldMarker>(schema: FieldSchema) -> FieldKey<T> {
253    if !same_value_kind(schema.value_kind(), T::KIND) {
254        panic!("field marker does not match schema value kind");
255    }
256    FieldKey::new(schema)
257}
258
259const fn same_value_kind(left: FieldValueKind, right: FieldValueKind) -> bool {
260    matches!(
261        (left, right),
262        (FieldValueKind::Text, FieldValueKind::Text)
263            | (FieldValueKind::I64, FieldValueKind::I64)
264            | (FieldValueKind::U64, FieldValueKind::U64)
265            | (FieldValueKind::Bool, FieldValueKind::Bool)
266            | (FieldValueKind::Presence, FieldValueKind::Presence)
267    )
268}
269
270const fn is_valid_text_limit(value_kind: FieldValueKind, limit: Option<usize>) -> bool {
271    match (value_kind, limit) {
272        (FieldValueKind::Text, Some(limit)) => limit > 0 && limit <= MAX_TEXT_FIELD_BYTES,
273        (FieldValueKind::Text, None) => false,
274        (_, None) => true,
275        (_, Some(_)) => false,
276    }
277}
278
279const fn is_valid_field_name(name: &str) -> bool {
280    let bytes = name.as_bytes();
281    if bytes.is_empty() {
282        return false;
283    }
284
285    let mut index = 0;
286    let mut starts_segment = true;
287    while index < bytes.len() {
288        let byte = bytes[index];
289        if starts_segment {
290            if !byte.is_ascii_lowercase() {
291                return false;
292            }
293            starts_segment = false;
294        } else if byte == b'_' {
295            starts_segment = true;
296        } else if !byte.is_ascii_lowercase() && !byte.is_ascii_digit() {
297            return false;
298        }
299        index += 1;
300    }
301    !starts_segment
302}
303
304macro_rules! define_field {
305    ($name:ident, $marker:ty, $external_name:literal, $visibility:path, $kind:path, $limit:expr, $docs:literal) => {
306        #[doc = $docs]
307        pub const $name: FieldKey<$marker> = {
308            const SCHEMA: FieldSchema = match FieldSchema::try_new($external_name, $visibility, $kind, $limit) {
309                Some(schema) => schema,
310                None => panic!("invalid context field schema"),
311            };
312            checked_key(SCHEMA)
313        };
314    };
315}
316
317/// Catalog-owned context field keys.
318///
319/// Constants with different Rust names may intentionally share one external
320/// name when separate domains require different immutable schemas. Context
321/// uniqueness is still determined by the external name.
322pub mod fields {
323    use super::*;
324
325    define_field!(
326        ACTUAL_STATE,
327        TextField,
328        "actual",
329        ContextVisibility::Diagnostic,
330        FieldValueKind::Text,
331        Some(64),
332        "Actual client state."
333    );
334    define_field!(
335        ACTUAL_U64,
336        U64Field,
337        "actual",
338        ContextVisibility::Diagnostic,
339        FieldValueKind::U64,
340        None,
341        "Actual numeric version."
342    );
343    define_field!(
344        ACTUAL_BYTES,
345        U64Field,
346        "actual_bytes",
347        ContextVisibility::Public,
348        FieldValueKind::U64,
349        None,
350        "Actual message size in bytes."
351    );
352    define_field!(
353        ADDR,
354        TextField,
355        "addr",
356        ContextVisibility::Diagnostic,
357        FieldValueKind::Text,
358        Some(256),
359        "Network address used by a failed operation."
360    );
361    define_field!(
362        ALLOWED,
363        TextField,
364        "allowed",
365        ContextVisibility::Diagnostic,
366        FieldValueKind::Text,
367        Some(64),
368        "Allowed values for a validation failure."
369    );
370    define_field!(
371        ATTEMPTED,
372        BoolField,
373        "attempted",
374        ContextVisibility::Diagnostic,
375        FieldValueKind::Bool,
376        None,
377        "Whether installation was attempted."
378    );
379    define_field!(
380        BROKER,
381        TextField,
382        "broker",
383        ContextVisibility::Diagnostic,
384        FieldValueKind::Text,
385        Some(127),
386        "Broker identity involved in an operation."
387    );
388    define_field!(
389        BROKER_ADDR,
390        TextField,
391        "broker_addr",
392        ContextVisibility::Diagnostic,
393        FieldValueKind::Text,
394        Some(256),
395        "Broker address involved in an operation."
396    );
397    define_field!(
398        BROKER_CODE,
399        I64Field,
400        "broker_code",
401        ContextVisibility::Diagnostic,
402        FieldValueKind::I64,
403        None,
404        "Broker response code."
405    );
406    define_field!(
407        CLUSTER,
408        TextField,
409        "cluster",
410        ContextVisibility::Public,
411        FieldValueKind::Text,
412        Some(127),
413        "Cluster identity."
414    );
415    define_field!(
416        CLIENT_ROLE,
417        TextField,
418        "client_role",
419        ContextVisibility::Public,
420        FieldValueKind::Text,
421        Some(32),
422        "Client component role."
423    );
424    define_field!(
425        COMPONENT_NAME,
426        TextField,
427        "component",
428        ContextVisibility::Diagnostic,
429        FieldValueKind::Text,
430        Some(64),
431        "Component involved in a lifecycle failure."
432    );
433    define_field!(
434        CONSUMER,
435        TextField,
436        "consumer",
437        ContextVisibility::Diagnostic,
438        FieldValueKind::Text,
439        Some(127),
440        "Consumer identity."
441    );
442    define_field!(
443        CURRENT,
444        I64Field,
445        "current",
446        ContextVisibility::Public,
447        FieldValueKind::I64,
448        None,
449        "Current numeric value."
450    );
451    define_field!(
452        DURATION_MS,
453        U64Field,
454        "duration_ms",
455        ContextVisibility::Public,
456        FieldValueKind::U64,
457        None,
458        "Operation duration in milliseconds."
459    );
460    define_field!(
461        EXPECTED_STATE,
462        TextField,
463        "expected",
464        ContextVisibility::Diagnostic,
465        FieldValueKind::Text,
466        Some(64),
467        "Expected client state."
468    );
469    define_field!(
470        EXPECTED_U64,
471        U64Field,
472        "expected",
473        ContextVisibility::Diagnostic,
474        FieldValueKind::U64,
475        None,
476        "Expected numeric version."
477    );
478    define_field!(
479        FEATURE,
480        TextField,
481        "feature",
482        ContextVisibility::Public,
483        FieldValueKind::Text,
484        Some(64),
485        "Disabled feature name."
486    );
487    define_field!(
488        FILTER_KIND,
489        TextField,
490        "filter_kind",
491        ContextVisibility::Diagnostic,
492        FieldValueKind::Text,
493        Some(64),
494        "Filter failure classification."
495    );
496    define_field!(
497        FORMAT,
498        TextField,
499        "format",
500        ContextVisibility::Diagnostic,
501        FieldValueKind::Text,
502        Some(64),
503        "Serialization format classification."
504    );
505    define_field!(
506        FIELD,
507        TextField,
508        "field",
509        ContextVisibility::Public,
510        FieldValueKind::Text,
511        Some(64),
512        "Input field name."
513    );
514    define_field!(
515        FILTER_COMPILE_KIND,
516        TextField,
517        "filter_compile_kind",
518        ContextVisibility::Diagnostic,
519        FieldValueKind::Text,
520        Some(64),
521        "Filter compilation failure kind."
522    );
523    define_field!(
524        FILTER_COMPILE_POSITION,
525        U64Field,
526        "filter_compile_position",
527        ContextVisibility::Diagnostic,
528        FieldValueKind::U64,
529        None,
530        "Filter compilation byte position."
531    );
532    define_field!(
533        FILTER_COMPILE_SOURCE,
534        TextField,
535        "filter_compile_source",
536        ContextVisibility::Diagnostic,
537        FieldValueKind::Text,
538        Some(64),
539        "Filter compiler source classification."
540    );
541    define_field!(
542        FILTER_COMPILE_STAGE,
543        TextField,
544        "filter_compile_stage",
545        ContextVisibility::Diagnostic,
546        FieldValueKind::Text,
547        Some(64),
548        "Filter compilation stage."
549    );
550    define_field!(
551        GROUP,
552        TextField,
553        "group",
554        ContextVisibility::Public,
555        FieldValueKind::Text,
556        Some(127),
557        "Consumer group identity."
558    );
559    define_field!(
560        INSTALLED,
561        BoolField,
562        "installed",
563        ContextVisibility::Diagnostic,
564        FieldValueKind::Bool,
565        None,
566        "Whether a subscriber was installed."
567    );
568    define_field!(
569        INVARIANT,
570        TextField,
571        "invariant",
572        ContextVisibility::Diagnostic,
573        FieldValueKind::Text,
574        Some(128),
575        "Violated invariant classification."
576    );
577    define_field!(
578        KEY,
579        TextField,
580        "key",
581        ContextVisibility::Diagnostic,
582        FieldValueKind::Text,
583        Some(64),
584        "Configuration key name."
585    );
586    define_field!(
587        LEADER_ID,
588        U64Field,
589        "leader_id",
590        ContextVisibility::Diagnostic,
591        FieldValueKind::U64,
592        None,
593        "Controller leader identifier."
594    );
595    define_field!(
596        LIMIT,
597        U64Field,
598        "limit",
599        ContextVisibility::Diagnostic,
600        FieldValueKind::U64,
601        None,
602        "Capacity limit associated with a failure."
603    );
604    define_field!(
605        LIMIT_BYTES,
606        U64Field,
607        "limit_bytes",
608        ContextVisibility::Public,
609        FieldValueKind::U64,
610        None,
611        "Configured byte limit."
612    );
613    define_field!(
614        MASTER_ADDRESS,
615        TextField,
616        "master_address",
617        ContextVisibility::Diagnostic,
618        FieldValueKind::Text,
619        Some(256),
620        "Current master broker address."
621    );
622    define_field!(
623        MAX,
624        I64Field,
625        "max",
626        ContextVisibility::Public,
627        FieldValueKind::I64,
628        None,
629        "Maximum numeric value."
630    );
631    define_field!(
632        MAX_QUEUE_ID,
633        I64Field,
634        "max_queue_id",
635        ContextVisibility::Public,
636        FieldValueKind::I64,
637        None,
638        "Maximum queue identifier."
639    );
640    define_field!(
641        OFFSET,
642        I64Field,
643        "offset",
644        ContextVisibility::Diagnostic,
645        FieldValueKind::I64,
646        None,
647        "Storage or message offset."
648    );
649    define_field!(
650        OPERATION,
651        TextField,
652        "operation",
653        ContextVisibility::Public,
654        FieldValueKind::Text,
655        Some(64),
656        "Public operation classification."
657    );
658    define_field!(
659        OPERATION_DIAGNOSTIC,
660        TextField,
661        "operation",
662        ContextVisibility::Diagnostic,
663        FieldValueKind::Text,
664        Some(64),
665        "Diagnostic operation classification."
666    );
667    define_field!(
668        OBSERVABILITY_SIGNAL,
669        TextField,
670        "observability_signal",
671        ContextVisibility::Diagnostic,
672        FieldValueKind::Text,
673        Some(32),
674        "Observability signal classification."
675    );
676    define_field!(
677        ORDINAL,
678        U64Field,
679        "ordinal",
680        ContextVisibility::Public,
681        FieldValueKind::U64,
682        None,
683        "Protocol version ordinal."
684    );
685    define_field!(
686        PERMISSION_VALUE,
687        I64Field,
688        "value",
689        ContextVisibility::Public,
690        FieldValueKind::I64,
691        None,
692        "Invalid permission value."
693    );
694    define_field!(
695        PROPERTY,
696        TextField,
697        "property",
698        ContextVisibility::Public,
699        FieldValueKind::Text,
700        Some(127),
701        "Message property name."
702    );
703    define_field!(
704        PHASE,
705        TextField,
706        "phase",
707        ContextVisibility::Diagnostic,
708        FieldValueKind::Text,
709        Some(32),
710        "Operation phase at which the failure occurred."
711    );
712    define_field!(
713        POSITION,
714        U64Field,
715        "position",
716        ContextVisibility::Diagnostic,
717        FieldValueKind::U64,
718        None,
719        "Input position associated with a failure."
720    );
721    define_field!(
722        QUEUE_ID,
723        I64Field,
724        "queue_id",
725        ContextVisibility::Public,
726        FieldValueKind::I64,
727        None,
728        "Message queue identifier."
729    );
730    define_field!(
731        REQUEST_CODE,
732        I64Field,
733        "request_code",
734        ContextVisibility::Public,
735        FieldValueKind::I64,
736        None,
737        "RocketMQ request code."
738    );
739    define_field!(
740        REMOTE_ADDR,
741        TextField,
742        "remote_addr",
743        ContextVisibility::Diagnostic,
744        FieldValueKind::Text,
745        Some(256),
746        "Remote transport address."
747    );
748    define_field!(
749        REMOTE_CODE,
750        I64Field,
751        "remote_code",
752        ContextVisibility::Diagnostic,
753        FieldValueKind::I64,
754        None,
755        "Remote response code."
756    );
757    define_field!(
758        RESOURCE,
759        TextField,
760        "resource",
761        ContextVisibility::Public,
762        FieldValueKind::Text,
763        Some(127),
764        "Resource identity."
765    );
766    define_field!(
767        STORE_COMPONENT,
768        TextField,
769        "store_component",
770        ContextVisibility::Diagnostic,
771        FieldValueKind::Text,
772        Some(64),
773        "Storage component classification."
774    );
775    define_field!(
776        STORE_OPERATION,
777        TextField,
778        "store_operation",
779        ContextVisibility::Diagnostic,
780        FieldValueKind::Text,
781        Some(64),
782        "Storage operation classification."
783    );
784    define_field!(
785        SERIALIZATION_TYPE,
786        U64Field,
787        "serialization_type",
788        ContextVisibility::Public,
789        FieldValueKind::U64,
790        None,
791        "Protocol serialization type identifier."
792    );
793    define_field!(
794        TASK,
795        TextField,
796        "task",
797        ContextVisibility::Diagnostic,
798        FieldValueKind::Text,
799        Some(64),
800        "Background task classification."
801    );
802    define_field!(
803        TIMEOUT_MS,
804        U64Field,
805        "timeout_ms",
806        ContextVisibility::Public,
807        FieldValueKind::U64,
808        None,
809        "Timeout duration in milliseconds."
810    );
811    define_field!(
812        TOPIC,
813        TextField,
814        "topic",
815        ContextVisibility::Public,
816        FieldValueKind::Text,
817        Some(127),
818        "Topic identity."
819    );
820    define_field!(
821        DECLARED_SIZE,
822        I64Field,
823        "declared_size",
824        ContextVisibility::Diagnostic,
825        FieldValueKind::I64,
826        None,
827        "Declared record size."
828    );
829
830    define_field!(
831        AUTH_ERROR_PRESENT,
832        SecretPresenceField,
833        "auth_error",
834        ContextVisibility::SecretPresenceOnly,
835        FieldValueKind::Presence,
836        None,
837        "Presence of rendered authentication error detail."
838    );
839    define_field!(
840        CONTEXT_PRESENT,
841        SecretPresenceField,
842        "context",
843        ContextVisibility::SecretPresenceOnly,
844        FieldValueKind::Presence,
845        None,
846        "Presence of task context."
847    );
848    define_field!(
849        DETAIL_PRESENT,
850        SecretPresenceField,
851        "detail",
852        ContextVisibility::SecretPresenceOnly,
853        FieldValueKind::Presence,
854        None,
855        "Presence of arbitrary diagnostic detail."
856    );
857    define_field!(
858        CONTROLLER_ERROR_PRESENT,
859        SecretPresenceField,
860        "controller_error",
861        ContextVisibility::SecretPresenceOnly,
862        FieldValueKind::Presence,
863        None,
864        "Presence of rendered controller error detail."
865    );
866    define_field!(
867        CREDENTIALS_PRESENT,
868        SecretPresenceField,
869        "credentials_present",
870        ContextVisibility::SecretPresenceOnly,
871        FieldValueKind::Presence,
872        None,
873        "Presence of authentication credentials."
874    );
875    define_field!(
876        DOMAIN_ERROR_PRESENT,
877        SecretPresenceField,
878        "domain_error",
879        ContextVisibility::SecretPresenceOnly,
880        FieldValueKind::Presence,
881        None,
882        "Presence of rendered domain error detail."
883    );
884    define_field!(
885        ERROR_PRESENT,
886        SecretPresenceField,
887        "error",
888        ContextVisibility::SecretPresenceOnly,
889        FieldValueKind::Presence,
890        None,
891        "Presence of rendered error detail."
892    );
893    define_field!(
894        FILTER_PRESENT,
895        SecretPresenceField,
896        "filter",
897        ContextVisibility::SecretPresenceOnly,
898        FieldValueKind::Presence,
899        None,
900        "Presence of filter text."
901    );
902    define_field!(
903        FILTER_ERROR_PRESENT,
904        SecretPresenceField,
905        "filter_error",
906        ContextVisibility::SecretPresenceOnly,
907        FieldValueKind::Presence,
908        None,
909        "Presence of rendered filter error detail."
910    );
911    define_field!(
912        INTERNAL_ERROR_PRESENT,
913        SecretPresenceField,
914        "internal_error",
915        ContextVisibility::SecretPresenceOnly,
916        FieldValueKind::Presence,
917        None,
918        "Presence of rendered internal error detail."
919    );
920    define_field!(
921        HOST_PRESENT,
922        SecretPresenceField,
923        "host",
924        ContextVisibility::SecretPresenceOnly,
925        FieldValueKind::Presence,
926        None,
927        "Presence of a network host value."
928    );
929    define_field!(
930        INVALID_VALUE_PRESENT,
931        SecretPresenceField,
932        "invalid_value_present",
933        ContextVisibility::SecretPresenceOnly,
934        FieldValueKind::Presence,
935        None,
936        "Presence of an invalid secret-bearing value."
937    );
938    define_field!(
939        IO_ERROR_PRESENT,
940        SecretPresenceField,
941        "io_error",
942        ContextVisibility::SecretPresenceOnly,
943        FieldValueKind::Presence,
944        None,
945        "Presence of rendered I/O error detail."
946    );
947    define_field!(
948        MESSAGE_PRESENT,
949        SecretPresenceField,
950        "message",
951        ContextVisibility::SecretPresenceOnly,
952        FieldValueKind::Presence,
953        None,
954        "Presence of arbitrary error message detail."
955    );
956    define_field!(
957        PATH_PRESENT,
958        SecretPresenceField,
959        "path",
960        ContextVisibility::SecretPresenceOnly,
961        FieldValueKind::Presence,
962        None,
963        "Presence of a storage or configuration path."
964    );
965    define_field!(
966        PROTOCOL_ERROR_PRESENT,
967        SecretPresenceField,
968        "protocol_error",
969        ContextVisibility::SecretPresenceOnly,
970        FieldValueKind::Presence,
971        None,
972        "Presence of rendered protocol error detail."
973    );
974    define_field!(
975        REMOTE_ADDR_PRESENT,
976        SecretPresenceField,
977        "remote_addr",
978        ContextVisibility::SecretPresenceOnly,
979        FieldValueKind::Presence,
980        None,
981        "Presence of a remote transport address."
982    );
983    define_field!(
984        REASON_PRESENT,
985        SecretPresenceField,
986        "reason",
987        ContextVisibility::SecretPresenceOnly,
988        FieldValueKind::Presence,
989        None,
990        "Presence of arbitrary failure detail."
991    );
992    define_field!(
993        RPC_ERROR_PRESENT,
994        SecretPresenceField,
995        "rpc_error",
996        ContextVisibility::SecretPresenceOnly,
997        FieldValueKind::Presence,
998        None,
999        "Presence of rendered RPC error detail."
1000    );
1001    define_field!(
1002        SERIALIZATION_ERROR_PRESENT,
1003        SecretPresenceField,
1004        "serialization_error",
1005        ContextVisibility::SecretPresenceOnly,
1006        FieldValueKind::Presence,
1007        None,
1008        "Presence of rendered serialization error detail."
1009    );
1010    define_field!(
1011        SERVICE_ERROR_PRESENT,
1012        SecretPresenceField,
1013        "service_error",
1014        ContextVisibility::SecretPresenceOnly,
1015        FieldValueKind::Presence,
1016        None,
1017        "Presence of rendered service error detail."
1018    );
1019    define_field!(
1020        SOURCE_PRESENT,
1021        SecretPresenceField,
1022        "source_present",
1023        ContextVisibility::SecretPresenceOnly,
1024        FieldValueKind::Presence,
1025        None,
1026        "Presence of a typed source error."
1027    );
1028    define_field!(
1029        SOURCE_DETAIL_PRESENT,
1030        SecretPresenceField,
1031        "source",
1032        ContextVisibility::SecretPresenceOnly,
1033        FieldValueKind::Presence,
1034        None,
1035        "Presence of rendered source error detail."
1036    );
1037    define_field!(
1038        STORE_DETAIL_PRESENT,
1039        SecretPresenceField,
1040        "store_detail",
1041        ContextVisibility::SecretPresenceOnly,
1042        FieldValueKind::Presence,
1043        None,
1044        "Presence of storage diagnostic detail."
1045    );
1046    define_field!(
1047        VALUE_PRESENT,
1048        SecretPresenceField,
1049        "value",
1050        ContextVisibility::SecretPresenceOnly,
1051        FieldValueKind::Presence,
1052        None,
1053        "Presence of a configuration value."
1054    );
1055}
1056
1057#[cfg(test)]
1058mod tests {
1059    use super::*;
1060
1061    #[test]
1062    fn invalid_schemas_are_rejected() {
1063        for name in ["", "Upper", "two__parts", "trailing_", "9leading"] {
1064            assert_eq!(
1065                FieldSchema::try_new(name, ContextVisibility::Public, FieldValueKind::Text, Some(64)),
1066                None
1067            );
1068        }
1069        assert_eq!(
1070            FieldSchema::try_new("text", ContextVisibility::Public, FieldValueKind::Text, None),
1071            None
1072        );
1073        assert_eq!(
1074            FieldSchema::try_new("text", ContextVisibility::Public, FieldValueKind::Text, Some(0)),
1075            None
1076        );
1077        assert_eq!(
1078            FieldSchema::try_new("text", ContextVisibility::Public, FieldValueKind::Text, Some(257)),
1079            None
1080        );
1081        assert_eq!(
1082            FieldSchema::try_new("number", ContextVisibility::Public, FieldValueKind::U64, Some(8)),
1083            None
1084        );
1085        assert_eq!(
1086            FieldSchema::try_new(
1087                "secret",
1088                ContextVisibility::SecretPresenceOnly,
1089                FieldValueKind::Text,
1090                Some(64)
1091            ),
1092            None
1093        );
1094    }
1095
1096    #[test]
1097    fn same_external_name_can_have_distinct_immutable_schemas() {
1098        assert_eq!(
1099            fields::OPERATION.schema().name(),
1100            fields::OPERATION_DIAGNOSTIC.schema().name()
1101        );
1102        assert_ne!(fields::OPERATION.schema(), fields::OPERATION_DIAGNOSTIC.schema());
1103    }
1104}