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