use serde::{Deserialize, Serialize};
use std::fmt;
use crate::consts::*;
use crate::pages::{ParseError, ParseState};
use crate::{DgParser, ParseResult};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct DialogueChoice {
pub text: String,
pub label: Option<Label>,
}
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub enum Label {
Goto(String),
}
impl Label {
pub fn new_goto(id: &str) -> Self {
Self::Goto(id.to_owned())
}
}
impl fmt::Display for Label {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Goto(id) => write!(f, "{}", id),
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Deserialize, Serialize)]
pub enum DialogueEnding {
Choices(Vec<DialogueChoice>),
Label(Label),
#[default]
End,
}
impl DialogueEnding {
pub fn append_choice(&mut self, choice: DialogueChoice) -> ParseResult<()> {
match self {
DialogueEnding::Choices(ref mut choices) => {
choices.push(choice);
}
DialogueEnding::End => {
*self = DialogueEnding::Choices(vec![choice]);
}
_ => return Err(ParseError::MixedEndings(choice.text.clone())),
}
Ok(())
}
}
pub fn parse_choice(parser: &mut DgParser, line: &str) -> ParseResult<()> {
if line.is_empty() {
return Ok(());
}
if line == SEPARATOR {
parser.push_page()?;
parser.state = ParseState::Metadata;
return Ok(());
}
let (first_ch, rest) = {
let mut it = line.chars();
let first_ch = it
.next()
.ok_or(ParseError::MalformedEnding(line.to_owned()))?;
it.next(); (first_ch, it.as_str())
};
let ix = parser
.interaction
.as_mut()
.ok_or(ParseError::PushPageNoIX)?;
match first_ch {
PREFIX_CHOICE => {
let choice = DialogueChoice {
text: rest.to_owned(),
label: None,
};
ix.ending.append_choice(choice)?;
}
_ => {
let label = match first_ch {
PREFIX_GOTO_LABEL => Label::new_goto(rest),
_ => return Err(ParseError::MalformedEnding(line.to_owned())),
};
match ix.ending {
DialogueEnding::Choices(ref mut choices) => {
let choice = choices
.last_mut()
.ok_or_else(|| ParseError::MalformedEnding(line.to_owned()))?;
if choice.label.is_some() {
return Err(ParseError::MixedEndings(line.to_owned()));
}
choice.label = Some(label);
}
DialogueEnding::Label(_) => {
return Err(ParseError::MixedEndings(line.to_owned()));
}
DialogueEnding::End => {
ix.ending = DialogueEnding::Label(label);
}
}
}
}
Ok(())
}