protovalidate_buffa/
error_proto.rs1use buffa_descriptor::generated::descriptor::field_descriptor_proto::Type;
2
3use crate::{FieldPath, FieldType, Subscript, ValidationError, Violation, proto};
4
5pub enum Exposure {
6 Diagnostic,
7 #[cfg(feature = "connect")]
8 Public,
9}
10
11impl ValidationError {
12 #[must_use]
30 pub fn to_proto(&self) -> proto::Violations {
31 proto::Violations {
32 violations: self
33 .violations
34 .iter()
35 .map(|v| convert_violation(v, &Exposure::Diagnostic))
36 .collect(),
37 ..Default::default()
38 }
39 }
40}
41
42pub fn convert_violation(v: &Violation, exposure: &Exposure) -> proto::Violation {
43 proto::Violation {
44 field: convert_path(&v.field, exposure).into(),
45 rule: convert_path(&v.rule, exposure).into(),
46 rule_id: nonempty(&v.rule_id),
47 message: match exposure {
48 Exposure::Diagnostic => nonempty(&v.message),
49 #[cfg(feature = "connect")]
50 Exposure::Public => None,
51 },
52 for_key: v.for_key.then_some(true),
53 ..Default::default()
54 }
55}
56
57fn nonempty(value: &str) -> Option<String> {
58 (!value.is_empty()).then(|| value.to_owned())
59}
60
61fn convert_path(path: &FieldPath, exposure: &Exposure) -> Option<proto::FieldPath> {
62 if path.elements.is_empty() {
63 return None;
64 }
65 Some(proto::FieldPath {
66 elements: path
67 .elements
68 .iter()
69 .map(|element| proto::FieldPathElement {
70 field_number: element.field_number,
71 field_name: element.field_name.as_deref().map(str::to_owned),
72 field_type: element.field_type.map(convert_type),
73 key_type: element.key_type.map(convert_type),
74 value_type: element.value_type.map(convert_type),
75 subscript: element
76 .subscript
77 .as_ref()
78 .filter(|subscript| {
79 let _ = subscript; match exposure {
81 Exposure::Diagnostic => true,
82 #[cfg(feature = "connect")]
84 Exposure::Public => matches!(subscript, Subscript::Index(_)),
85 }
86 })
87 .map(convert_subscript),
88 ..Default::default()
89 })
90 .collect(),
91 ..Default::default()
92 })
93}
94
95fn convert_subscript(
96 subscript: &Subscript,
97) -> proto::__buffa::oneof::field_path_element::Subscript {
98 use proto::__buffa::oneof::field_path_element::Subscript as S;
99 match subscript {
100 Subscript::Index(value) => S::Index(*value),
101 Subscript::BoolKey(value) => S::BoolKey(*value),
102 Subscript::IntKey(value) => S::IntKey(*value),
103 Subscript::UintKey(value) => S::UintKey(*value),
104 Subscript::StringKey(value) => S::StringKey(value.to_string()),
105 }
106}
107
108const fn convert_type(field_type: FieldType) -> Type {
109 match field_type {
110 FieldType::Double => Type::TYPE_DOUBLE,
111 FieldType::Float => Type::TYPE_FLOAT,
112 FieldType::Int64 => Type::TYPE_INT64,
113 FieldType::Uint64 => Type::TYPE_UINT64,
114 FieldType::Int32 => Type::TYPE_INT32,
115 FieldType::Fixed64 => Type::TYPE_FIXED64,
116 FieldType::Fixed32 => Type::TYPE_FIXED32,
117 FieldType::Bool => Type::TYPE_BOOL,
118 FieldType::String => Type::TYPE_STRING,
119 FieldType::Group => Type::TYPE_GROUP,
120 FieldType::Message => Type::TYPE_MESSAGE,
121 FieldType::Bytes => Type::TYPE_BYTES,
122 FieldType::Uint32 => Type::TYPE_UINT32,
123 FieldType::Enum => Type::TYPE_ENUM,
124 FieldType::Sfixed32 => Type::TYPE_SFIXED32,
125 FieldType::Sfixed64 => Type::TYPE_SFIXED64,
126 FieldType::Sint32 => Type::TYPE_SINT32,
127 FieldType::Sint64 => Type::TYPE_SINT64,
128 }
129}