Skip to main content

AnswerSet

Trait AnswerSet 

Source
pub trait AnswerSet: Sized {
    // Required method
    fn deserialize_answers<'de, D>(
        deserializer: D,
        context: AnswerContext,
    ) -> Result<Self, D::Error>
       where D: Deserializer<'de>;
}
Expand description

A type the answers object of a response decodes into.

Answers implements it as a lookup by question name. A question set declared as a struct implements it by reading each answer into the field of the same name, which needs no map and no name string at all: the struct’s visitor matches the key and hands the value to NoulAnswer, ChoiceAnswer or ScoreAnswer, whose Deserialize implementations are the same single-pass readers Answers uses, fixed to one kind.

§Contract

Every implementation, written by hand or generated, keeps these rules; Answers and the struct example below keep them, and the tests of this module hold both to them.

  • Input. The deserializer yields exactly one JSON object, keyed by question name. Anything else (an array, a string, null) is an error at answers.
  • Order. The members of that object may arrive in any order, and inside one answer type may arrive after the members it governs. What a successful decode yields does not depend on either order, except which of two answers with one name is kept: the first in wire order, as the repeated-answer rule says. Which path a failure names can depend on order, as the next two rules say.
  • Wrong kind. An answer whose type is not the kind the field holds - including an answer that is not an object at all, or has no type - is an error at answers.<field>.type whenever type comes before the members of the field’s kind, which is the order the API writes. A typed field knows its kind before type arrives and reads those members as they come, so when a misshaped member of the field’s kind precedes a wrong type, the error is reported at that member, answers.<field>.<member>.
  • Wrong shape. A member of the right kind with the wrong shape is an error at answers.<field>.<member> when the kind is known as the member arrives: always for a typed field, and for Answers when type came first. Answers holds a member that arrives before type as raw text and checks it once the type is known, after the walk has left it, so it reports that failure at answers.<field>.
  • Two types. An answer that names type twice with two different values is an error at answers.<field>.type, whichever members it carries; naming the same type twice is accepted. (Upstream lets the last type win; an answer that contradicts itself is refused here instead.)
  • Repeated answer. When the object names one question twice, the first answer is the one the set holds. A struct keeps its field’s first answer and skips a later answer of the same name unread, as it skips an extra answer, so the later one’s kind and shape do not matter. Answers keeps every answer it reads, in wire order, and every lookup returns the first of them; it reads a later answer like any other, so one of the wrong shape is still an error there. A body both accept gives both the same answer.
  • Missing answer. A field with no answer is an error at answers.<field>, where <field> is the question’s wire name (what missing_field receives), not the Rust field’s identifier. A response with no answers member at all is an error at answers for every set that cannot be empty: the method is then called with an empty object, and whatever it fails with is reported as the missing member. A set that can be empty, as Answers can, decodes to its empty value.
  • Extra answers. An answer the type has no field for is skipped unread, whatever its kind or shape, and is never an error. It stays in the raw body. Answers keeps every answer of a kind this version models and skips the others the same way.
  • Allocation. Nothing is allocated beyond the storage of the fields themselves: keys are matched where they lie, never copied into a String, and no intermediate map or value tree is built.
  • Context. The AnswerContext is a sizing hint. An implementation may use it or ignore it, and the result is the same either way.

A type that does not implement the trait is refused where a response of it is asked for:

use typesafe_sdk::de::AnswerSet;

fn decode_into<A: AnswerSet>() {}

decode_into::<String>();

An implementation for a struct of three answers looks like this. The key is matched by a field identifier whose visitor only compares the text, so a key written with escapes works and no key is copied:

use std::fmt;

use serde::de::{self, Deserialize, Deserializer, IgnoredAny, MapAccess, Visitor};
use typesafe_sdk::{
    de::{AnswerContext, AnswerSet},
    response::{ChoiceAnswer, NoulAnswer, ScoreAnswer},
};

struct Ticket {
    spam: NoulAnswer,
    tone: ChoiceAnswer,
    quality: ScoreAnswer,
}

enum Field {
    Spam,
    Tone,
    Quality,
    Other,
}

impl AnswerSet for Ticket {
    fn deserialize_answers<'de, D>(deserializer: D, _: AnswerContext) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct TicketVisitor;

        impl<'de> Visitor<'de> for TicketVisitor {
            type Value = Ticket;

            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
                formatter.write_str("the answers of a Ticket")
            }

            fn visit_map<M>(self, mut map: M) -> Result<Ticket, M::Error>
            where
                M: MapAccess<'de>,
            {
                let (mut spam, mut tone, mut quality) = (None, None, None);
                while let Some(field) = map.next_key::<Field>()? {
                    match field {
                        Field::Spam if spam.is_none() => spam = Some(map.next_value()?),
                        Field::Tone if tone.is_none() => tone = Some(map.next_value()?),
                        Field::Quality if quality.is_none() => {
                            quality = Some(map.next_value()?);
                        }
                        // An answer the struct has no field for, or a
                        // later answer to a question already read: the
                        // first answer of a name is the one kept.
                        _ => {
                            map.next_value::<IgnoredAny>()?;
                        }
                    }
                }
                Ok(Ticket {
                    spam: spam.ok_or_else(|| de::Error::missing_field("spam"))?,
                    tone: tone.ok_or_else(|| de::Error::missing_field("tone"))?,
                    quality: quality.ok_or_else(|| de::Error::missing_field("quality"))?,
                })
            }
        }

        deserializer.deserialize_map(TicketVisitor)
    }
}

Required Methods§

Source

fn deserialize_answers<'de, D>( deserializer: D, context: AnswerContext, ) -> Result<Self, D::Error>
where D: Deserializer<'de>,

Reads the answers object of a response.

When a response carries no answers member at all, this is called with a deserializer of an empty object. An implementation that holds required answers fails there in the usual way, and the decoder reports that failure as the missing answers member.

§Errors

Returns the deserializer’s error when an answer is missing, is of the wrong kind, or does not have the shape its kind requires.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§