Skip to main content

protify/
types.rs

1use crate::*;
2use proto_types::field_descriptor_proto::Type as DescriptorType;
3
4/// A trait that specifies the protobuf type to which a rust type should be mapped.
5///
6/// Automatically implemented for all primitive types, as well as the targets of the [`proto_message`], [`proto_oneof`] and [`proto_enum`] macros, and all the types exported from the [`proto-types`](crate::proto_types) crate.
7pub trait AsProtoType {
8	/// The protobuf type to which this rust type should be mapped.
9	fn proto_type() -> ProtoType;
10}
11
12/// A trait that specifies the protobuf field type to which a rust type should be mapped (i.e. single, optional, repeated, map).
13///
14/// Automatically implemented for the types that implement [`AsProtoType`].
15pub trait AsProtoField {
16	fn as_proto_field() -> FieldType;
17}
18
19impl<T> AsProtoField for Option<T>
20where
21	T: AsProtoType,
22{
23	#[inline]
24	fn as_proto_field() -> FieldType {
25		let type_ = T::proto_type();
26
27		if type_.is_message() {
28			FieldType::Normal(type_)
29		} else {
30			FieldType::Optional(type_)
31		}
32	}
33}
34
35impl<T: AsProtoType> AsProtoField for T {
36	#[inline]
37	fn as_proto_field() -> FieldType {
38		FieldType::Normal(T::proto_type())
39	}
40}
41
42/// The types in which a protobuf field can be represented.
43#[derive(Debug, Clone, PartialEq, Eq)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
45pub enum FieldType {
46	Normal(ProtoType),
47	Map {
48		keys: ProtoMapKey,
49		values: ProtoType,
50	},
51	Repeated(ProtoType),
52	Optional(ProtoType),
53}
54
55pub(crate) struct Sealed;
56
57/// A sealed trait that defines the kind of protobuf map key that a rust type maps to.
58pub trait AsProtoMapKey {
59	fn as_proto_map_key() -> ProtoMapKey;
60
61	#[doc(hidden)]
62	#[allow(private_interfaces)]
63	const SEALED: Sealed;
64}
65
66/// An enum representing the types that are supported as map keys in protobuf.
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
69pub enum ProtoMapKey {
70	String,
71	Bool,
72	Int32,
73	Int64,
74	Sint32,
75	Sint64,
76	Sfixed32,
77	Sfixed64,
78	Fixed32,
79	Fixed64,
80	Uint32,
81	Uint64,
82}
83
84impl ProtoMapKey {
85	/// Converts to [`ProtoType`].
86	#[must_use]
87	pub fn into_type(self) -> ProtoType {
88		self.into()
89	}
90}
91
92impl From<ProtoMapKey> for ProtoType {
93	fn from(value: ProtoMapKey) -> Self {
94		Self::Scalar(value.into())
95	}
96}
97
98impl From<ProtoMapKey> for ProtoScalar {
99	fn from(value: ProtoMapKey) -> Self {
100		match value {
101			ProtoMapKey::String => Self::String,
102			ProtoMapKey::Bool => Self::Bool,
103			ProtoMapKey::Int32 => Self::Int32,
104			ProtoMapKey::Int64 => Self::Int64,
105			ProtoMapKey::Sint32 => Self::Sint32,
106			ProtoMapKey::Sint64 => Self::Sint64,
107			ProtoMapKey::Sfixed32 => Self::Sfixed32,
108			ProtoMapKey::Sfixed64 => Self::Sfixed64,
109			ProtoMapKey::Fixed32 => Self::Fixed32,
110			ProtoMapKey::Fixed64 => Self::Fixed64,
111			ProtoMapKey::Uint32 => Self::Uint32,
112			ProtoMapKey::Uint64 => Self::Uint64,
113		}
114	}
115}
116
117impl Display for ProtoMapKey {
118	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
119		match self {
120			Self::Int32 => write!(f, "int32"),
121			Self::Int64 => write!(f, "int64"),
122			Self::Uint32 => write!(f, "uint32"),
123			Self::Uint64 => write!(f, "uint64"),
124			Self::Sint32 => write!(f, "sint32"),
125			Self::Sint64 => write!(f, "sint64"),
126			Self::Fixed32 => write!(f, "fixed32"),
127			Self::Fixed64 => write!(f, "fixed64"),
128			Self::Sfixed32 => write!(f, "sfixed32"),
129			Self::Sfixed64 => write!(f, "sfixed64"),
130			Self::Bool => write!(f, "bool"),
131			Self::String => write!(f, "string"),
132		}
133	}
134}
135
136/// An enum representing the scalar types in protobuf.
137#[derive(Debug, Clone, PartialEq, Eq, Copy)]
138#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
139pub enum ProtoScalar {
140	Double,
141	Float,
142	Int32,
143	Int64,
144	Uint32,
145	Uint64,
146	Sint32,
147	Sint64,
148	Fixed32,
149	Fixed64,
150	Sfixed32,
151	Sfixed64,
152	Bool,
153	String,
154	Bytes,
155}
156
157impl From<ProtoScalar> for DescriptorType {
158	#[inline]
159	fn from(value: ProtoScalar) -> Self {
160		match value {
161			ProtoScalar::Double => Self::Double,
162			ProtoScalar::Float => Self::Float,
163			ProtoScalar::Int32 => Self::Int32,
164			ProtoScalar::Int64 => Self::Int64,
165			ProtoScalar::Uint32 => Self::Uint32,
166			ProtoScalar::Uint64 => Self::Uint64,
167			ProtoScalar::Sint32 => Self::Sint32,
168			ProtoScalar::Sint64 => Self::Sint64,
169			ProtoScalar::Fixed32 => Self::Fixed32,
170			ProtoScalar::Fixed64 => Self::Fixed64,
171			ProtoScalar::Sfixed32 => Self::Sfixed32,
172			ProtoScalar::Sfixed64 => Self::Sfixed64,
173			ProtoScalar::Bool => Self::Bool,
174			ProtoScalar::String => Self::String,
175			ProtoScalar::Bytes => Self::Bytes,
176		}
177	}
178}
179
180impl Display for ProtoScalar {
181	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
182		match self {
183			Self::Double => write!(f, "double"),
184			Self::Float => write!(f, "float"),
185			Self::Int32 => write!(f, "int32"),
186			Self::Int64 => write!(f, "int64"),
187			Self::Uint32 => write!(f, "uint32"),
188			Self::Uint64 => write!(f, "uint64"),
189			Self::Sint32 => write!(f, "sint32"),
190			Self::Sint64 => write!(f, "sint64"),
191			Self::Fixed32 => write!(f, "fixed32"),
192			Self::Fixed64 => write!(f, "fixed64"),
193			Self::Sfixed32 => write!(f, "sfixed32"),
194			Self::Sfixed64 => write!(f, "sfixed64"),
195			Self::Bool => write!(f, "bool"),
196			Self::String => write!(f, "string"),
197			Self::Bytes => write!(f, "bytes"),
198		}
199	}
200}
201
202/// An enum representing a protobuf type.
203#[derive(Debug, Clone, PartialEq, Eq)]
204#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
205pub enum ProtoType {
206	Scalar(ProtoScalar),
207	Message(ProtoPath),
208	Enum(ProtoPath),
209}
210
211impl From<ProtoType> for DescriptorType {
212	#[inline]
213	fn from(value: ProtoType) -> Self {
214		match value {
215			ProtoType::Scalar(scalar) => scalar.into(),
216			ProtoType::Message(_) => Self::Message,
217			ProtoType::Enum(_) => Self::Enum,
218		}
219	}
220}
221
222impl Display for ProtoType {
223	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
224		match self {
225			Self::Scalar(proto_scalar) => write!(f, "{proto_scalar}"),
226			Self::Message(proto_path) | Self::Enum(proto_path) => write!(f, "{proto_path}"),
227		}
228	}
229}
230
231impl ProtoType {
232	/// Returns `true` if the proto type is [`Message`].
233	///
234	/// [`Message`]: ProtoType::Message
235	#[must_use]
236	pub const fn is_message(&self) -> bool {
237		matches!(self, Self::Message { .. })
238	}
239
240	/// Returns `true` if the proto type is [`Enum`].
241	///
242	/// [`Enum`]: ProtoType::Enum
243	#[must_use]
244	pub const fn is_enum(&self) -> bool {
245		matches!(self, Self::Enum(..))
246	}
247}
248
249impl FieldType {
250	pub(crate) fn render(&self, current_package: &FixedStr) -> Cow<'_, str> {
251		let name = self.render_name(current_package);
252
253		match self {
254			Self::Normal(_) | Self::Map { .. } => name,
255			Self::Repeated(_) => format!("repeated {name}").into(),
256			Self::Optional(inner) => {
257				if inner.is_message() {
258					name
259				} else {
260					format!("optional {name}").into()
261				}
262			}
263		}
264	}
265
266	pub(crate) fn render_name(&self, current_package: &FixedStr) -> Cow<'_, str> {
267		match self {
268			Self::Normal(type_info) | Self::Repeated(type_info) | Self::Optional(type_info) => {
269				type_info.render_name(current_package)
270			}
271			Self::Map { keys, values } => format!(
272				"map<{}, {}>",
273				keys.into_type().render_name(current_package),
274				values.render_name(current_package)
275			)
276			.into(),
277		}
278	}
279}
280
281impl ProtoType {
282	pub(crate) fn render_name(&self, current_package: &FixedStr) -> Cow<'_, str> {
283		match self {
284			Self::Scalar(scalar) => scalar.to_string().into(),
285			Self::Message(path) | Self::Enum(path) => {
286				if *path.package == **current_package {
287					path.name.as_ref().into()
288				} else {
289					format!("{}.{}", path.package, path.name).into()
290				}
291			}
292		}
293	}
294
295	pub(crate) fn register_import(&self, imports: &mut FileImports) {
296		match self {
297			Self::Scalar { .. } => {}
298			Self::Message(path) | Self::Enum(path) => imports.insert_from_path(path),
299		}
300	}
301}
302
303impl ProtoPath {
304	pub(crate) fn render_name(&self, current_package: &FixedStr) -> Cow<'_, str> {
305		if self.package == *current_package {
306			self.name.as_ref().into()
307		} else {
308			format!("{}.{}", self.package, self.name).into()
309		}
310	}
311}
312
313/// A struct that represents a path to an item (message or enum) in a protobuf package.
314#[derive(Debug, Clone, PartialEq, Eq)]
315#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
316pub struct ProtoPath {
317	pub name: FixedStr,
318	pub package: FixedStr,
319	pub file: FixedStr,
320}
321
322impl Display for ProtoPath {
323	fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
324		let Self { name, package, .. } = self;
325
326		write!(f, "{package}.{name}")
327	}
328}