|
|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
/// A monologue containing a list of optional lines
|
|
|
|
|
#[derive(Asset, TypePath, Debug, Deserialize, Default, Clone)]
|
|
|
|
|
#[derive(Asset, TypePath, Debug, Deserialize, Default, Clone, PartialEq)]
|
|
|
|
|
pub(crate) struct Monologue {
|
|
|
|
|
pub batches: Vec<MonologueLineBatch>,
|
|
|
|
|
}
|
|
|
|
|
@ -16,8 +16,49 @@ impl Monologue {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Error)]
|
|
|
|
|
pub struct MonologueParseError;
|
|
|
|
|
|
|
|
|
|
impl std::fmt::Display for MonologueParseError {
|
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
|
write!(f, "Could not parse monologue file")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl TryFrom<&str> for Monologue {
|
|
|
|
|
type Error = MonologueParseError;
|
|
|
|
|
|
|
|
|
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
|
|
|
|
// Create a blank monologue to populate
|
|
|
|
|
let mut monologue = Monologue::default();
|
|
|
|
|
|
|
|
|
|
// Add an initial batch that may end up being empty
|
|
|
|
|
monologue.add_batch(MonologueLineBatch::default());
|
|
|
|
|
|
|
|
|
|
// Iterate over raw string lines in the .mono file
|
|
|
|
|
for line in value.lines() {
|
|
|
|
|
// Break up into batches by --- separator
|
|
|
|
|
if line.starts_with("---") {
|
|
|
|
|
monologue.add_batch(MonologueLineBatch::default());
|
|
|
|
|
// Skip any empty lines or comments
|
|
|
|
|
} else if line.starts_with("#") || line.is_empty() {
|
|
|
|
|
// Skip comments and blank lines
|
|
|
|
|
// everything else we read as a monologue line
|
|
|
|
|
} else {
|
|
|
|
|
monologue.batches.last_mut().unwrap().add_line(line.into());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear empty batches
|
|
|
|
|
monologue.batches.retain(|batch| !batch.lines.is_empty());
|
|
|
|
|
|
|
|
|
|
Ok(monologue)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A set of possible lines in a monologue
|
|
|
|
|
#[derive(Debug, Deserialize, Default, Clone)]
|
|
|
|
|
#[derive(Debug, Deserialize, Default, Clone, PartialEq)]
|
|
|
|
|
pub(crate) struct MonologueLineBatch {
|
|
|
|
|
pub lines: Vec<MonologueLine>,
|
|
|
|
|
}
|
|
|
|
|
@ -29,7 +70,7 @@ impl MonologueLineBatch {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A single monologue line
|
|
|
|
|
#[derive(Debug, Deserialize, Default, Clone)]
|
|
|
|
|
#[derive(Debug, Deserialize, Default, Clone, PartialEq)]
|
|
|
|
|
pub(crate) struct MonologueLine {
|
|
|
|
|
pub value: String,
|
|
|
|
|
}
|
|
|
|
|
@ -60,6 +101,156 @@ impl From<&str> for MonologueLine {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
use indoc::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn empty_monologue() {
|
|
|
|
|
const MONO: &str = "";
|
|
|
|
|
|
|
|
|
|
let parsed: Monologue = MONO.try_into().unwrap();
|
|
|
|
|
|
|
|
|
|
let expected = Monologue::default();
|
|
|
|
|
|
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn complicated_empty_monologue() {
|
|
|
|
|
const MONO: &str = indoc! {"
|
|
|
|
|
---
|
|
|
|
|
---
|
|
|
|
|
# a comment
|
|
|
|
|
---
|
|
|
|
|
--- # more stuff
|
|
|
|
|
# another comment
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
"};
|
|
|
|
|
|
|
|
|
|
let parsed: Monologue = MONO.try_into().unwrap();
|
|
|
|
|
|
|
|
|
|
let expected = Monologue::default();
|
|
|
|
|
|
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn basic_monologue() {
|
|
|
|
|
const MONO: &str = indoc! {"
|
|
|
|
|
---
|
|
|
|
|
hello
|
|
|
|
|
---
|
|
|
|
|
world
|
|
|
|
|
---
|
|
|
|
|
"};
|
|
|
|
|
|
|
|
|
|
let parsed: Monologue = MONO.try_into().unwrap();
|
|
|
|
|
|
|
|
|
|
let expected = Monologue {
|
|
|
|
|
batches: vec![
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"hello".into()
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"world".into()
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn basic_with_comments_monologue() {
|
|
|
|
|
const MONO: &str = indoc! {"
|
|
|
|
|
# Some stuff before we get started
|
|
|
|
|
---
|
|
|
|
|
# I really like this line
|
|
|
|
|
hello
|
|
|
|
|
---
|
|
|
|
|
# I'm skeptical about this one...
|
|
|
|
|
world
|
|
|
|
|
# This line needs work
|
|
|
|
|
---
|
|
|
|
|
# More notes after the lines
|
|
|
|
|
"};
|
|
|
|
|
|
|
|
|
|
let parsed: Monologue = MONO.try_into().unwrap();
|
|
|
|
|
|
|
|
|
|
let expected = Monologue {
|
|
|
|
|
batches: vec![
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"hello".into()
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"world".into()
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn batches_monologue() {
|
|
|
|
|
const MONO: &str = indoc! {"
|
|
|
|
|
a
|
|
|
|
|
|
|
|
|
|
b
|
|
|
|
|
|
|
|
|
|
c
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
d
|
|
|
|
|
|
|
|
|
|
e
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
# with comment before...
|
|
|
|
|
f
|
|
|
|
|
# ...and after
|
|
|
|
|
"};
|
|
|
|
|
|
|
|
|
|
let parsed: Monologue = MONO.try_into().unwrap();
|
|
|
|
|
|
|
|
|
|
let expected = Monologue {
|
|
|
|
|
batches: vec![
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"a".into(), "b".into(), "c".into()
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"d".into(), "e".into()
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
MonologueLineBatch {
|
|
|
|
|
lines: vec![
|
|
|
|
|
"f".into()
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
assert_eq!(parsed, expected);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
struct MonologueLoader;
|
|
|
|
|
|
|
|
|
|
@ -69,6 +260,9 @@ enum MonologueLoaderError {
|
|
|
|
|
Io(#[from] std::io::Error),
|
|
|
|
|
#[error("Could not parse utf8")]
|
|
|
|
|
Utf8(#[from] std::string::FromUtf8Error),
|
|
|
|
|
// TODO: Real errors
|
|
|
|
|
#[error("Could not as monologue")]
|
|
|
|
|
Parse(#[from] MonologueParseError),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl AssetLoader for MonologueLoader {
|
|
|
|
|
@ -86,30 +280,7 @@ impl AssetLoader for MonologueLoader {
|
|
|
|
|
|
|
|
|
|
let raw_string = String::from_utf8(bytes)?;
|
|
|
|
|
|
|
|
|
|
// Create a blank monologue to populate
|
|
|
|
|
let mut monologue = Monologue::default();
|
|
|
|
|
|
|
|
|
|
// Add an initial batch that may end up being empty
|
|
|
|
|
monologue.add_batch(MonologueLineBatch::default());
|
|
|
|
|
|
|
|
|
|
// Iterate over raw string lines in the .mono file
|
|
|
|
|
for line in raw_string.lines() {
|
|
|
|
|
// Break up into batches by --- separator
|
|
|
|
|
if line.starts_with("---") {
|
|
|
|
|
monologue.add_batch(MonologueLineBatch::default());
|
|
|
|
|
// Skip any empty lines or comments
|
|
|
|
|
} else if line.starts_with("#") || line.is_empty() {
|
|
|
|
|
// Skip comments and blank lines
|
|
|
|
|
// everything else we read as a monologue line
|
|
|
|
|
} else {
|
|
|
|
|
monologue.batches.last_mut().unwrap().add_line(line.into());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear empty batches
|
|
|
|
|
monologue.batches.retain(|batch| !batch.lines.is_empty());
|
|
|
|
|
|
|
|
|
|
Ok(monologue)
|
|
|
|
|
Ok(Monologue::try_from(raw_string.as_str())?)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn extensions(&self) -> &[&str] {
|
|
|
|
|
|