directive: test parsing builtins
This commit is contained in:
parent
3fd74b7fd5
commit
ff5261ae60
200
src/directive.rs
200
src/directive.rs
@ -12,7 +12,7 @@ pub struct TypeSystem {
|
||||
/// The definition of a standard directive.
|
||||
///
|
||||
/// Note that this type is an input to the parser, not an output.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Default)]
|
||||
pub struct Directive {
|
||||
/// The provider's name for the directive.
|
||||
name: String,
|
||||
@ -79,28 +79,14 @@ mod serial {
|
||||
pub directives: Vec<Directive>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields, tag = "kind")]
|
||||
pub enum TypeKind {
|
||||
Text,
|
||||
Speech,
|
||||
Character,
|
||||
Duration,
|
||||
Asset {
|
||||
tags: Vec<String>,
|
||||
},
|
||||
Enum {
|
||||
#[serde(rename = "enum")]
|
||||
enm: Vec<String>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||
pub struct Type {
|
||||
pub name: String,
|
||||
pub doc: String,
|
||||
pub kind: TypeKind,
|
||||
pub kind: String,
|
||||
pub tags: Option<Vec<String>>,
|
||||
#[serde(rename = "enum")]
|
||||
pub enm: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -133,6 +119,16 @@ pub enum Error {
|
||||
#[snafu(display("invalid directive definition file: {source}"))]
|
||||
Decode { source: serde_json::Error },
|
||||
|
||||
#[snafu(display("type {name} has invalid kind {given}"))]
|
||||
InvalidKind { name: String, given: String },
|
||||
|
||||
#[snafu(display("{kind} type {name} is missing its {field}"))]
|
||||
IncompleteType {
|
||||
name: String,
|
||||
kind: String,
|
||||
field: &'static str,
|
||||
},
|
||||
|
||||
#[snafu(display("no type {name} of {position} parameter for {directive}"))]
|
||||
ParamType {
|
||||
directive: String,
|
||||
@ -168,13 +164,37 @@ impl TypeSystem {
|
||||
pub fn parse(&mut self, src: &str) -> Result<&mut Self, Error> {
|
||||
let s: serial::Document = serde_json::from_str(src).context(DecodeSnafu)?;
|
||||
for t in s.types {
|
||||
let p = match t.kind {
|
||||
serial::TypeKind::Text => Type::Text,
|
||||
serial::TypeKind::Speech => Type::Speech,
|
||||
serial::TypeKind::Character => Type::Character,
|
||||
serial::TypeKind::Duration => Type::Duration,
|
||||
serial::TypeKind::Asset { tags } => Type::Asset { tags },
|
||||
serial::TypeKind::Enum { enm } => Type::Enum(enm),
|
||||
let p = match t.kind.as_str() {
|
||||
"text" => Type::Text,
|
||||
"speech" => Type::Speech,
|
||||
"character" => Type::Character,
|
||||
"duration" => Type::Duration,
|
||||
"asset" => match t.tags {
|
||||
None => {
|
||||
return Err(Error::IncompleteType {
|
||||
name: t.name,
|
||||
kind: t.kind,
|
||||
field: "tags",
|
||||
});
|
||||
}
|
||||
Some(v) => Type::Asset { tags: v },
|
||||
},
|
||||
"enum" => match t.enm {
|
||||
None => {
|
||||
return Err(Error::IncompleteType {
|
||||
name: t.name,
|
||||
kind: t.kind,
|
||||
field: "enumerants",
|
||||
});
|
||||
}
|
||||
Some(v) => Type::Enum(v),
|
||||
},
|
||||
_ => {
|
||||
return Err(Error::InvalidKind {
|
||||
name: t.name,
|
||||
given: t.kind,
|
||||
});
|
||||
}
|
||||
};
|
||||
self.types.insert(t.name, p);
|
||||
}
|
||||
@ -268,6 +288,13 @@ mod tests {
|
||||
use super::*;
|
||||
use rstest::rstest;
|
||||
|
||||
macro_rules! vec_into {
|
||||
[] => { Vec::new() };
|
||||
[$($val:expr),+ $(,)?] =>{
|
||||
Vec::from([$($val,)+].map(|x| x.into()))
|
||||
};
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn builtin() {
|
||||
let mut s = TypeSystem {
|
||||
@ -281,21 +308,124 @@ mod tests {
|
||||
("speech", Type::Speech),
|
||||
("character", Type::Character),
|
||||
("duration", Type::Duration),
|
||||
("scene", Type::Asset { tags: Vec::from(["scene"].map(String::from)) }),
|
||||
("background", Type::Asset { tags: Vec::from(["background"].map(String::from)) }),
|
||||
("stage position", Type::Enum(Vec::from(["close", "left third", "right third", "left", "center", "right", "far left", "mid left", "mid right", "far right", "far"].map(String::from)))),
|
||||
("scene", Type::Asset { tags: vec_into!["scene"] }),
|
||||
("background", Type::Asset { tags: vec_into!["background"] }),
|
||||
("stage position", Type::Enum(vec_into!["close", "left third", "right third", "left", "center", "right", "far left", "mid left", "mid right", "far right", "far"])),
|
||||
].map(|(k, v)| (k.into(), v))),
|
||||
directives: BTreeMap::from([
|
||||
("page", Directive {
|
||||
name: "page".into(),
|
||||
doc: "Start showing the current static speaker, text, &c.\nNormally not used directly; script speech lines call this directive implicitly.".into(),
|
||||
prefix: "page".into(),
|
||||
prefix_param: None,
|
||||
infix: None,
|
||||
infix_param: None,
|
||||
suffix: None,
|
||||
suffix_param: None,
|
||||
list_param: None,
|
||||
..Default::default()
|
||||
}),
|
||||
("set static text _", Directive {
|
||||
name: "set static text _".into(),
|
||||
doc: "Set the current page's text.\nNormally not used directly; script speech lines call this directive implicitly.".into(),
|
||||
prefix: "set static text".into(),
|
||||
prefix_param: Some(Param {
|
||||
name: "text".into(),
|
||||
doc: Some("Line content.".into()),
|
||||
typ: Type::Speech,
|
||||
nuance: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("set static speaker _", Directive {
|
||||
name: "set static speaker _".into(),
|
||||
doc: "Set the current page's speaker.\nNormally not used directly; script speech lines call this directive implicitly.".into(),
|
||||
prefix: "set static speaker".into(),
|
||||
prefix_param: Some(Param {
|
||||
name: "name".into(),
|
||||
doc: Some("Speaker name.".into()),
|
||||
typ: Type::Text,
|
||||
nuance: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("characters", Directive {
|
||||
name: "characters".into(),
|
||||
doc: "Set the names of characters in the current scene.".into(),
|
||||
prefix: "characters".into(),
|
||||
list_param: Some(Param {
|
||||
name: "characters".into(),
|
||||
doc: Some("Name of the character to include.".into()),
|
||||
typ: Type::Text,
|
||||
nuance: Some(Box::new(Param {
|
||||
name: "variant".into(),
|
||||
doc: Some("Variant of this character.".into()),
|
||||
typ: Type::Text,
|
||||
nuance: None,
|
||||
})),
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("scene", Directive {
|
||||
name: "scene".into(),
|
||||
doc: "Mark the beginning of a scene.".into(),
|
||||
prefix: "scene".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
("background", Directive {
|
||||
name: "background".into(),
|
||||
doc: "Set the background image.".into(),
|
||||
prefix: "background".into(),
|
||||
prefix_param: Some(Param {
|
||||
name: "asset".into(),
|
||||
doc: Some("Background image asset.".into()),
|
||||
typ: Type::Asset { tags: vec_into!["background"] },
|
||||
nuance: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("enter _ stage _", Directive {
|
||||
name: "enter _ stage _".into(),
|
||||
doc: "Bring a character onto the stage at a given position.".into(),
|
||||
prefix: "enter".into(),
|
||||
prefix_param: Some(Param {
|
||||
name: "character".into(),
|
||||
doc: Some("Character to enter.".into()),
|
||||
typ: Type::Character,
|
||||
nuance: None,
|
||||
}),
|
||||
suffix: Some("stage".into()),
|
||||
suffix_param: Some(Param {
|
||||
name: "position".into(),
|
||||
doc: Some("Position on the stage where the character enters.".into()),
|
||||
typ: Type::Enum(vec_into!["close", "left third", "right third", "left", "center", "right", "far left", "mid left", "mid right", "far right", "far"]),
|
||||
nuance: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("enter _", Directive {
|
||||
name: "enter _".into(),
|
||||
doc: "Bring a character onto the stage at a default position.".into(),
|
||||
prefix: "enter".into(),
|
||||
prefix_param: Some(Param {
|
||||
name: "character".into(),
|
||||
doc: Some("Character to enter.".into()),
|
||||
typ: Type::Character,
|
||||
nuance: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("exit _", Directive {
|
||||
name: "exit _".into(),
|
||||
doc: "Remove a character from the stage.".into(),
|
||||
prefix: "exit".into(),
|
||||
prefix_param: Some(Param {
|
||||
name: "character".into(),
|
||||
doc: Some("Character to exit.".into()),
|
||||
typ: Type::Character,
|
||||
nuance: None,
|
||||
}),
|
||||
..Default::default()
|
||||
}),
|
||||
("exeunt", Directive {
|
||||
name: "exeunt".into(),
|
||||
doc: "Remove all characters from the stage.".into(),
|
||||
prefix: "exeunt".into(),
|
||||
..Default::default()
|
||||
}),
|
||||
].map(|(k, v)| (k.into(), v))),
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user