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