use pasta_core::registry::{SceneRegistry, WordDefRegistry};
use pasta_dsl::parser::{Attr, AttrValue, GlobalSceneScope, LocalSceneScope};
use std::collections::HashMap;
#[derive(Default)]
pub struct TranspileContext {
pub scene_registry: SceneRegistry,
pub word_registry: WordDefRegistry,
pub current_module: Option<String>,
file_attrs: HashMap<String, AttrValue>,
}
impl TranspileContext {
pub fn new() -> Self {
Self::default()
}
pub fn set_current_module(&mut self, module: String) {
self.current_module = Some(module);
}
pub fn get_current_module(&self) -> Option<&str> {
self.current_module.as_deref()
}
pub fn register_global_scene(&mut self, scene: &GlobalSceneScope) -> (i64, usize) {
let attrs: HashMap<String, String> = scene
.attrs
.iter()
.map(|a| (a.key.clone(), a.value.to_string()))
.collect();
self.scene_registry.register_global(&scene.name, attrs)
}
pub fn register_local_scene(
&mut self,
local_scene: &LocalSceneScope,
parent_name: &str,
parent_counter: usize,
local_index: usize,
) -> i64 {
let attrs: HashMap<String, String> = local_scene
.attrs
.iter()
.map(|a| (a.key.clone(), a.value.to_string()))
.collect();
let name = local_scene.name.as_deref().unwrap_or("__start__");
self.scene_registry
.register_local(name, parent_name, parent_counter, local_index, attrs)
}
pub fn accumulate_file_attr(&mut self, attr: &Attr) {
self.file_attrs.insert(attr.key.clone(), attr.value.clone());
}
pub fn file_attrs(&self) -> &HashMap<String, AttrValue> {
&self.file_attrs
}
pub fn merge_attrs(&self, scene_attrs: &[Attr]) -> HashMap<String, AttrValue> {
let mut result = self.file_attrs.clone();
for attr in scene_attrs {
result.insert(attr.key.clone(), attr.value.clone());
}
result
}
pub fn merge_from(&mut self, other: TranspileContext) {
self.scene_registry.merge_from(other.scene_registry);
self.word_registry.merge_from(other.word_registry);
}
}
#[cfg(test)]
mod tests {
use super::*;
use pasta_dsl::parser::{KeyWords, Span};
fn create_test_scene(name: &str) -> GlobalSceneScope {
GlobalSceneScope {
name: name.to_string(),
is_continuation: false,
attrs: vec![],
words: vec![],
actors: vec![],
code_blocks: vec![],
local_scenes: vec![],
span: Span::default(),
}
}
fn create_test_local_scene(name: &str) -> LocalSceneScope {
LocalSceneScope::named(name.to_string())
}
#[test]
fn test_context_new() {
let ctx = TranspileContext::new();
assert!(ctx.current_module.is_none());
}
#[test]
fn test_context_set_module() {
let mut ctx = TranspileContext::new();
ctx.set_current_module("メイン1".to_string());
assert_eq!(ctx.get_current_module(), Some("メイン1"));
}
#[test]
fn test_register_global_scene() {
let mut ctx = TranspileContext::new();
let scene = create_test_scene("メイン");
let (id, counter) = ctx.register_global_scene(&scene);
assert_eq!(id, 1);
assert_eq!(counter, 1);
let scenes = ctx.scene_registry.all_scenes();
assert_eq!(scenes.len(), 1);
assert_eq!(scenes[0].name, "メイン");
}
#[test]
fn test_register_local_scene() {
let mut ctx = TranspileContext::new();
let parent = create_test_scene("メイン");
let (_, parent_counter) = ctx.register_global_scene(&parent);
let local = create_test_local_scene("自己紹介");
let id = ctx.register_local_scene(&local, "メイン", parent_counter, 1);
assert_eq!(id, 2);
let scenes = ctx.scene_registry.all_scenes();
assert_eq!(scenes.len(), 2);
}
#[test]
fn test_register_global_words() {
let mut ctx = TranspileContext::new();
ctx.word_registry
.register_global("挨拶", vec!["こんにちは".to_string(), "やあ".to_string()]);
let entries = ctx.word_registry.all_entries();
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].key, "挨拶");
}
#[test]
fn test_register_global_words_multi_key() {
let mut ctx = TranspileContext::new();
let kw = KeyWords {
names: vec!["女性".to_string(), "水の妖精".to_string()],
words: vec!["水無灯里".to_string(), "アリス・キャロル".to_string()],
span: Span::default(),
};
for name in &kw.names {
ctx.word_registry.register_global(name, kw.words.clone());
}
let entries = ctx.word_registry.all_entries();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].key, "女性");
assert_eq!(entries[1].key, "水の妖精");
}
#[test]
fn test_register_local_words() {
let mut ctx = TranspileContext::new();
ctx.word_registry.register_local(
"メイン_1",
"場所",
vec!["東京".to_string(), "大阪".to_string()],
);
let entries = ctx.word_registry.all_entries();
assert_eq!(entries.len(), 1);
assert!(entries[0].key.contains(":メイン_1:場所"));
}
#[test]
fn test_register_local_words_multi_key() {
let mut ctx = TranspileContext::new();
let kw = KeyWords {
names: vec!["場所".to_string(), "地名".to_string()],
words: vec!["東京".to_string(), "大阪".to_string()],
span: Span::default(),
};
for name in &kw.names {
ctx.word_registry
.register_local("メイン_1", name, kw.words.clone());
}
let entries = ctx.word_registry.all_entries();
assert_eq!(entries.len(), 2);
assert!(entries[0].key.contains(":メイン_1:場所"));
assert!(entries[1].key.contains(":メイン_1:地名"));
}
use pasta_dsl::parser::{Attr, AttrValue};
fn create_attr(key: &str, value: &str) -> Attr {
Attr {
key: key.to_string(),
value: AttrValue::AttrString(value.to_string()),
span: Span::default(),
}
}
#[test]
fn test_accumulate_file_attr_basic() {
let mut ctx = TranspileContext::new();
let attr1 = create_attr("author", "Alice");
let attr2 = create_attr("version", "1.0");
ctx.accumulate_file_attr(&attr1);
ctx.accumulate_file_attr(&attr2);
let attrs = ctx.file_attrs();
assert_eq!(attrs.len(), 2);
assert_eq!(
attrs.get("author"),
Some(&AttrValue::AttrString("Alice".to_string()))
);
assert_eq!(
attrs.get("version"),
Some(&AttrValue::AttrString("1.0".to_string()))
);
}
#[test]
fn test_accumulate_file_attr_shadowing() {
let mut ctx = TranspileContext::new();
let attr1 = create_attr("author", "Alice");
let attr2 = create_attr("author", "Bob");
ctx.accumulate_file_attr(&attr1);
ctx.accumulate_file_attr(&attr2);
let attrs = ctx.file_attrs();
assert_eq!(attrs.len(), 1);
assert_eq!(
attrs.get("author"),
Some(&AttrValue::AttrString("Bob".to_string()))
);
}
#[test]
fn test_merge_attrs_file_only() {
let mut ctx = TranspileContext::new();
ctx.accumulate_file_attr(&create_attr("author", "Alice"));
let merged = ctx.merge_attrs(&[]);
assert_eq!(merged.len(), 1);
assert_eq!(
merged.get("author"),
Some(&AttrValue::AttrString("Alice".to_string()))
);
}
#[test]
fn test_merge_attrs_scene_overrides_file() {
let mut ctx = TranspileContext::new();
ctx.accumulate_file_attr(&create_attr("author", "Alice"));
ctx.accumulate_file_attr(&create_attr("version", "1.0"));
let scene_attrs = vec![create_attr("author", "Bob")];
let merged = ctx.merge_attrs(&scene_attrs);
assert_eq!(merged.len(), 2);
assert_eq!(
merged.get("author"),
Some(&AttrValue::AttrString("Bob".to_string()))
);
assert_eq!(
merged.get("version"),
Some(&AttrValue::AttrString("1.0".to_string()))
);
}
#[test]
fn test_get_current_module_none_by_default() {
let ctx = TranspileContext::new();
assert_eq!(ctx.get_current_module(), None);
}
#[test]
fn test_merge_from_combines_scene_and_word_registries() {
let mut ctx1 = TranspileContext::new();
ctx1.register_global_scene(&create_test_scene("メイン"));
ctx1.word_registry
.register_global("挨拶", vec!["こんにちは".to_string()]);
let mut ctx2 = TranspileContext::new();
ctx2.register_global_scene(&create_test_scene("サブ"));
ctx2.word_registry
.register_global("別れ", vec!["さようなら".to_string()]);
ctx1.merge_from(ctx2);
let scenes = ctx1.scene_registry.all_scenes();
assert_eq!(scenes.len(), 2, "scenes from both contexts must be merged");
let names: Vec<&str> = scenes.iter().map(|s| s.name.as_str()).collect();
assert!(names.contains(&"メイン"));
assert!(names.contains(&"サブ"));
let entries = ctx1.word_registry.all_entries();
assert_eq!(entries.len(), 2, "words from both contexts must be merged");
let keys: Vec<&str> = entries.iter().map(|e| e.key.as_str()).collect();
assert!(keys.contains(&"挨拶"));
assert!(keys.contains(&"別れ"));
}
#[test]
fn test_merge_from_does_not_merge_file_attrs() {
let mut ctx1 = TranspileContext::new();
ctx1.accumulate_file_attr(&create_attr("author", "Alice"));
let mut ctx2 = TranspileContext::new();
ctx2.accumulate_file_attr(&create_attr("version", "2.0"));
ctx1.merge_from(ctx2);
let attrs = ctx1.file_attrs();
assert_eq!(attrs.len(), 1, "other context's file attrs must be dropped");
assert!(attrs.contains_key("author"));
assert!(!attrs.contains_key("version"));
}
#[test]
fn test_merge_attrs_scene_adds_new_key() {
let mut ctx = TranspileContext::new();
ctx.accumulate_file_attr(&create_attr("author", "Alice"));
let scene_attrs = vec![create_attr("title", "MyScene")];
let merged = ctx.merge_attrs(&scene_attrs);
assert_eq!(merged.len(), 2);
assert_eq!(
merged.get("author"),
Some(&AttrValue::AttrString("Alice".to_string()))
);
assert_eq!(
merged.get("title"),
Some(&AttrValue::AttrString("MyScene".to_string()))
);
}
}