Skip to content

Commit

Permalink
Bump to 0.3.2
Browse files Browse the repository at this point in the history
  • Loading branch information
willcrichton committed Sep 21, 2023
1 parent c6f7823 commit 6a20f60
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 71 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ rm -rf js/packages/quiz/src/bindings crates/mdbook-quiz-schema/bindings

[tasks.init-bindings]
script = """
cargo test -p mdbook-quiz-schema --locked export_bindings
cargo test -p mdbook-quiz-schema --locked export_bindings --features ts
mkdir -p js/packages/quiz/src/bindings
cp crates/mdbook-quiz-schema/bindings/* js/packages/quiz/src/bindings
"""
Expand Down
8 changes: 6 additions & 2 deletions crates/mdbook-quiz-schema/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
[package]
name = "mdbook-quiz-schema"
version = "0.1.1"
version = "0.2.0"
authors = ["Will Crichton <[email protected]>"]
description = "Schema for quizzes used in mdbook-quiz"
license = "MIT OR Apache-2.0"
edition = "2021"
repository = "https://github.com/cognitive-engineering-lab/mdbook-quiz"

[features]
# for generating a JSON schema, used in conjunction with `cargo run --bin gen-json-schema`
json-schema = ["dep:schemars", "dep:serde_json"]

# for generating Typescript bindings, used in conjunction with `cargo test export_bindings`
ts = ["dep:ts-rs"]

[dependencies]
serde = {version = "1.0.188", features = ["derive"]}
ts-rs = "7.0.0"
ts-rs = {version = "7.0.0", optional = true}
schemars = {version = "0.8.15", optional = true}
serde_json = {version = "1.0.107", optional = true}

Expand Down
84 changes: 43 additions & 41 deletions crates/mdbook-quiz-schema/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,39 @@

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[cfg(feature = "ts")]
use ts_rs::TS;

#[cfg(feature = "json-schema")]
use schemars::JsonSchema;

/// A quiz is the top-level data structure in mdbook-quiz.
/// It represents a sequence of questions.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct Quiz {
/// The questions of the quiz.
pub questions: Vec<Question>,

/// Context for multipart questions.
///
/// Maps from a string key to a description of the question context.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub multipart: Option<HashMap<String, Markdown>>,
}

/// A [Markdown](https://commonmark.org/help/) string.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct Markdown(pub String);

/// An individual question. One of several fixed types.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
#[serde(tag = "type")]
pub enum Question {
/// A [`ShortAnswer`] question.
Expand All @@ -62,20 +64,20 @@ pub enum Question {
}

/// Fields common to all question types.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct QuestionFields<Prompt, Answer> {
/// A unique identifier for a given question.
///
/// Used primarily for telemetry, as a stable identifer for questions.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub id: Option<String>,

/// If this key exists, then this question is part of a multipart group.
/// The key must be contained in the [`Quiz::multipart`] map.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub multipart: Option<String>,

/// The contents of the prompt. Depends on the question type.
Expand All @@ -87,21 +89,21 @@ pub struct QuestionFields<Prompt, Answer> {
/// Additional context that explains the correct answer.
///
/// Only shown after the user has answered correctly or given up.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub context: Option<Markdown>,

/// If true, asks all users for a brief prose justification of their answer.
///
/// Useful for getting a qualitative sense of why users respond a particular way.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub prompt_explanation: Option<bool>,
}

/// The kind of response format (and subsequent input method) that accompanies
/// a given short answer questions.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
#[serde(rename_all = "lowercase")]
pub enum ShortAnswerResponseFormat {
/// A one-sentence response, given an `<input>`
Expand All @@ -115,74 +117,74 @@ pub enum ShortAnswerResponseFormat {
}

/// A prompt for a [`ShortAnswer`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct ShortAnswerPrompt {
/// The text of the prompt.
pub prompt: Markdown,

/// Format of the response.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub response: Option<ShortAnswerResponseFormat>,
}

/// An answer for a [`ShortAnswer`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct ShortAnswerAnswer {
/// The exact string that answers the question.
pub answer: String,

/// Other acceptable strings answers.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub alternatives: Option<Vec<String>>,
}

/// A question where users type in a response.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct ShortAnswer(pub QuestionFields<ShortAnswerPrompt, ShortAnswerAnswer>);

/// A prompt for a [`Tracing`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct TracingPrompt {
/// The contents of the program to trace.
pub program: String,
}

/// An answer for a [`Tracing`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct TracingAnswer {
/// True if the program should pass the compiler
pub does_compile: bool,

/// If doesCompile=true, then the contents of stdout after running the program
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub stdout: Option<String>,

/// If doesCompile=false, then the line number of the code causing the error
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub line_number: Option<usize>,
}

