ayaka_bindings_types/
config.rs

1use ayaka_primitive::RawValue;
2use fallback::FallbackSpec;
3use serde::{Deserialize, Serialize};
4use std::{
5    borrow::Cow,
6    collections::{HashMap, VecDeque},
7};
8
9/// The unit of one line in an action.
10///
11/// If a frontend supports animation,
12/// the characters in [`ActionSubText::Chars`] should be printed one by one,
13/// while the characters in [`ActionSubText::Block`] should be printed together.
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15#[serde(tag = "type", content = "data")]
16pub enum ActionSubText {
17    /// Characters printed one by one.
18    /// Usually they are meaningful texts.
19    Chars(String),
20    /// Characters printed together.
21    /// Usually they are HTML tags or other control characters.
22    Block(String),
23}
24
25impl ActionSubText {
26    /// Creates [`ActionSubText::Chars`].
27    pub fn chars(s: impl Into<String>) -> Self {
28        Self::Chars(s.into())
29    }
30
31    /// Creates [`ActionSubText::Block`].
32    pub fn block(s: impl Into<String>) -> Self {
33        Self::Block(s.into())
34    }
35
36    /// Gets a reference of [`str`].
37    pub fn as_str(&self) -> &str {
38        match self {
39            Self::Chars(s) | Self::Block(s) => s,
40        }
41    }
42
43    /// Gets the inner [`String`].
44    pub fn into_string(self) -> String {
45        match self {
46            Self::Chars(s) | Self::Block(s) => s,
47        }
48    }
49}
50
51/// A map from variable name to [`RawValue`].
52pub type VarMap = HashMap<String, RawValue>;
53
54/// The serializable context.
55#[derive(Debug, Default, Clone, Deserialize, Serialize)]
56pub struct RawContext {
57    /// Current base paragraph tag.
58    pub cur_base_para: String,
59    /// Current paragraph tag.
60    pub cur_para: String,
61    /// Current text index.
62    pub cur_act: usize,
63    /// Current local variables.
64    pub locals: VarMap,
65}
66
67/// The `text` is a [`VecDeque<ActionSubText>`].
68/// The [`ActionSubText`] could be pushed and poped at front or back.
69///
70/// Generally, you should avoid using `push_back` directly.
71/// To reduce allocations in serialization, you should use
72/// `push_back_chars` and `push_back_block`.
73///
74/// ```
75/// # use ayaka_bindings_types::*;
76/// let mut text = ActionText::default();
77/// text.push_back_chars("Hello ");
78/// assert_eq!(text.text[0], ActionSubText::chars("Hello "));
79/// text.push_back_chars("world!");
80/// assert_eq!(text.text[0], ActionSubText::chars("Hello world!"));
81/// ```
82#[derive(Debug, Default, Clone, Serialize, Deserialize, FallbackSpec)]
83pub struct ActionText {
84    /// The full texts.
85    pub text: VecDeque<ActionSubText>,
86    /// The key of current character.
87    pub ch_key: Option<String>,
88    /// The current character.
89    pub character: Option<String>,
90    /// The temp variables.
91    pub vars: VarMap,
92}
93
94impl ActionText {
95    /// Push the string as [`ActionSubText::Chars`] to the back.
96    /// If the back element is also [`ActionSubText::Chars`], the string is appended.
97    pub fn push_back_chars<'a>(&mut self, s: impl Into<Cow<'a, str>>) {
98        let s = s.into();
99        if let Some(ActionSubText::Chars(text)) = self.text.back_mut() {
100            text.push_str(&s);
101        } else {
102            self.text.push_back(ActionSubText::chars(s));
103        }
104    }
105
106    /// Push the string as [`ActionSubText::Block`] to the back.
107    /// If the back element is also [`ActionSubText::Block`], the string is appended.
108    pub fn push_back_block<'a>(&mut self, s: impl Into<Cow<'a, str>>) {
109        let s = s.into();
110        if let Some(ActionSubText::Block(text)) = self.text.back_mut() {
111            text.push_str(&s);
112        } else {
113            self.text.push_back(ActionSubText::block(s));
114        }
115    }
116}
117
118impl std::fmt::Display for ActionText {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        for text in &self.text {
121            write!(f, "{}", text.as_str())?;
122        }
123        Ok(())
124    }
125}
126
127impl PartialEq for ActionText {
128    fn eq(&self, other: &Self) -> bool {
129        self.to_string() == other.to_string()
130            && self.ch_key == other.ch_key
131            && self.character == other.character
132            && self.vars == other.vars
133    }
134}
135
136/// The full action information in one line of config.
137/// It provides the full texts and other properties exacted from [`ayaka_primitive::Text`].
138#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
139#[serde(tag = "type", content = "data")]
140pub enum Action {
141    /// An empty action usually means an `exec` or custom action.
142    #[default]
143    Empty,
144    /// A text action, display some texts.
145    Text(ActionText),
146    /// A switch action, display switches and let player to choose.
147    Switches(Vec<Switch>),
148    /// A custom action.
149    Custom(VarMap),
150}
151
152/// One switch in the switches of an [`Action`].
153#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, FallbackSpec)]
154pub struct Switch {
155    /// The switch text.
156    pub text: String,
157    /// Whether the switch is enabled.
158    pub enabled: bool,
159}