1mod presence;
10
11use std::borrow::Cow;
12use std::collections::HashMap;
13
14use prost_reflect::{
15 DynamicMessage, FieldDescriptor, Kind, MessageDescriptor, ReflectMessage, Value,
16};
17use serde::de::value::StrDeserializer;
18use serde::Deserializer;
19use serde_json::Value as JsonValue;
20
21use presence::{has_special_json, OneEntry, Presence, Recording};
22
23pub(crate) trait PathFields {
26 fn fields(&self) -> impl Iterator<Item = (&str, &str)>;
27
28 fn is_empty(&self) -> bool {
29 self.fields().next().is_none()
30 }
31}
32
33impl PathFields for HashMap<String, String> {
34 fn fields(&self) -> impl Iterator<Item = (&str, &str)> {
35 self.iter()
36 .map(|(key, value)| (key.as_str(), value.as_str()))
37 }
38}
39
40impl PathFields for [(&str, Cow<'_, str>)] {
41 fn fields(&self) -> impl Iterator<Item = (&str, &str)> {
42 self.iter().map(|(key, value)| (*key, value.as_ref()))
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
48#[non_exhaustive]
49pub enum BodyMapping {
50 None,
52 Root,
54 Field(String),
56}
57
58impl BodyMapping {
59 pub fn parse(raw: &str) -> Self {
64 match raw {
65 "" => BodyMapping::None,
66 "*" => BodyMapping::Root,
67 field => BodyMapping::Field(field.to_string()),
68 }
69 }
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum Body<'a> {
75 Absent,
79 Json(&'a [u8]),
82 Form(&'a [u8]),
85}
86
87impl<'a> Body<'a> {
88 pub fn new(content_type: Option<&str>, bytes: &'a [u8]) -> Self {
101 match content_type {
102 Some(ct) if ct.starts_with("application/x-www-form-urlencoded") => Body::Form(bytes),
103 _ => Body::Json(bytes),
104 }
105 }
106}
107
108pub fn build_request_message(
177 input: &MessageDescriptor,
178 mapping: &BodyMapping,
179 body: Body<'_>,
180 path_params: &HashMap<String, String>,
181 raw_query: Option<&str>,
182) -> Result<DynamicMessage, String> {
183 build_message(input, mapping, body, path_params, raw_query)
184}
185
186pub(crate) fn build_message<P: PathFields + ?Sized>(
188 input: &MessageDescriptor,
189 mapping: &BodyMapping,
190 body: Body<'_>,
191 path_params: &P,
192 raw_query: Option<&str>,
193) -> Result<DynamicMessage, String> {
194 let query: Vec<(Cow<'_, str>, Cow<'_, str>)> = match raw_query {
196 Some(q) => url::form_urlencoded::parse(q.as_bytes()).collect(),
197 None => Vec::new(),
198 };
199 build(input, mapping, body, path_params, &query)
200}
201
202#[derive(Clone, Copy, PartialEq, Eq)]
204enum Binding {
205 Set,
207 Fill,
209}
210
211fn build<P: PathFields + ?Sized>(
212 input: &MessageDescriptor,
213 mapping: &BodyMapping,
214 body: Body<'_>,
215 path_params: &P,
216 query: &[(Cow<'_, str>, Cow<'_, str>)],
217) -> Result<DynamicMessage, String> {
218 let body_len = match body {
220 Body::Json(bytes) | Body::Form(bytes) => bytes.len(),
221 Body::Absent => 0,
222 };
223 let mut presence = (!query.is_empty()).then(|| Presence::for_body(body_len));
224 let mut path = Vec::new();
226
227 let mut inside = false;
229
230 let mut message = match (mapping, body) {
231 (BodyMapping::None, _) => DynamicMessage::new(input.clone()),
232 (BodyMapping::Field(name), Body::Absent) => {
233 if let (Some(presence), Some(field)) = (&mut presence, input.get_field_by_name(name)) {
234 presence.record_path(&[field], false);
235 }
236 DynamicMessage::new(input.clone())
237 }
238 (BodyMapping::Root, Body::Absent) => DynamicMessage::new(input.clone()),
239 (_, Body::Json(bytes) | Body::Form(bytes)) if bytes.is_empty() => {
241 json_body(input, mapping, b"{}", path_params, presence.as_mut())?
242 }
243 (_, Body::Json(bytes)) => json_body(input, mapping, bytes, path_params, presence.as_mut())?,
244 (_, Body::Form(bytes)) => {
245 let pairs: Vec<(Cow<'_, str>, Cow<'_, str>)> =
246 url::form_urlencoded::parse(bytes).collect();
247 let form = Form {
248 pairs: &pairs,
249 path_params,
250 };
251 let (message, form_inside) =
252 form_body(input, mapping, form, presence.as_mut(), &mut path)?;
253 inside = form_inside;
254 message
255 }
256 };
257
258 let root_inside = has_special_json(input);
259 for (key, raw) in path_params.fields() {
261 let target = Target {
262 root: input,
263 inside: root_inside,
264 prefix: None,
265 key,
266 values: &[raw],
267 };
268 inside |= bind(
269 &mut message,
270 target,
271 Binding::Set,
272 presence.as_mut(),
273 &mut path,
274 )?;
275 }
276
277 if let Some(presence) = &mut presence {
278 let start = Start {
279 root: input,
280 inside: root_inside,
281 prefix: None,
282 };
283 inside |= bind_pairs(
284 &mut message,
285 query,
286 start,
287 Binding::Fill,
288 Some(presence),
289 None::<&P>,
290 &mut path,
291 )?;
292 }
293 if inside {
294 check_well_known(&message)?;
295 }
296 Ok(message)
297}
298
299fn check_well_known(message: &DynamicMessage) -> Result<(), String> {
304 let invalid = |e: &dyn std::fmt::Display| format!("invalid value for a well-known type: {e}");
305 check_time_ranges(message).map_err(|e| invalid(&e))?;
306 serde_json::to_writer(std::io::sink(), message).map_err(|e| invalid(&e))
310}
311
312fn check_time_ranges(message: &DynamicMessage) -> Result<(), &'static str> {
316 let field = |name| message.get_field_by_name(name);
317 match message.descriptor().full_name() {
318 "google.protobuf.Timestamp" => {
321 let seconds = field("seconds").and_then(|v| v.as_i64()).unwrap_or(0);
322 let nanos = field("nanos").and_then(|v| v.as_i32()).unwrap_or(0);
323 if !(-62_135_596_800..=253_402_300_799).contains(&seconds)
324 || !(0..=999_999_999).contains(&nanos)
325 {
326 return Err("timestamp out of range");
327 }
328 }
329 "google.protobuf.Duration" => {
333 let seconds = field("seconds").and_then(|v| v.as_i64()).unwrap_or(0);
334 let nanos = field("nanos").and_then(|v| v.as_i32()).unwrap_or(0);
335 if seconds.unsigned_abs() > 315_576_000_000
336 || nanos.unsigned_abs() > 999_999_999
337 || (seconds > 0 && nanos < 0)
338 || (seconds < 0 && nanos > 0)
339 {
340 return Err("duration out of range");
341 }
342 }
343 _ => {}
344 }
345 for (_, value) in message.fields() {
346 check_value_time_ranges(value)?;
347 }
348 Ok(())
349}
350
351fn check_value_time_ranges(value: &Value) -> Result<(), &'static str> {
352 match value {
353 Value::Message(inner) => check_time_ranges(inner),
354 Value::List(items) => items.iter().try_for_each(check_value_time_ranges),
355 Value::Map(entries) => entries.values().try_for_each(check_value_time_ranges),
356 _ => Ok(()),
357 }
358}
359
360#[derive(Clone, Copy)]
363struct Target<'a> {
364 root: &'a MessageDescriptor,
365 inside: bool,
367 prefix: Option<&'a FieldDescriptor>,
368 key: &'a str,
369 values: &'a [&'a str],
371}
372
373fn json_body<P: PathFields + ?Sized>(
380 input: &MessageDescriptor,
381 mapping: &BodyMapping,
382 bytes: &[u8],
383 path_params: &P,
384 mut presence: Option<&mut Presence>,
385) -> Result<DynamicMessage, String> {
386 if matches!(mapping, BodyMapping::Root) && trim_json_whitespace(bytes) == b"null" {
388 return Ok(DynamicMessage::new(input.clone()));
389 }
390 let mut de = serde_json::Deserializer::from_slice(bytes);
391 let first = deserialize(input, mapping, &mut de, presence.as_deref_mut())
392 .and_then(|message| de.end().map(|()| message));
393 let error = match first {
394 Ok(message) => return Ok(message),
395 Err(error) => error,
396 };
397 let fail = |e: serde_json::Error| format!("failed to decode request body: {e}");
398 if path_params.is_empty() {
399 return Err(fail(error));
400 }
401 let Ok(mut value) = serde_json::from_slice::<JsonValue>(bytes) else {
403 return Err(fail(error));
404 };
405 strip_path_fields(&mut value, input, mapping, path_params);
406 if let Some(presence) = presence.as_deref_mut() {
407 presence.clear();
408 }
409 deserialize(input, mapping, value, presence).map_err(fail)
410}
411
412fn strip_path_fields<P: PathFields + ?Sized>(
415 body: &mut JsonValue,
416 input: &MessageDescriptor,
417 mapping: &BodyMapping,
418 path_params: &P,
419) {
420 for (key, _) in path_params.fields() {
421 let mut segments = key.split('.');
422 let desc = match mapping {
423 BodyMapping::Root => input.clone(),
424 BodyMapping::Field(name) => {
427 if segments.next() != Some(name.as_str()) {
428 continue;
429 }
430 match input.get_field_by_name(name).map(|f| f.kind()) {
431 Some(Kind::Message(inner)) => inner,
432 _ => continue,
433 }
434 }
435 BodyMapping::None => return,
436 };
437 remove_path(body, desc, segments);
438 }
439}
440
441fn remove_path<'k>(
443 mut value: &mut JsonValue,
444 mut desc: MessageDescriptor,
445 segments: impl Iterator<Item = &'k str>,
446) {
447 let mut segments = segments.peekable();
448 while let Some(segment) = segments.next() {
449 let Some(field) = field_named(&desc, segment, &mut false) else {
450 return;
451 };
452 let Some(object) = value.as_object_mut() else {
453 return;
454 };
455 if segments.peek().is_none() {
456 object.remove(field.json_name());
457 object.remove(field.name());
458 return;
459 }
460 let Kind::Message(inner) = field.kind() else {
461 return;
462 };
463 let key = if object.contains_key(field.json_name()) {
464 field.json_name()
465 } else {
466 field.name()
467 };
468 let Some(next) = object.get_mut(key) else {
469 return;
470 };
471 value = next;
472 desc = inner;
473 }
474}
475
476fn trim_json_whitespace(bytes: &[u8]) -> &[u8] {
477 let ws = |b: &u8| matches!(b, b' ' | b'\t' | b'\n' | b'\r');
478 let start = bytes.iter().position(|b| !ws(b)).unwrap_or(bytes.len());
479 let end = bytes.iter().rposition(|b| !ws(b)).map_or(start, |i| i + 1);
480 &bytes[start..end]
481}
482
483fn deserialize<'de, D: Deserializer<'de>>(
486 input: &MessageDescriptor,
487 mapping: &BodyMapping,
488 body: D,
489 presence: Option<&mut Presence>,
490) -> Result<DynamicMessage, D::Error> {
491 match mapping {
492 BodyMapping::Field(name) => {
493 let field = input.get_field_by_name(name);
496 let key = field.as_ref().map_or(name.as_str(), |f| f.json_name());
497 deserialize_input(input, OneEntry { key, value: body }, presence)
498 }
499 _ => deserialize_input(input, body, presence),
500 }
501}
502
503fn deserialize_input<'de, D: Deserializer<'de>>(
504 input: &MessageDescriptor,
505 de: D,
506 presence: Option<&mut Presence>,
507) -> Result<DynamicMessage, D::Error> {
508 let Some(presence) = presence else {
509 return DynamicMessage::deserialize(input.clone(), de);
510 };
511 if has_special_json(input) {
512 presence.record_whole();
513 return DynamicMessage::deserialize(input.clone(), de);
514 }
515 DynamicMessage::deserialize(input.clone(), Recording::root(de, presence))
516}
517
518fn form_body<P: PathFields + ?Sized>(
521 input: &MessageDescriptor,
522 mapping: &BodyMapping,
523 form: Form<'_, '_, P>,
524 mut presence: Option<&mut Presence>,
525 path: &mut Vec<FieldDescriptor>,
526) -> Result<(DynamicMessage, bool), String> {
527 let Form { pairs, path_params } = form;
528 let mut message = DynamicMessage::new(input.clone());
529 let (prefix, start_inside) = match mapping {
530 BodyMapping::Field(name) => {
531 let (field, inner) = input
532 .get_field_by_name(name)
533 .filter(|f| !f.is_list() && !f.is_map())
534 .and_then(|f| match f.kind() {
535 Kind::Message(inner) => Some((f, inner)),
536 _ => None,
537 })
538 .ok_or_else(|| format!("a form body cannot fill field `{name}`"))?;
539 message.get_field_mut(&field);
541 if let Some(presence) = presence.as_deref_mut() {
542 presence.record_path(std::slice::from_ref(&field), true);
543 }
544 (Some(field), has_special_json(&inner))
545 }
546 _ => (None, has_special_json(input)),
547 };
548 let start = Start {
549 root: input,
550 inside: start_inside,
551 prefix: prefix.as_ref(),
552 };
553 let inside = bind_pairs(
554 &mut message,
555 pairs,
556 start,
557 Binding::Set,
558 presence,
559 Some(path_params),
560 path,
561 )?;
562 Ok((message, inside))
563}
564
565struct Form<'p, 'q, P: ?Sized> {
567 pairs: &'p [(Cow<'q, str>, Cow<'q, str>)],
568 path_params: &'p P,
569}
570
571fn covers(bound: &str, path: &[FieldDescriptor]) -> bool {
574 let mut fields = path.iter();
575 bound.split('.').all(|segment| {
576 fields
577 .next()
578 .is_some_and(|f| segment == f.name() || segment == f.json_name())
579 })
580}
581
582#[derive(Clone, Copy)]
585struct Start<'a> {
586 root: &'a MessageDescriptor,
587 inside: bool,
588 prefix: Option<&'a FieldDescriptor>,
589}
590
591fn bind_pairs<P: PathFields + ?Sized>(
599 message: &mut DynamicMessage,
600 pairs: &[(Cow<'_, str>, Cow<'_, str>)],
601 start: Start<'_>,
602 binding: Binding,
603 mut presence: Option<&mut Presence>,
604 path_params: Option<&P>,
605 path: &mut Vec<FieldDescriptor>,
606) -> Result<bool, String> {
607 let mut order: Vec<usize> = (0..pairs.len()).collect();
609 order.sort_by(|&a, &b| pairs[a].0.cmp(&pairs[b].0));
610 let mut values = Vec::new();
611 let mut inside = false;
612 let mut aliased: Vec<(String, &[usize])> = Vec::new();
615 let mut failed: Vec<(&str, String)> = Vec::new();
617 for run in order.chunk_by(|&a, &b| pairs[a].0 == pairs[b].0) {
618 values.clear();
619 values.extend(run.iter().map(|&i| pairs[i].1.as_ref()));
620 let target = Target {
621 root: start.root,
622 inside: start.inside,
623 prefix: start.prefix,
624 key: &pairs[run[0]].0,
625 values: &values,
626 };
627 let Some(resolved) = resolve(target, path) else {
628 continue;
629 };
630 if resolved.aliased {
631 let own = &path[usize::from(start.prefix.is_some())..];
632 let canonical = own.iter().map(|f| f.name()).collect::<Vec<_>>().join(".");
633 aliased.push((canonical, run));
634 continue;
635 }
636 let bound = bind_resolved(
637 message,
638 path,
639 &values,
640 binding,
641 presence.as_deref_mut(),
642 path_params,
643 );
644 match bound {
645 Ok(set) => inside |= set && resolved.inside,
646 Err(e) => failed.push((pairs[run[0]].0.as_ref(), e)),
651 }
652 }
653 aliased.sort_by(|a, b| a.0.cmp(&b.0));
654 let covered = |key: &str| {
656 aliased
657 .binary_search_by(|(c, _)| c.as_str().cmp(key))
658 .is_ok()
659 };
660 if let Some((_, e)) = failed.into_iter().find(|(key, _)| !covered(key)) {
661 return Err(e);
662 }
663 if aliased.is_empty() {
664 return Ok(inside);
665 }
666 let mut indices = Vec::new();
667 for group in aliased.chunk_by(|a, b| a.0 == b.0) {
668 let canonical = group[0].0.as_str();
669 indices.clear();
670 for (_, run) in group {
671 indices.extend_from_slice(run);
672 }
673 let from = order.partition_point(|&i| pairs[i].0.as_ref() < canonical);
675 let to = order.partition_point(|&i| pairs[i].0.as_ref() <= canonical);
676 indices.extend_from_slice(&order[from..to]);
677 indices.sort_unstable();
678 values.clear();
679 values.extend(indices.iter().map(|&i| pairs[i].1.as_ref()));
680 let target = Target {
681 root: start.root,
682 inside: start.inside,
683 prefix: start.prefix,
684 key: canonical,
685 values: &values,
686 };
687 let Some(resolved) = resolve(target, path) else {
688 continue;
689 };
690 inside |= bind_resolved(
691 message,
692 path,
693 &values,
694 binding,
695 presence.as_deref_mut(),
696 path_params,
697 )? && resolved.inside;
698 }
699 Ok(inside)
700}
701
702fn bind_resolved<P: PathFields + ?Sized>(
705 message: &mut DynamicMessage,
706 path: &[FieldDescriptor],
707 values: &[&str],
708 binding: Binding,
709 presence: Option<&mut Presence>,
710 path_params: Option<&P>,
711) -> Result<bool, String> {
712 if path_params.is_some_and(|bound| bound.fields().any(|(key, _)| covers(key, path))) {
715 return Ok(false);
716 }
717 assign(message, path, values, binding, presence)?;
718 Ok(true)
719}
720
721fn bind(
725 message: &mut DynamicMessage,
726 target: Target<'_>,
727 binding: Binding,
728 presence: Option<&mut Presence>,
729 path: &mut Vec<FieldDescriptor>,
730) -> Result<bool, String> {
731 let Some(resolved) = resolve(target, path) else {
732 return Ok(false);
733 };
734 assign(message, path, target.values, binding, presence)?;
735 Ok(resolved.inside)
736}
737
738#[derive(Clone, Copy)]
740struct Resolved {
741 inside: bool,
743 aliased: bool,
745}
746
747fn resolve(target: Target<'_>, path: &mut Vec<FieldDescriptor>) -> Option<Resolved> {
757 path.clear();
758 let mut inside = target.inside;
759 let mut aliased = false;
760 let mut owned;
761 let mut desc = match target.prefix {
762 Some(field) => {
763 let Kind::Message(inner) = field.kind() else {
764 return None;
765 };
766 path.push(field.clone());
767 owned = inner;
768 &owned
769 }
770 None => target.root,
771 };
772 let mut segments = target.key.split('.');
773 let mut leaf = segments.next().unwrap_or_default();
774 for next in segments {
775 let field = field_named(desc, leaf, &mut aliased)?;
776 let Kind::Message(inner) = field.kind() else {
777 return None;
778 };
779 if field.is_list() || field.is_map() {
780 return None;
781 }
782 inside |= has_special_json(&inner);
783 path.push(field);
784 owned = inner;
785 desc = &owned;
786 leaf = next;
787 }
788 path.push(field_named(desc, leaf, &mut aliased)?);
789 Some(Resolved { inside, aliased })
790}
791
792fn field_named(
795 desc: &MessageDescriptor,
796 name: &str,
797 aliased: &mut bool,
798) -> Option<FieldDescriptor> {
799 if let Some(field) = desc.get_field_by_name(name) {
800 return Some(field);
801 }
802 let field = desc.get_field_by_json_name(name)?;
803 *aliased = true;
804 Some(field)
805}
806
807fn assign(
811 message: &mut DynamicMessage,
812 path: &[FieldDescriptor],
813 values: &[&str],
814 binding: Binding,
815 mut presence: Option<&mut Presence>,
816) -> Result<(), String> {
817 if binding == Binding::Fill && presence.as_deref_mut().is_some_and(|p| p.blocks(path)) {
818 return Ok(());
819 }
820
821 let (field, parents) = path
822 .split_last()
823 .expect("a resolved path ends in its field");
824 let value = field_value(field, values)?;
825 let mut holder = message;
826 for parent in parents {
827 check_oneof(holder, parent)?;
828 holder = match holder.get_field_mut(parent) {
829 Value::Message(inner) => inner,
830 _ => unreachable!("a singular message field holds a message"),
831 };
832 }
833 check_oneof(holder, field)?;
834 holder.set_field(field, value);
835 if binding == Binding::Set {
836 if let Some(presence) = presence {
837 presence.record_path(path, false);
838 }
839 }
840 Ok(())
841}
842
843fn check_oneof(message: &DynamicMessage, field: &FieldDescriptor) -> Result<(), String> {
846 let Some(oneof) = field.containing_oneof() else {
847 return Ok(());
848 };
849 if oneof
850 .fields()
851 .any(|other| other.number() != field.number() && message.has_field(&other))
852 {
853 return Err(format!(
854 "multiple fields provided for oneof '{}'",
855 oneof.name()
856 ));
857 }
858 Ok(())
859}
860
861fn field_value(field: &FieldDescriptor, raw: &[&str]) -> Result<Value, String> {
863 let invalid = |e: String| format!("invalid value for field `{}`: {e}", field.name());
864 if field.is_map() {
865 return Err(invalid("a map field cannot be set from a string".into()));
866 }
867 let kind = field.kind();
868 if field.is_list() {
869 return raw
870 .iter()
871 .map(|raw| scalar(&kind, raw))
872 .collect::<Result<Vec<_>, _>>()
873 .map(Value::List)
874 .map_err(invalid);
875 }
876 let raw = raw.last().expect("a bound key has a value");
877 scalar(&kind, raw).map_err(invalid)
878}
879
880fn scalar(kind: &Kind, raw: &str) -> Result<Value, String> {
885 Ok(match kind {
886 Kind::Double => Value::F64(parse_f64(raw)?),
887 Kind::Float => Value::F32(parse_f32(raw)?),
888 Kind::Int32 | Kind::Sint32 | Kind::Sfixed32 => Value::I32(parse(raw)?),
889 Kind::Int64 | Kind::Sint64 | Kind::Sfixed64 => Value::I64(parse(raw)?),
890 Kind::Uint32 | Kind::Fixed32 => Value::U32(parse(raw)?),
891 Kind::Uint64 | Kind::Fixed64 => Value::U64(parse(raw)?),
892 Kind::Bool => Value::Bool(parse(raw)?),
893 Kind::String => Value::String(raw.to_owned()),
894 Kind::Bytes => Value::Bytes(decode_base64(raw)?.into()),
895 Kind::Enum(desc) => Value::EnumNumber(
896 desc.get_value_by_name(raw)
897 .ok_or_else(|| format!("unrecognized enum value '{raw}'"))?
898 .number(),
899 ),
900 Kind::Message(desc) => Value::Message(
901 DynamicMessage::deserialize(
902 desc.clone(),
903 StrDeserializer::<serde::de::value::Error>::new(raw),
904 )
905 .map_err(|e| e.to_string())?,
906 ),
907 })
908}
909
910fn parse<T: std::str::FromStr>(raw: &str) -> Result<T, String>
911where
912 T::Err: std::fmt::Display,
913{
914 raw.parse().map_err(|e: T::Err| e.to_string())
915}
916
917fn special_float(raw: &str) -> Option<f64> {
919 match raw {
920 "Infinity" => Some(f64::INFINITY),
921 "-Infinity" => Some(f64::NEG_INFINITY),
922 "NaN" => Some(f64::NAN),
923 _ => None,
924 }
925}
926
927fn parse_f64(raw: &str) -> Result<f64, String> {
932 if let Some(special) = special_float(raw) {
933 return Ok(special);
934 }
935 match raw.parse::<f64>() {
936 Ok(value) if value.is_finite() => Ok(value),
937 Ok(_) => Err("value out of range, or not a decimal number".to_string()),
938 Err(e) => Err(e.to_string()),
939 }
940}
941
942fn parse_f32(raw: &str) -> Result<f32, String> {
945 let wide = parse_f64(raw)?;
946 if wide.is_finite() && (wide < f64::from(f32::MIN) || wide > f64::from(f32::MAX)) {
947 return Err("float value out of range".to_string());
948 }
949 Ok(wide as f32)
952}
953
954fn decode_base64(raw: &str) -> Result<Vec<u8>, String> {
957 use base64::alphabet;
958 use base64::engine::{DecodePaddingMode, GeneralPurpose, GeneralPurposeConfig};
959 use base64::{DecodeError, Engine};
960
961 const CONFIG: GeneralPurposeConfig = GeneralPurposeConfig::new()
962 .with_decode_allow_trailing_bits(true)
963 .with_decode_padding_mode(DecodePaddingMode::Indifferent);
964 const STANDARD: GeneralPurpose = GeneralPurpose::new(&alphabet::STANDARD, CONFIG);
965 const URL_SAFE: GeneralPurpose = GeneralPurpose::new(&alphabet::URL_SAFE, CONFIG);
966
967 let mut buf = Vec::new();
968 match STANDARD.decode_vec(raw, &mut buf) {
969 Ok(()) => Ok(buf),
970 Err(DecodeError::InvalidByte(_, b'-' | b'_')) => {
971 buf.clear();
972 URL_SAFE
973 .decode_vec(raw, &mut buf)
974 .map(|()| buf)
975 .map_err(|e| format!("invalid base64: {e}"))
976 }
977 Err(e) => Err(format!("invalid base64: {e}")),
978 }
979}
980
981#[cfg(test)]
982mod tests;