/// A question where users guess the output of a program.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct Tracing(pub QuestionFields<TracingPrompt, TracingAnswer>);

/// A prompt for a [`MultipleChoice`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
#[serde(rename_all = "camelCase")]
pub struct MultipleChoicePrompt {
/// The text of the prompt.
Expand All @@ -192,18 +194,18 @@ pub struct MultipleChoicePrompt {
pub distractors: Vec<Markdown>,

/// If defined, don't randomize distractors and put answer at this index.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub answer_index: Option<usize>,

/// If defined, don't randomize distractors and sort answers by content.
#[ts(optional)]
#[cfg_attr(feature = "ts", ts(optional))]
pub sort_answers: Option<bool>,
}

/// The type of response for a [`MultipleChoice`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
#[serde(untagged)]
pub enum MultipleChoiceAnswerFormat {
/// There is one correct answer.
Expand All @@ -214,18 +216,18 @@ pub enum MultipleChoiceAnswerFormat {
}

/// An answer for a [`MultipleChoice`] question.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct MultipleChoiceAnswer {
/// The text of the correct answer.
pub answer: MultipleChoiceAnswerFormat,
}

/// A question where users select among several possible answers.
#[derive(Debug, Serialize, Deserialize, TS)]
#[derive(Debug, Serialize, Deserialize)]
#[cfg_attr(feature = "ts", derive(TS))]
#[cfg_attr(feature = "json-schema", derive(JsonSchema))]
#[ts(export)]
pub struct MultipleChoice(pub QuestionFields<MultipleChoicePrompt, MultipleChoiceAnswer>);

#[cfg(test)]
Expand Down
4 changes: 2 additions & 2 deletions crates/mdbook-quiz-validate/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "mdbook-quiz-validate"
version = "0.1.1"
version = "0.2.0"
authors = ["Will Crichton <[email protected]>"]
description = "Input validation for quizzes used in mdbook-quiz"
license = "MIT OR Apache-2.0"
edition = "2021"
repository = "https://github.com/cognitive-engineering-lab/mdbook-quiz"

[dependencies]
mdbook-quiz-schema = { path = "../mdbook-quiz-schema", version = "0.1.1" }
mdbook-quiz-schema = { path = "../mdbook-quiz-schema", version = "0.2.0" }
zspell = "0.3.3"
markdown = "1.0.0-alpha.13"
toml = { workspace = true }
Expand Down
18 changes: 0 additions & 18 deletions crates/mdbook-quiz-validate/src/impls/multiple_choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,6 @@ impl Validate for MultipleChoicePrompt {
self.prompt.validate(cx, tomlcast!(value.table["prompt"]));

let distractors = tomlcast!(value.table["distractors"]);
cxensure!(
cx,
!self.distractors.is_empty(),
labels = vec![distractors.labeled_span()],
"Must be at least one distractor",
);
for (d, dv) in self.distractors.iter().zip(tomlcast!(distractors.array)) {
d.validate(cx, dv);
}
Expand Down Expand Up @@ -95,15 +89,3 @@ prompt.sortAnswers = true
"#;
assert!(crate::harness(contents).is_err());
}

#[test]
fn validate_mcq_empty_distractors() {
let contents = r#"
[[questions]]
type = "MultipleChoice"
prompt.prompt = ""
answer.answer = ""
prompt.distractors = [] # no distractors
"#;
assert!(crate::harness(contents).is_err());
}
8 changes: 4 additions & 4 deletions crates/mdbook-quiz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "mdbook-quiz"
authors = ["Will Crichton <[email protected]>"]
description = "Interactive quizzes for your mdBook"
license = "MIT OR Apache-2.0"
version = "0.3.1"
version = "0.3.2"
edition = "2021"
include = ["/src", "/js"]
repository = "https://github.com/cognitive-engineering-lab/mdbook-quiz"
Expand All @@ -22,13 +22,13 @@ toml = { workspace = true }
mdbook = "= 0.4.25"
mdbook-preprocessor-utils = "0.1"
mdbook-aquascope = {version = "0.3", optional = true}
mdbook-quiz-schema = {path = "../mdbook-quiz-schema", version = "0.1.1"}
mdbook-quiz-validate = {path = "../mdbook-quiz-validate", version = "0.1.1"}
mdbook-quiz-schema = {path = "../mdbook-quiz-schema", version = "0.2.0"}
mdbook-quiz-validate = {path = "../mdbook-quiz-validate", version = "0.2.0"}
toml_edit = "0.20.0"
uuid = {version = "1.4.1", features = ["v4"]}

[dev-dependencies]
mdbook-preprocessor-utils = { version = "0.1", features = ["testing"] }

[build-dependencies]
anyhow = "1"
anyhow = { workspace = true }

0 comments on commit 6a20f60

Please sign in to comment.