Skip to main content

structured_proxy/transcode/
request.rs

1//! Request message construction for REST→gRPC transcoding.
2//!
3//! Builds the gRPC request message from three `google.api.http` sources, in
4//! precedence order: path parameters (highest), the request body, then query
5//! parameters (lowest, fill only). A JSON body is deserialized straight into
6//! the message; path and query values arrive as strings and are converted to
7//! each field's proto type as they are set.
8
9mod 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
24/// The path parameters of a request: each dotted field path the path binds,
25/// with its value.
26pub(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/// How the HTTP request body maps onto the gRPC request message.
48#[derive(Debug, Clone, PartialEq, Eq)]
49#[non_exhaustive]
50pub enum BodyMapping {
51    /// No body is read; fields come from path + query (typical for GET/DELETE).
52    None,
53    /// The entire body maps to the message root (`body: "*"`).
54    Root,
55    /// The body maps to a single named field of the message (`body: "field"`).
56    Field(String),
57}
58
59impl BodyMapping {
60    /// Parse the `body` value of a `google.api.http` rule.
61    ///
62    /// `""` (absent) → [`BodyMapping::None`], `"*"` → [`BodyMapping::Root`],
63    /// any other string → [`BodyMapping::Field`].
64    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/// The HTTP request body the [`BodyMapping`] reads.
74#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum Body<'a> {
76    /// No body is read. Under [`BodyMapping::Field`] the field still belongs
77    /// to the body (a raw `google.api.HttpBody` filled afterwards), so query
78    /// parameters leave it alone.
79    Absent,
80    /// A JSON body (ProtoJSON of the mapped message or field). Empty means
81    /// `{}`.
82    Json(&'a [u8]),
83    /// An `application/x-www-form-urlencoded` body, bound field by field like
84    /// query parameters.
85    Form(&'a [u8]),
86}
87
88impl<'a> Body<'a> {
89    /// The body as its media type (no parameters) says: a form for
90    /// `application/x-www-form-urlencoded`, JSON otherwise.
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use structured_proxy::transcode::request::Body;
96    ///
97    /// let form = Body::new(Some("application/x-www-form-urlencoded"), b"a=1");
98    /// assert_eq!(form, Body::Form(b"a=1"));
99    /// assert_eq!(Body::new(None, b"{}"), Body::Json(b"{}"));
100    /// ```
101    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
109/// Build the request message from the body mapping, path params, and query.
110///
111/// Path-bound fields win over the body, and the body wins over query parameters
112/// (query only fills fields the body did not set, even to their default).
113/// Unknown query, form and path keys are dropped rather than rejected,
114/// matching common transcoder behavior; an unknown key in a JSON body is an
115/// error, as ProtoJSON has it.
116///
117/// # Errors
118/// A body that is not the ProtoJSON of the mapped message or field, a form
119/// body mapped onto a field that is not a message, or a path, query or form
120/// value that is not a valid value of its field. The message is meant for the
121/// client, as the text of an `INVALID_ARGUMENT` answer.
122///
123/// # Examples
124///
125/// ```
126/// use std::collections::HashMap;
127/// use prost_reflect::{MessageDescriptor, Value};
128/// use structured_proxy::transcode::request::{build_request_message, Body, BodyMapping};
129///
130/// // message Page { int32 size = 1; string cursor = 2; }
131/// # fn page() -> MessageDescriptor {
132/// #     use prost_reflect::prost::Message;
133/// #     use prost_reflect::prost_types::{
134/// #         field_descriptor_proto::{Label, Type},
135/// #         DescriptorProto, FieldDescriptorProto, FileDescriptorProto, FileDescriptorSet,
136/// #     };
137/// #     let field = |name: &str, number, ty: Type| FieldDescriptorProto {
138/// #         name: Some(name.into()),
139/// #         number: Some(number),
140/// #         label: Some(Label::Optional as i32),
141/// #         r#type: Some(ty as i32),
142/// #         ..Default::default()
143/// #     };
144/// #     let file = FileDescriptorProto {
145/// #         name: Some("page.proto".into()),
146/// #         package: Some("example".into()),
147/// #         message_type: vec![DescriptorProto {
148/// #             name: Some("Page".into()),
149/// #             field: vec![field("size", 1, Type::Int32), field("cursor", 2, Type::String)],
150/// #             ..Default::default()
151/// #         }],
152/// #         syntax: Some("proto3".into()),
153/// #         ..Default::default()
154/// #     };
155/// #     let set = FileDescriptorSet { file: vec![file] };
156/// #     prost_reflect::DescriptorPool::decode(set.encode_to_vec().as_slice())
157/// #         .unwrap()
158/// #         .get_message_by_name("example.Page")
159/// #         .unwrap()
160/// # }
161/// let message = build_request_message(
162///     &page(),
163///     &BodyMapping::Root,
164///     Body::Json(br#"{"size": 0}"#),
165///     &HashMap::new(),
166///     Some("size=50&cursor=abc"),
167/// )
168/// .unwrap();
169/// // The body set `size`, to its default: the query cannot replace it.
170/// assert_eq!(message.get_field_by_name("size").unwrap().as_i32(), Some(0));
171/// // It left `cursor` out: the query fills it.
172/// assert_eq!(
173///     *message.get_field_by_name("cursor").unwrap(),
174///     Value::String("abc".into())
175/// );
176/// ```
177pub 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
187/// [`build_request_message`] for any form of the path parameters.
188pub(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    // Parsed without allocating wherever nothing needs unescaping.
196    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/// Which source a string value comes from, and so whether it overwrites.
204#[derive(Clone, Copy, PartialEq, Eq)]
205enum Binding {
206    /// Path parameters and form fields set their field.
207    Set,
208    /// Query parameters fill only what nothing else set.
209    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    // What the body and the path set matters only when a query can fill.
220    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    // The fields of the path being bound, reused across keys.
226    let mut path = Vec::new();
227
228    // Whether a field inside a well-known type was set from a string.
229    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        // An empty body is `{}`, whatever its media type.
241        (_, 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    // Path params win over everything (the router already matched them).
261    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
300/// Refuse a well-known type set field by field whose value its JSON form
301/// could not hold: a Timestamp's `nanos` past 999999999, a Duration whose
302/// `seconds` and `nanos` differ in sign, an `Any` of an unknown type.
303/// Checked once all keys are bound, as they arrive in any order.
304fn 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    // Writing the ProtoJSON checks the rest (an `Any` must name a known type
308    // and decode as it, a `FieldMask` path must round-trip through camelCase,
309    // a `Value` number must be finite); the text is not kept.
310    serde_json::to_writer(std::io::sink(), message).map_err(|e| invalid(&e))
311}
312
313/// Check every `Timestamp` and `Duration` in `message` against the ranges
314/// their definitions set. prost-reflect's writer checks only part of them and
315/// carries excess nanos into seconds, which would change the value.
316fn 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.proto: seconds from 0001-01-01T00:00:00Z
320        // to 9999-12-31T23:59:59Z, nanos in [0, 999999999].
321        "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.proto: seconds within +-315576000000,
331        // nanos within +-999999999, and nanos of the same sign as a non-zero
332        // seconds.
333        "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/// Where string values go: the field at the dotted path `key`, below the
362/// message field `prefix` of `root` when there is one, below `root` otherwise.
363#[derive(Clone, Copy)]
364struct Target<'a> {
365    root: &'a MessageDescriptor,
366    /// Whether the message the key starts in is a well-known type.
367    inside: bool,
368    prefix: Option<&'a FieldDescriptor>,
369    key: &'a str,
370    /// Every value of the key, in request order.
371    values: &'a [&'a str],
372}
373
374/// Deserialize a JSON body in one pass.
375///
376/// The path wins over the body, so a body value for a field the path binds
377/// never decides the request, even when it is not a valid value of that field.
378/// Such a body fails the one pass; it is then read again without those keys.
379/// Only a failing body pays for the second read.
380fn 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    // A `null` body sets nothing, like `{}`.
388    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    // Not JSON at all: the path cannot change that.
403    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
413/// Remove from `body` every key the path binds, under its JSON or proto name:
414/// the path sets those fields whatever the body holds.
415fn 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            // Under `body: "field"` the body is that field's value, so only
426            // the path keys below the field reach into it.
427            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
442/// Remove the field at `segments` below `desc` from the JSON object `value`.
443fn 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
484/// Deserialize `body` as the mapping says: the whole input message, or the
485/// value of one of its fields.
486fn 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            // Keyed by the JSON name, which prost-reflect looks up first, so
495            // no other field's JSON name can shadow it.
496            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
519/// Bind a form body field by field, into the input message or the message
520/// field the mapping names.
521fn 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            // The field is the body's even when the form is empty of it.
541            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
566/// A form body and the path parameters that override it.
567struct Form<'p, 'q, P: ?Sized> {
568    pairs: &'p [(Cow<'q, str>, Cow<'q, str>)],
569    path_params: &'p P,
570}
571
572/// Whether the dotted path key `bound` names the field at the end of `path`
573/// or one above it. Path keys start at the input message, as `path` does.
574fn 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/// Where the keys of a query or form start: `root`, or its message field
584/// `prefix`; `inside` when that message is a well-known type.
585#[derive(Clone, Copy)]
586struct Start<'a> {
587    root: &'a MessageDescriptor,
588    inside: bool,
589    prefix: Option<&'a FieldDescriptor>,
590}
591
592/// Bind every key of `pairs` to its field, with all the values of that field
593/// in request order, and tell whether a field inside a well-known type was
594/// set. A key the path binds (`path_params`, for a form) is left to the path.
595///
596/// Keys are grouped by the field they name, not by spelling: `tag_ids` and
597/// `tagIds` are one field, and binding them apart would let one replace the
598/// other's values.
599fn 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    // A stable sort keeps the values of each spelling in request order.
609    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    // Spellings that used a JSON name, by the proto-name key they stand for.
614    // Only a request that mixes the two spellings of a field pays for this.
615    let mut aliased: Vec<(String, &[usize])> = Vec::new();
616    // Spellings whose own values failed to bind, with the error.
617    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            // A failed assignment leaves the message as it was (parsing and
648            // the oneof check come before any write). The error is held: when
649            // the field also came under its JSON name, the values of both
650            // spellings decide the outcome below, not this spelling alone.
651            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    // Failures come in key order; the first one no merge below redoes wins.
656    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        // The proto-name spelling, bound above with its own values only.
675        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
703/// Assign `values` to the resolved `path` unless a path parameter binds it.
704/// True when it was assigned.
705fn 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    // The path sets these fields whatever the form holds, so their form
714    // values are never decoded, as a JSON body's are not.
715    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
722/// Set the field `target` names to its values (see [`resolve`] and
723/// [`assign`]). `path` is a buffer for the fields on the way. True when the
724/// field lies inside a well-known type.
725fn 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/// What [`resolve`] found besides the fields on the way.
740#[derive(Clone, Copy)]
741struct Resolved {
742    /// The field lies inside a well-known type.
743    inside: bool,
744    /// A segment named its field by a JSON name that is not its proto name.
745    aliased: bool,
746}
747
748/// Resolve the dotted key of `target` into the fields on the way, in `path`,
749/// and tell whether the field lies inside a well-known type.
750///
751/// Each segment names a field by its proto name or its JSON name, as
752/// ProtoJSON reads a key. `None` when the key is dropped: it names no field,
753/// or passes through a field that is not a singular message. A field inside
754/// a well-known type (`at.nanos` of a Timestamp) is bound like any other, and
755/// the value it leaves is checked once the request is built (see
756/// [`check_well_known`]).
757fn 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
793/// The field of `desc` named `name`, by its proto name or else its JSON name;
794/// `aliased` is set when only the JSON name matched.
795fn 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
808/// Set the field at the end of `path` to `values`: all of them for a repeated
809/// field, the last one otherwise. A [`Binding::Fill`] leaves a field the body
810/// or the path set, or one below a field they set to a non-object, alone.
811fn 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
844/// Refuse to set `field` over another member of its oneof, as ProtoJSON
845/// refuses a body naming two of them.
846fn 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
862/// The value of `field` from its string forms.
863fn 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
881/// A string as a value of `kind`, read as the ProtoJSON of that value would
882/// be: numbers in decimal (floats also `NaN`, `Infinity`, `-Infinity`), bools
883/// as `true`/`false`, enums by name, bytes as base64, and a message type from
884/// its JSON string form (a `Timestamp`, `Duration`, `FieldMask`, wrapper).
885fn 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
918/// Special float names of ProtoJSON.
919fn 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
928/// A ProtoJSON double: one of the special names, or a decimal with a finite
929/// value. Rust's parser also reads `inf`, `nan` and `infinity` in any case,
930/// and an overflowing decimal as infinity; ProtoJSON has none of those
931/// (protobuf JSON mapping, "float, double").
932fn 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
943/// A ProtoJSON float: a special name, or a finite decimal within the `f32`
944/// range.
945fn 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    // A special value casts to itself; a finite one is in range, so the cast
951    // rounds instead of saturating.
952    Ok(wide as f32)
953}
954
955/// Base64 as ProtoJSON reads it: the standard or the URL-safe alphabet,
956/// padded or not.
957fn 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;