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.
|
/// The definition of a standard directive.
|
||||||
///
|
///
|
||||||
/// Note that this type is an input to the parser, not an output.
|
/// 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 {
|
pub struct Directive {
|
||||||
/// The provider's name for the directive.
|
/// The provider's name for the directive.
|
||||||
name: String,
|
name: String,
|
||||||
@ -79,28 +79,14 @@ mod serial {
|
|||||||
pub directives: Vec<Directive>,
|
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)]
|
#[derive(Deserialize)]
|
||||||
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
#[serde(rename_all = "snake_case", deny_unknown_fields)]
|
||||||
pub struct Type {
|
pub struct Type {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub doc: String,
|
pub kind: String,
|
||||||
pub kind: TypeKind,
|
pub tags: Option<Vec<String>>,
|
||||||
|
#[serde(rename = "enum")]
|
||||||
|
pub enm: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -133,6 +119,16 @@ pub enum Error {
|
|||||||
#[snafu(display("invalid directive definition file: {source}"))]
|
#[snafu(display("invalid directive definition file: {source}"))]
|
||||||
Decode { source: serde_json::Error },
|
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}"))]
|
#[snafu(display("no type {name} of {position} parameter for {directive}"))]
|
||||||
ParamType {
|
ParamType {
|
||||||
directive: String,
|
directive: String,
|
||||||
@ -168,13 +164,37 @@ impl TypeSystem {
|
|||||||
pub fn parse(&mut self, src: &str) -> Result<&mut Self, Error> {
|
pub fn parse(&mut self, src: &str) -> Result<&mut Self, Error> {
|
||||||
let s: serial::Document = serde_json::from_str(src).context(DecodeSnafu)?;
|
let s: serial::Document = serde_json::from_str(src).context(DecodeSnafu)?;
|
||||||
for t in s.types {
|
for t in s.types {
|
||||||
let p = match t.kind {
|
let p = match t.kind.as_str() {
|
||||||
serial::TypeKind::Text => Type::Text,
|
"text" => Type::Text,
|
||||||
serial::TypeKind::Speech => Type::Speech,
|
"speech" => Type::Speech,
|
||||||
serial::TypeKind::Character => Type::Character,
|
"character" => Type::Character,
|
||||||
serial::TypeKind::Duration => Type::Duration,
|
"duration" => Type::Duration,
|
||||||
serial::TypeKind::Asset { tags } => Type::Asset { tags },
|
"asset" => match t.tags {
|
||||||
serial::TypeKind::Enum { enm } => Type::Enum(enm),
|
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);
|
self.types.insert(t.name, p);
|
||||||
}
|
}
|
||||||
@ -268,6 +288,13 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use rstest::rstest;
|
use rstest::rstest;
|
||||||
|
|
||||||
|
macro_rules! vec_into {
|
||||||
|
[] => { Vec::new() };
|
||||||
|
[$($val:expr),+ $(,)?] =>{
|
||||||
|
Vec::from([$($val,)+].map(|x| x.into()))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn builtin() {
|
fn builtin() {
|
||||||
let mut s = TypeSystem {
|
let mut s = TypeSystem {
|
||||||
@ -281,21 +308,124 @@ mod tests {
|
|||||||
("speech", Type::Speech),
|
("speech", Type::Speech),
|
||||||
("character", Type::Character),
|
("character", Type::Character),
|
||||||
("duration", Type::Duration),
|
("duration", Type::Duration),
|
||||||
("scene", Type::Asset { tags: Vec::from(["scene"].map(String::from)) }),
|
("scene", Type::Asset { tags: vec_into!["scene"] }),
|
||||||
("background", Type::Asset { tags: Vec::from(["background"].map(String::from)) }),
|
("background", Type::Asset { tags: vec_into!["background"] }),
|
||||||
("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)))),
|
("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))),
|
].map(|(k, v)| (k.into(), v))),
|
||||||
directives: BTreeMap::from([
|
directives: BTreeMap::from([
|
||||||
("page", Directive {
|
("page", Directive {
|
||||||
name: "page".into(),
|
name: "page".into(),
|
||||||
doc: "Start showing the current static speaker, text, &c.\nNormally not used directly; script speech lines call this directive implicitly.".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: "page".into(),
|
||||||
prefix_param: None,
|
..Default::default()
|
||||||
infix: None,
|
}),
|
||||||
infix_param: None,
|
("set static text _", Directive {
|
||||||
suffix: None,
|
name: "set static text _".into(),
|
||||||
suffix_param: None,
|
doc: "Set the current page's text.\nNormally not used directly; script speech lines call this directive implicitly.".into(),
|
||||||
list_param: None,
|
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))),
|
].map(|(k, v)| (k.into(), v))),
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user