1use serde::{Deserialize, Serialize};
4
5use super::{DeclarationProvenance, Program, ProgramProvenance, action_argument_count, fit};
6use crate::source::{FileId, Position, SourceFile, Span};
7
8pub const TEXT_V1: &str = "workshop-rs/text-v1";
10
11pub const MAPPED_TEXT_V1: &str = "workshop-rs/mapped-text-v1";
14
15#[derive(Debug, Clone, PartialEq, Eq)]
28pub struct SourceMap {
29 files: Vec<String>,
30 shape: Shape,
31 spans: Vec<MappedNode>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
37#[non_exhaustive]
38pub struct MappedText {
39 pub text: String,
41 pub map: SourceMap,
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
50#[non_exhaustive]
51pub enum SourceMapError {
52 GlobalVariableCount {
53 expected: usize,
54 found: usize,
55 },
56 PlayerVariableCount {
57 expected: usize,
58 found: usize,
59 },
60 SubroutineCount {
61 expected: usize,
62 found: usize,
63 },
64 RuleCount {
65 expected: usize,
66 found: usize,
67 },
68 ConditionCount {
69 rule: usize,
70 expected: usize,
71 found: usize,
72 },
73 ActionCount {
74 rule: usize,
75 expected: usize,
76 found: usize,
77 },
78 InvalidPosition,
80 DuplicateEntry,
82 EmptyEntry,
84 UnknownFile(usize),
86 InvalidSpan(Span),
88 UnsupportedFormat(String),
90 Malformed(String),
92}
93
94impl std::fmt::Display for SourceMapError {
95 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 let mismatch = |formatter: &mut std::fmt::Formatter<'_>, what, expected, found| {
97 write!(
98 formatter,
99 "source map shape mismatch: expected {expected} {what}, found {found}"
100 )
101 };
102 match self {
103 Self::GlobalVariableCount { expected, found } => {
104 mismatch(formatter, "global variables", expected, found)
105 }
106 Self::PlayerVariableCount { expected, found } => {
107 mismatch(formatter, "player variables", expected, found)
108 }
109 Self::SubroutineCount { expected, found } => {
110 mismatch(formatter, "subroutines", expected, found)
111 }
112 Self::RuleCount { expected, found } => mismatch(formatter, "rules", expected, found),
113 Self::ConditionCount {
114 rule,
115 expected,
116 found,
117 } => mismatch(
118 formatter,
119 &format!("conditions in rule {rule}"),
120 expected,
121 found,
122 ),
123 Self::ActionCount {
124 rule,
125 expected,
126 found,
127 } => mismatch(
128 formatter,
129 &format!("actions in rule {rule}"),
130 expected,
131 found,
132 ),
133 Self::InvalidPosition => {
134 write!(formatter, "source map entry is outside the program shape")
135 }
136 Self::DuplicateEntry => write!(formatter, "source map maps a node twice"),
137 Self::EmptyEntry => write!(formatter, "source map declaration entry has no span"),
138 Self::UnknownFile(file) => {
139 write!(formatter, "source map span references unknown file {file}")
140 }
141 Self::InvalidSpan(span) => write!(formatter, "invalid source map span {span:?}"),
142 Self::UnsupportedFormat(format) => {
143 write!(formatter, "unsupported mapped text format {format:?}")
144 }
145 Self::Malformed(message) => write!(formatter, "malformed mapped text: {message}"),
146 }
147 }
148}
149
150impl std::error::Error for SourceMapError {}
151
152impl SourceMap {
153 pub fn extract(program: &Program) -> Self {
158 let mut spans = Vec::new();
159 push_declarations(
160 program,
161 program.global_variables.len(),
162 |provenance| &provenance.global_variables,
163 |index, span, name_span| MappedNode::GlobalVariable {
164 index,
165 span,
166 name_span,
167 },
168 &mut spans,
169 );
170 push_declarations(
171 program,
172 program.player_variables.len(),
173 |provenance| &provenance.player_variables,
174 |index, span, name_span| MappedNode::PlayerVariable {
175 index,
176 span,
177 name_span,
178 },
179 &mut spans,
180 );
181 push_declarations(
182 program,
183 program.subroutines.len(),
184 |provenance| &provenance.subroutines,
185 |index, span, name_span| MappedNode::Subroutine {
186 index,
187 span,
188 name_span,
189 },
190 &mut spans,
191 );
192 for (rule, public) in program.rules.iter().enumerate() {
193 if let Some(span) = program.rule_span(rule) {
194 spans.push(MappedNode::Rule {
195 rule,
196 span: span.into(),
197 });
198 }
199 for condition in 0..public.conditions.len() {
200 if let Some(span) = program.condition_span(rule, condition) {
201 spans.push(MappedNode::Condition {
202 rule,
203 condition,
204 span: span.into(),
205 });
206 }
207 }
208 for (action, public_action) in public.actions.iter().enumerate() {
209 if let Some(span) = program.action_span(rule, action) {
210 spans.push(MappedNode::Action {
211 rule,
212 action,
213 span: span.into(),
214 });
215 }
216 for argument in 0..action_argument_count(public_action) {
217 if let Some(span) = program.action_argument_span(rule, action, argument) {
218 spans.push(MappedNode::ActionArgument {
219 rule,
220 action,
221 argument,
222 span: span.into(),
223 });
224 }
225 }
226 }
227 }
228 Self {
229 files: program.files.iter().map(|file| file.path.clone()).collect(),
230 shape: Shape::of(program),
231 spans,
232 }
233 }
234
235 pub fn files(&self) -> &[String] {
237 &self.files
238 }
239
240 pub fn apply(&self, program: &mut Program) -> Result<(), SourceMapError> {
247 self.shape.check(program)?;
248
249 let mut provenance = ProgramProvenance::default();
250 fit(
251 &mut provenance.global_variables,
252 self.shape.global_variables,
253 );
254 fit(
255 &mut provenance.player_variables,
256 self.shape.player_variables,
257 );
258 fit(&mut provenance.subroutines, self.shape.subroutines);
259 fit(&mut provenance.rules, self.shape.rules.len());
260 for (rule, shape) in provenance.rules.iter_mut().zip(&self.shape.rules) {
261 fit(&mut rule.conditions, shape.conditions);
262 fit(&mut rule.actions, shape.actions);
263 }
264
265 for node in &self.spans {
266 match node {
267 MappedNode::GlobalVariable {
268 index,
269 span,
270 name_span,
271 } => {
272 let declaration = provenance
273 .global_variables
274 .get_mut(*index)
275 .ok_or(SourceMapError::InvalidPosition)?;
276 let mapped = self.declaration(*span, *name_span)?;
277 if declaration.span.is_some() || declaration.name_span.is_some() {
278 return Err(SourceMapError::DuplicateEntry);
279 }
280 *declaration = mapped;
281 }
282 MappedNode::PlayerVariable {
283 index,
284 span,
285 name_span,
286 } => {
287 let declaration = provenance
288 .player_variables
289 .get_mut(*index)
290 .ok_or(SourceMapError::InvalidPosition)?;
291 let mapped = self.declaration(*span, *name_span)?;
292 if declaration.span.is_some() || declaration.name_span.is_some() {
293 return Err(SourceMapError::DuplicateEntry);
294 }
295 *declaration = mapped;
296 }
297 MappedNode::Subroutine {
298 index,
299 span,
300 name_span,
301 } => {
302 let declaration = provenance
303 .subroutines
304 .get_mut(*index)
305 .ok_or(SourceMapError::InvalidPosition)?;
306 let mapped = self.declaration(*span, *name_span)?;
307 if declaration.span.is_some() || declaration.name_span.is_some() {
308 return Err(SourceMapError::DuplicateEntry);
309 }
310 *declaration = mapped;
311 }
312 MappedNode::Rule { rule, span } => {
313 let span = self.span(*span)?;
314 let slot = &mut provenance
315 .rules
316 .get_mut(*rule)
317 .ok_or(SourceMapError::InvalidPosition)?
318 .span;
319 if slot.replace(span).is_some() {
320 return Err(SourceMapError::DuplicateEntry);
321 }
322 }
323 MappedNode::Condition {
324 rule,
325 condition,
326 span,
327 } => {
328 let span = self.span(*span)?;
329 let slot = provenance
330 .rules
331 .get_mut(*rule)
332 .and_then(|rule| rule.conditions.get_mut(*condition))
333 .ok_or(SourceMapError::InvalidPosition)?;
334 if slot.replace(span).is_some() {
335 return Err(SourceMapError::DuplicateEntry);
336 }
337 }
338 MappedNode::Action { rule, action, span } => {
339 let span = self.span(*span)?;
340 let slot = &mut provenance
341 .rules
342 .get_mut(*rule)
343 .and_then(|rule| rule.actions.get_mut(*action))
344 .ok_or(SourceMapError::InvalidPosition)?
345 .span;
346 if slot.replace(span).is_some() {
347 return Err(SourceMapError::DuplicateEntry);
348 }
349 }
350 MappedNode::ActionArgument {
351 rule,
352 action,
353 argument,
354 span,
355 } => {
356 let span = self.span(*span)?;
357 let count = program
358 .rules
359 .get(*rule)
360 .and_then(|rule| rule.actions.get(*action))
361 .map(action_argument_count)
362 .ok_or(SourceMapError::InvalidPosition)?;
363 if *argument >= count {
364 return Err(SourceMapError::InvalidPosition);
365 }
366 let arguments = &mut provenance
367 .rules
368 .get_mut(*rule)
369 .and_then(|rule| rule.actions.get_mut(*action))
370 .ok_or(SourceMapError::InvalidPosition)?
371 .arguments;
372 fit(arguments, count);
373 if arguments[*argument].replace(span).is_some() {
374 return Err(SourceMapError::DuplicateEntry);
375 }
376 }
377 }
378 }
379
380 program.files.clear();
381 for path in &self.files {
382 program.add_file(SourceFile::new(path.clone()));
383 }
384 program.provenance = Some(Box::new(provenance));
385 Ok(())
386 }
387
388 fn span(&self, wire: WireSpan) -> Result<Span, SourceMapError> {
389 if wire.file >= self.files.len() {
390 return Err(SourceMapError::UnknownFile(wire.file));
391 }
392 let span = Span::from(wire);
393 if !span.is_valid() {
394 return Err(SourceMapError::InvalidSpan(span));
395 }
396 Ok(span)
397 }
398
399 fn declaration(
400 &self,
401 span: Option<WireSpan>,
402 name_span: Option<WireSpan>,
403 ) -> Result<DeclarationProvenance, SourceMapError> {
404 if span.is_none() && name_span.is_none() {
405 return Err(SourceMapError::EmptyEntry);
406 }
407 Ok(DeclarationProvenance {
408 span: span.map(|span| self.span(span)).transpose()?,
409 name_span: name_span.map(|span| self.span(span)).transpose()?,
410 })
411 }
412}
413
414impl MappedText {
415 pub fn new(text: impl Into<String>, map: SourceMap) -> Self {
416 Self {
417 text: text.into(),
418 map,
419 }
420 }
421
422 pub fn to_json(&self) -> String {
424 let artifact = Artifact {
425 format: MAPPED_TEXT_V1.to_string(),
426 text: self.text.clone(),
427 files: self
428 .map
429 .files
430 .iter()
431 .map(|path| WireFile { path: path.clone() })
432 .collect(),
433 shape: self.map.shape.clone(),
434 spans: self.map.spans.clone(),
435 };
436 serde_json::to_string(&artifact).expect("mapped text serializes to JSON")
437 }
438
439 pub fn from_json(json: &str) -> Result<Self, SourceMapError> {
444 let value: serde_json::Value = serde_json::from_str(json)
445 .map_err(|error| SourceMapError::Malformed(error.to_string()))?;
446 match value.get("format").and_then(serde_json::Value::as_str) {
447 Some(MAPPED_TEXT_V1) => {}
448 Some(other) => return Err(SourceMapError::UnsupportedFormat(other.to_string())),
449 None => return Err(SourceMapError::Malformed("missing format".to_string())),
450 }
451 let artifact: Artifact = serde_json::from_value(value)
452 .map_err(|error| SourceMapError::Malformed(error.to_string()))?;
453 Ok(Self {
454 text: artifact.text,
455 map: SourceMap {
456 files: artifact.files.into_iter().map(|file| file.path).collect(),
457 shape: artifact.shape,
458 spans: artifact.spans,
459 },
460 })
461 }
462}
463
464fn push_declarations(
465 program: &Program,
466 count: usize,
467 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
468 node: impl Fn(usize, Option<WireSpan>, Option<WireSpan>) -> MappedNode,
469 output: &mut Vec<MappedNode>,
470) {
471 for index in 0..count {
472 let declaration = program.declaration_provenance(&recorded, count, index);
473 if declaration.span.is_some() || declaration.name_span.is_some() {
474 output.push(node(
475 index,
476 declaration.span.map(WireSpan::from),
477 declaration.name_span.map(WireSpan::from),
478 ));
479 }
480 }
481}
482
483#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
484struct Artifact {
485 format: String,
486 text: String,
487 files: Vec<WireFile>,
488 shape: Shape,
489 spans: Vec<MappedNode>,
490}
491
492#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
493struct WireFile {
494 path: String,
495}
496
497#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
498struct Shape {
499 global_variables: usize,
500 player_variables: usize,
501 subroutines: usize,
502 rules: Vec<RuleShape>,
503}
504
505#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
506struct RuleShape {
507 conditions: usize,
508 actions: usize,
509}
510
511impl Shape {
512 fn of(program: &Program) -> Self {
513 Self {
514 global_variables: program.global_variables.len(),
515 player_variables: program.player_variables.len(),
516 subroutines: program.subroutines.len(),
517 rules: program
518 .rules
519 .iter()
520 .map(|rule| RuleShape {
521 conditions: rule.conditions.len(),
522 actions: rule.actions.len(),
523 })
524 .collect(),
525 }
526 }
527
528 fn check(&self, program: &Program) -> Result<(), SourceMapError> {
529 let found = Self::of(program);
530 if self.global_variables != found.global_variables {
531 return Err(SourceMapError::GlobalVariableCount {
532 expected: self.global_variables,
533 found: found.global_variables,
534 });
535 }
536 if self.player_variables != found.player_variables {
537 return Err(SourceMapError::PlayerVariableCount {
538 expected: self.player_variables,
539 found: found.player_variables,
540 });
541 }
542 if self.subroutines != found.subroutines {
543 return Err(SourceMapError::SubroutineCount {
544 expected: self.subroutines,
545 found: found.subroutines,
546 });
547 }
548 if self.rules.len() != found.rules.len() {
549 return Err(SourceMapError::RuleCount {
550 expected: self.rules.len(),
551 found: found.rules.len(),
552 });
553 }
554 for (rule, (expected, found)) in self.rules.iter().zip(&found.rules).enumerate() {
555 if expected.conditions != found.conditions {
556 return Err(SourceMapError::ConditionCount {
557 rule,
558 expected: expected.conditions,
559 found: found.conditions,
560 });
561 }
562 if expected.actions != found.actions {
563 return Err(SourceMapError::ActionCount {
564 rule,
565 expected: expected.actions,
566 found: found.actions,
567 });
568 }
569 }
570 Ok(())
571 }
572}
573
574#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
575#[serde(tag = "node", rename_all = "snake_case")]
576enum MappedNode {
577 Rule {
578 rule: usize,
579 span: WireSpan,
580 },
581 Condition {
582 rule: usize,
583 condition: usize,
584 span: WireSpan,
585 },
586 Action {
587 rule: usize,
588 action: usize,
589 span: WireSpan,
590 },
591 ActionArgument {
592 rule: usize,
593 action: usize,
594 argument: usize,
595 span: WireSpan,
596 },
597 GlobalVariable {
598 index: usize,
599 #[serde(default, skip_serializing_if = "Option::is_none")]
600 span: Option<WireSpan>,
601 #[serde(default, skip_serializing_if = "Option::is_none")]
602 name_span: Option<WireSpan>,
603 },
604 PlayerVariable {
605 index: usize,
606 #[serde(default, skip_serializing_if = "Option::is_none")]
607 span: Option<WireSpan>,
608 #[serde(default, skip_serializing_if = "Option::is_none")]
609 name_span: Option<WireSpan>,
610 },
611 Subroutine {
612 index: usize,
613 #[serde(default, skip_serializing_if = "Option::is_none")]
614 span: Option<WireSpan>,
615 #[serde(default, skip_serializing_if = "Option::is_none")]
616 name_span: Option<WireSpan>,
617 },
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
621struct WireSpan {
622 file: usize,
623 start: WirePosition,
624 end: WirePosition,
625}
626
627#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
628struct WirePosition {
629 line: u32,
630 column: u32,
631}
632
633impl From<Span> for WireSpan {
634 fn from(span: Span) -> Self {
635 Self {
636 file: span.file.index(),
637 start: span.start.into(),
638 end: span.end.into(),
639 }
640 }
641}
642
643impl From<WireSpan> for Span {
644 fn from(wire: WireSpan) -> Self {
645 Span::new(
646 FileId::from_index(wire.file),
647 wire.start.into(),
648 wire.end.into(),
649 )
650 }
651}
652
653impl From<Position> for WirePosition {
654 fn from(position: Position) -> Self {
655 Self {
656 line: position.line,
657 column: position.col,
658 }
659 }
660}
661
662impl From<WirePosition> for Position {
663 fn from(position: WirePosition) -> Self {
664 Position::new(position.line, position.column)
665 }
666}