workshop_rs/actions/
layout.rs1use crate::catalog::{Catalog, Locale};
4use crate::output::emitter::EmitContext;
5use crate::wir;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct ActionLayout {
11 pub width: usize,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum ActionLayoutError {
18 InvalidProgram { message: String },
20 Emission(crate::WorkshopError),
22}
23
24impl std::fmt::Display for ActionLayoutError {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 match self {
27 Self::InvalidProgram { message } => write!(f, "invalid program: {message}"),
28 Self::Emission(error) => write!(f, "action layout emission failed: {error}"),
29 }
30 }
31}
32
33impl std::error::Error for ActionLayoutError {}
34
35#[doc(hidden)]
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub enum WIRActionLayoutError {
38 InvalidWIR(crate::wir::error::IrError),
39 Emission(crate::WorkshopError),
40}
41
42impl std::fmt::Display for WIRActionLayoutError {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 match self {
45 Self::InvalidWIR(error) => write!(f, "invalid WIR: {error}"),
46 Self::Emission(error) => write!(f, "action layout emission failed: {error}"),
47 }
48 }
49}
50
51impl std::error::Error for WIRActionLayoutError {}
52
53pub fn action_width(
61 program: &crate::Program,
62 catalog: &Catalog,
63 locale: &Locale,
64 actions: &[crate::Action],
65) -> std::result::Result<ActionLayout, ActionLayoutError> {
66 let mut input = crate::Program::new();
67 input.settings = program.settings.clone();
68 input.global_variables = program.global_variables.clone();
69 input.player_variables = program.player_variables.clone();
70 input.subroutines = program.subroutines.clone();
71 input
72 .rules
73 .push(crate::Rule::new("action layout", crate::Event::Global));
74 input.rules[0].actions = actions.to_vec();
75 let storage = input
76 .to_wir()
77 .map_err(|error| ActionLayoutError::InvalidProgram {
78 message: error.to_string(),
79 })?;
80 let rule = storage.rules.iter().next().ok_or_else(|| {
81 ActionLayoutError::Emission(crate::WorkshopError::Malformed {
82 message: "action layout program has no rule".to_string(),
83 span: None,
84 })
85 })?;
86 action_width_wir(&storage, catalog, locale, &rule.actions).map_err(|error| match error {
87 WIRActionLayoutError::InvalidWIR(error) => ActionLayoutError::InvalidProgram {
88 message: error.to_string(),
89 },
90 WIRActionLayoutError::Emission(error) => ActionLayoutError::Emission(error),
91 })
92}
93
94#[doc(hidden)]
95pub fn action_width_wir(
96 program: &wir::Program,
97 catalog: &Catalog,
98 locale: &Locale,
99 actions: &[wir::ActionId],
100) -> std::result::Result<ActionLayout, WIRActionLayoutError> {
101 program
102 .validate()
103 .map_err(WIRActionLayoutError::InvalidWIR)?;
104 let mut emitter = EmitContext {
105 program,
106 catalog,
107 locale: locale.clone(),
108 fallback: None,
109 fallback_ids: Vec::new(),
110 force_hero_constructors: false,
111 out: String::new(),
112 line_count: 0,
113 };
114 for action in actions {
115 emitter
116 .action(*action, 0, false)
117 .map_err(WIRActionLayoutError::Emission)?;
118 }
119 Ok(ActionLayout {
120 width: emitter.line_count,
121 })
122}