#[derive(QuestionSet)]
{
// Attributes available to this derive:
#[noul]
#[choice]
#[score]
#[question]
#[question_set]
}
Expand description
Implements QuestionSet and AnswerSet for a struct with one field
per question.
Available with the macros feature, which is on by default.
use typesafe_sdk::{ChoiceAnswer, NoulAnswer, QuestionSet, ScoreAnswer};
#[derive(QuestionSet)]
struct Ticket {
#[noul(instructions = "Is this about billing?", yes = "payments or invoices")]
billing: NoulAnswer,
#[choice(instructions = "What is the tone?", options("calm" = "neutral or polite", "angry"))]
tone: ChoiceAnswer,
#[score(instructions = "How urgent?", levels("can wait", "this week", "today"))]
urgency: ScoreAnswer,
}
assert_eq!(Ticket::prepared().names().collect::<Vec<_>>(), ["billing", "tone", "urgency"]);Declares a question set as a struct: one field per question, each holding that question’s answer.
The questions are validated and serialized while the program compiles,
into the same bytes Questions::prepare produces for them at run time.
QuestionSet::prepared hands out that set without allocating, and the
generated AnswerSet implementation reads each answer of a response
straight into its field.
§Attributes
Every field carries exactly one question attribute, and its type is the answer type of that question:
#[noul(instructions = "...", yes = "...", no = "...")] field: NoulAnswer
#[choice(instructions = "...", options("a" = "...", "b"))] field: ChoiceAnswer
#[score(instructions = "...", levels("low", "high"))] field: ScoreAnswer#[noul]: every key is optional, and#[noul]alone is a noul with nothing set.yesandnodescribe what counts as each outcome.#[choice]:options(...)is required; it lists the options in order, each an option name optionally followed by= "description". An option is listed once.instructionsis optional.#[score]:levels(...)is required and lists at least one level description, lowest first; a level’s position is its score.instructionsis optional.
Each key is given at most once. Every value is a string literal: text is
the only content the derive takes. Object or array content is a feature of
the runtime Questions builder.
A question is asked under its field’s name, r# removed. Another name,
such as one that is not a Rust identifier, is set with
#[question(name = "...")] on the field. No two fields answer to one name.
The field type is recognized by its last path segment, so NoulAnswer and
typesafe_sdk::NoulAnswer both work; a type alias does not, because a
derive sees the tokens of a type and not what they name. A field cannot be
an Option: the API answers every question it is asked, and a response
without one of the answers is refused as malformed.
The struct itself has named fields, at least one, and no generics. Its
name is not one of those the generated code gives to its own items:
__QuestionSetField, __QuestionSetFieldVisitor, __QuestionSetVisitor,
__D, __M and __private are refused.
§Crate path
The generated code reaches the SDK as ::typesafe_sdk. Where it is known
by another path, such as a re-export, the container attribute
#[question_set(crate = path::to::sdk)] names that path. A dependency
renamed in Cargo.toml, as sdk = { package = "typesafe-sdk-rust", ... },
is one such case: the generated code still names ::typesafe_sdk, so the
struct needs #[question_set(crate = ::sdk)].
§Limits
None on the number of options or levels: the API documents its limits as subject to change, so the server is the one to judge them.