1use serde::{Deserialize, Serialize};
2
3use super::identity::{condition_identity, value_identity};
4use super::{
5 DeclarationProvenance, Program, ProgramProvenance, Value, ValueProvenance,
6 action_argument_values, fit, value_children,
7};
8use crate::source::{FileId, Position, SourceFile, Span};
9
10pub const TEXT_V1: &str = "workshop-rs/text-v1";
12
13pub const MAPPED_TEXT_V1: &str = "workshop-rs/mapped-text-v1";
16
17#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct SourceMap {
38 files: Vec<String>,
39 shape: Shape,
40 spans: Vec<MappedNode>,
41}
42
43#[derive(Debug, Clone, PartialEq, Eq)]
46#[non_exhaustive]
47pub struct MappedText {
48 pub text: String,
50 pub map: SourceMap,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
59#[non_exhaustive]
60pub enum SourceMapError {
61 GlobalVariableCount {
62 expected: usize,
63 found: usize,
64 },
65 PlayerVariableCount {
66 expected: usize,
67 found: usize,
68 },
69 SubroutineCount {
70 expected: usize,
71 found: usize,
72 },
73 RuleCount {
74 expected: usize,
75 found: usize,
76 },
77 ConditionCount {
78 rule: usize,
79 expected: usize,
80 found: usize,
81 },
82 ActionCount {
83 rule: usize,
84 expected: usize,
85 found: usize,
86 },
87 InvalidPosition,
89 DuplicateEntry,
91 EmptyEntry,
93 UnknownFile(usize),
95 InvalidSpan(Span),
97 UnsupportedFormat(String),
99 Malformed(String),
101}
102
103impl std::fmt::Display for SourceMapError {
104 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 let mismatch = |formatter: &mut std::fmt::Formatter<'_>, what, expected, found| {
106 write!(
107 formatter,
108 "source map shape mismatch: expected {expected} {what}, found {found}"
109 )
110 };
111 match self {
112 Self::GlobalVariableCount { expected, found } => {
113 mismatch(formatter, "global variables", expected, found)
114 }
115 Self::PlayerVariableCount { expected, found } => {
116 mismatch(formatter, "player variables", expected, found)
117 }
118 Self::SubroutineCount { expected, found } => {
119 mismatch(formatter, "subroutines", expected, found)
120 }
121 Self::RuleCount { expected, found } => mismatch(formatter, "rules", expected, found),
122 Self::ConditionCount {
123 rule,
124 expected,
125 found,
126 } => mismatch(
127 formatter,
128 &format!("conditions in rule {rule}"),
129 expected,
130 found,
131 ),
132 Self::ActionCount {
133 rule,
134 expected,
135 found,
136 } => mismatch(
137 formatter,
138 &format!("actions in rule {rule}"),
139 expected,
140 found,
141 ),
142 Self::InvalidPosition => {
143 write!(formatter, "source map entry is outside the program shape")
144 }
145 Self::DuplicateEntry => write!(formatter, "source map maps a node twice"),
146 Self::EmptyEntry => write!(formatter, "source map entry has no position"),
147 Self::UnknownFile(file) => {
148 write!(formatter, "source map span references unknown file {file}")
149 }
150 Self::InvalidSpan(span) => write!(formatter, "invalid source map span {span:?}"),
151 Self::UnsupportedFormat(format) => {
152 write!(formatter, "unsupported mapped text format {format:?}")
153 }
154 Self::Malformed(message) => write!(formatter, "malformed mapped text: {message}"),
155 }
156 }
157}
158
159impl std::error::Error for SourceMapError {}
160
161impl SourceMap {
162 pub fn extract(program: &Program) -> Self {
167 let mut spans = Vec::new();
168 push_declarations(
169 program,
170 &program.global_variables,
171 |provenance| &provenance.global_variables,
172 |index, span, name_span| MappedNode::GlobalVariable {
173 index,
174 span,
175 name_span,
176 },
177 &mut spans,
178 );
179 push_declarations(
180 program,
181 &program.player_variables,
182 |provenance| &provenance.player_variables,
183 |index, span, name_span| MappedNode::PlayerVariable {
184 index,
185 span,
186 name_span,
187 },
188 &mut spans,
189 );
190 push_declarations(
191 program,
192 &program.subroutines,
193 |provenance| &provenance.subroutines,
194 |index, span, name_span| MappedNode::Subroutine {
195 index,
196 span,
197 name_span,
198 },
199 &mut spans,
200 );
201 for (rule, public) in program.rules.iter().enumerate() {
202 if let Some(provenance) = program.rule_provenance(rule)
203 && (provenance.span.is_some()
204 || provenance.name.is_some()
205 || provenance.event_name.is_some())
206 {
207 spans.push(MappedNode::Rule {
208 rule,
209 span: provenance.span.map(WireSpan::from),
210 name_span: provenance.name.map(WireSpan::from),
211 event_name_span: provenance.event_name.map(WireSpan::from),
212 });
213 }
214 for (condition, public_condition) in public.conditions.iter().enumerate() {
215 let Some(record) = program.condition_record(rule, condition) else {
216 continue;
217 };
218 let children =
219 wire_children(&record.children, &value_children(&public_condition.value));
220 let (span, identifier) = if record.identity == condition_identity(public_condition)
221 {
222 (record.span, record.identifier)
223 } else {
224 Default::default()
225 };
226 if span.is_some() || identifier.is_some() || !children.is_empty() {
227 spans.push(MappedNode::Condition {
228 rule,
229 condition,
230 span: span.map(WireSpan::from),
231 identifier_span: identifier.map(WireSpan::from),
232 children,
233 });
234 }
235 }
236 for (action, public_action) in public.actions.iter().enumerate() {
237 if let Some(provenance) = program.action_provenance(rule, action)
238 && (provenance.span.is_some() || provenance.identifier.is_some())
239 {
240 spans.push(MappedNode::Action {
241 rule,
242 action,
243 span: provenance.span.map(WireSpan::from),
244 identifier_span: provenance.identifier.map(WireSpan::from),
245 });
246 }
247 for (argument, value) in action_argument_values(public_action)
248 .iter()
249 .copied()
250 .enumerate()
251 {
252 let Some(record) = program.argument_record(rule, action, argument) else {
253 continue;
254 };
255 let children = wire_children(&record.children, &value_children(value));
256 let (span, identifier) = if record.identity == value_identity(value) {
257 (record.span, record.identifier)
258 } else {
259 Default::default()
260 };
261 if span.is_some() || identifier.is_some() || !children.is_empty() {
262 spans.push(MappedNode::ActionArgument {
263 rule,
264 action,
265 argument,
266 span: span.map(WireSpan::from),
267 identifier_span: identifier.map(WireSpan::from),
268 children,
269 });
270 }
271 }
272 }
273 }
274 Self {
275 files: program.files.iter().map(|file| file.path.clone()).collect(),
276 shape: Shape::of(program),
277 spans,
278 }
279 }
280
281 pub fn files(&self) -> &[String] {
283 &self.files
284 }
285
286 pub fn apply(&self, program: &mut Program) -> Result<(), SourceMapError> {
293 self.shape.check(program)?;
294
295 let mut provenance = ProgramProvenance::default();
296 fit(
297 &mut provenance.global_variables,
298 self.shape.global_variables,
299 );
300 fit(
301 &mut provenance.player_variables,
302 self.shape.player_variables,
303 );
304 fit(&mut provenance.subroutines, self.shape.subroutines);
305 fit(&mut provenance.rules, self.shape.rules.len());
306 for (rule, shape) in provenance.rules.iter_mut().zip(&self.shape.rules) {
307 fit(&mut rule.conditions, shape.conditions);
308 fit(&mut rule.actions, shape.actions);
309 }
310
311 for node in &self.spans {
312 match node {
313 MappedNode::GlobalVariable {
314 index,
315 span,
316 name_span,
317 } => {
318 let declaration = provenance
319 .global_variables
320 .get_mut(*index)
321 .ok_or(SourceMapError::InvalidPosition)?;
322 let mapped = self.declaration(*span, *name_span)?;
323 if declaration.span.is_some() || declaration.name_span.is_some() {
324 return Err(SourceMapError::DuplicateEntry);
325 }
326 *declaration = mapped;
327 }
328 MappedNode::PlayerVariable {
329 index,
330 span,
331 name_span,
332 } => {
333 let declaration = provenance
334 .player_variables
335 .get_mut(*index)
336 .ok_or(SourceMapError::InvalidPosition)?;
337 let mapped = self.declaration(*span, *name_span)?;
338 if declaration.span.is_some() || declaration.name_span.is_some() {
339 return Err(SourceMapError::DuplicateEntry);
340 }
341 *declaration = mapped;
342 }
343 MappedNode::Subroutine {
344 index,
345 span,
346 name_span,
347 } => {
348 let declaration = provenance
349 .subroutines
350 .get_mut(*index)
351 .ok_or(SourceMapError::InvalidPosition)?;
352 let mapped = self.declaration(*span, *name_span)?;
353 if declaration.span.is_some() || declaration.name_span.is_some() {
354 return Err(SourceMapError::DuplicateEntry);
355 }
356 *declaration = mapped;
357 }
358 MappedNode::Rule {
359 rule,
360 span,
361 name_span,
362 event_name_span,
363 } => {
364 let span = span.map(|span| self.span(span)).transpose()?;
365 let name = name_span.map(|span| self.span(span)).transpose()?;
366 let event_name = event_name_span.map(|span| self.span(span)).transpose()?;
367 let slot = provenance
368 .rules
369 .get_mut(*rule)
370 .ok_or(SourceMapError::InvalidPosition)?;
371 if slot.span.is_some() || slot.name.is_some() || slot.event_name.is_some() {
372 return Err(SourceMapError::DuplicateEntry);
373 }
374 if span.is_none() && name.is_none() && event_name.is_none() {
375 return Err(SourceMapError::EmptyEntry);
376 }
377 slot.span = span;
378 slot.name = name;
379 slot.event_name = event_name;
380 }
381 MappedNode::Condition {
382 rule,
383 condition,
384 span,
385 identifier_span,
386 children,
387 } => {
388 let span = span.map(|span| self.span(span)).transpose()?;
389 let identifier = identifier_span.map(|span| self.span(span)).transpose()?;
390 let has_children = children.iter().any(|child| !wire_value_unmapped(child));
391 let children = self.mapped_children(
392 children,
393 &program
394 .rules
395 .get(*rule)
396 .and_then(|rule| rule.conditions.get(*condition))
397 .ok_or(SourceMapError::InvalidPosition)?
398 .value,
399 )?;
400 let slot = provenance
401 .rules
402 .get_mut(*rule)
403 .and_then(|rule| rule.conditions.get_mut(*condition))
404 .ok_or(SourceMapError::InvalidPosition)?;
405 if slot.span.is_some() || slot.identifier.is_some() || !slot.children.is_empty()
406 {
407 return Err(SourceMapError::DuplicateEntry);
408 }
409 if span.is_none() && identifier.is_none() && !has_children {
410 return Err(SourceMapError::EmptyEntry);
411 }
412 slot.span = span;
413 slot.identifier = identifier;
414 slot.children = children;
415 }
416 MappedNode::Action {
417 rule,
418 action,
419 span,
420 identifier_span,
421 } => {
422 let span = span.map(|span| self.span(span)).transpose()?;
423 let identifier = identifier_span.map(|span| self.span(span)).transpose()?;
424 let slot = provenance
425 .rules
426 .get_mut(*rule)
427 .and_then(|rule| rule.actions.get_mut(*action))
428 .ok_or(SourceMapError::InvalidPosition)?;
429 if slot.span.is_some() || slot.identifier.is_some() {
430 return Err(SourceMapError::DuplicateEntry);
431 }
432 if span.is_none() && identifier.is_none() {
433 return Err(SourceMapError::EmptyEntry);
434 }
435 slot.span = span;
436 slot.identifier = identifier;
437 }
438 MappedNode::ActionArgument {
439 rule,
440 action,
441 argument,
442 span,
443 identifier_span,
444 children,
445 } => {
446 let span = span.map(|span| self.span(span)).transpose()?;
447 let identifier = identifier_span.map(|span| self.span(span)).transpose()?;
448 let argument_values = program
449 .rules
450 .get(*rule)
451 .and_then(|rule| rule.actions.get(*action))
452 .map(action_argument_values)
453 .ok_or(SourceMapError::InvalidPosition)?;
454 let Some(&value) = argument_values.get(*argument) else {
455 return Err(SourceMapError::InvalidPosition);
456 };
457 let count = argument_values.len();
458 let has_children = children.iter().any(|child| !wire_value_unmapped(child));
459 let children = self.mapped_children(children, value)?;
460 let arguments = &mut provenance
461 .rules
462 .get_mut(*rule)
463 .and_then(|rule| rule.actions.get_mut(*action))
464 .ok_or(SourceMapError::InvalidPosition)?
465 .arguments;
466 fit(arguments, count);
467 let slot = &mut arguments[*argument];
468 if slot.span.is_some() || slot.identifier.is_some() || !slot.children.is_empty()
469 {
470 return Err(SourceMapError::DuplicateEntry);
471 }
472 if span.is_none() && identifier.is_none() && !has_children {
473 return Err(SourceMapError::EmptyEntry);
474 }
475 slot.span = span;
476 slot.identifier = identifier;
477 slot.children = children;
478 }
479 }
480 }
481
482 program.files.clear();
483 for path in &self.files {
484 program.add_file(SourceFile::new(path.clone()));
485 }
486 if let Some(settings) = &mut program.settings {
487 settings.clear_spans();
488 }
489 program.provenance = Some(Box::new(provenance));
490 program.record_identities();
491 Ok(())
492 }
493
494 fn span(&self, wire: WireSpan) -> Result<Span, SourceMapError> {
495 if wire.file >= self.files.len() {
496 return Err(SourceMapError::UnknownFile(wire.file));
497 }
498 let span = Span::from(wire);
499 if !span.is_valid() {
500 return Err(SourceMapError::InvalidSpan(span));
501 }
502 Ok(span)
503 }
504
505 fn declaration(
506 &self,
507 span: Option<WireSpan>,
508 name_span: Option<WireSpan>,
509 ) -> Result<DeclarationProvenance, SourceMapError> {
510 if span.is_none() && name_span.is_none() {
511 return Err(SourceMapError::EmptyEntry);
512 }
513 Ok(DeclarationProvenance {
514 span: span.map(|span| self.span(span)).transpose()?,
515 name_span: name_span.map(|span| self.span(span)).transpose()?,
516 ..DeclarationProvenance::default()
517 })
518 }
519
520 fn mapped_children(
521 &self,
522 wire: &[WireValue],
523 value: &Value,
524 ) -> Result<Vec<ValueProvenance>, SourceMapError> {
525 let children = value_children(value);
526 let mut mapped = wire
527 .iter()
528 .zip(children.iter().copied())
529 .map(|(wire, value)| self.mapped_value(wire, value))
530 .collect::<Result<Vec<_>, _>>()?;
531 mapped.resize_with(children.len(), ValueProvenance::default);
532 Ok(mapped)
533 }
534
535 fn mapped_value(
536 &self,
537 wire: &WireValue,
538 value: &Value,
539 ) -> Result<ValueProvenance, SourceMapError> {
540 Ok(ValueProvenance {
541 span: wire.span.map(|span| self.span(span)).transpose()?,
542 identifier: wire
543 .identifier_span
544 .map(|span| self.span(span))
545 .transpose()?,
546 children: self.mapped_children(&wire.children, value)?,
547 ..ValueProvenance::default()
548 })
549 }
550}
551
552impl MappedText {
553 pub fn new(text: impl Into<String>, map: SourceMap) -> Self {
554 Self {
555 text: text.into(),
556 map,
557 }
558 }
559
560 pub fn to_json(&self) -> String {
562 let artifact = Artifact {
563 format: MAPPED_TEXT_V1.to_string(),
564 text: self.text.clone(),
565 files: self
566 .map
567 .files
568 .iter()
569 .map(|path| WireFile { path: path.clone() })
570 .collect(),
571 shape: self.map.shape.clone(),
572 spans: self.map.spans.clone(),
573 };
574 serde_json::to_string(&artifact).expect("mapped text serializes to JSON")
575 }
576
577 pub fn from_json(json: &str) -> Result<Self, SourceMapError> {
582 let value: serde_json::Value = serde_json::from_str(json)
583 .map_err(|error| SourceMapError::Malformed(error.to_string()))?;
584 match value.get("format").and_then(serde_json::Value::as_str) {
585 Some(MAPPED_TEXT_V1) => {}
586 Some(other) => return Err(SourceMapError::UnsupportedFormat(other.to_string())),
587 None => return Err(SourceMapError::Malformed("missing format".to_string())),
588 }
589 let artifact: Artifact = serde_json::from_value(value)
590 .map_err(|error| SourceMapError::Malformed(error.to_string()))?;
591 Ok(Self {
592 text: artifact.text,
593 map: SourceMap {
594 files: artifact.files.into_iter().map(|file| file.path).collect(),
595 shape: artifact.shape,
596 spans: artifact.spans,
597 },
598 })
599 }
600}
601
602fn wire_children(children: &[ValueProvenance], values: &[&Value]) -> Vec<WireValue> {
603 if children.len() != values.len() {
604 return Vec::new();
605 }
606 let mut wire: Vec<WireValue> = children
607 .iter()
608 .zip(values.iter().copied())
609 .map(|(child, value)| wire_value(child, value))
610 .collect();
611 let last = wire
612 .iter()
613 .rposition(|child| !wire_value_unmapped(child))
614 .map(|index| index + 1)
615 .unwrap_or(0);
616 wire.truncate(last);
617 wire
618}
619
620fn wire_value(provenance: &ValueProvenance, value: &Value) -> WireValue {
621 let (span, identifier) = if provenance.identity == value_identity(value) {
622 (provenance.span, provenance.identifier)
623 } else {
624 Default::default()
625 };
626 WireValue {
627 span: span.map(WireSpan::from),
628 identifier_span: identifier.map(WireSpan::from),
629 children: wire_children(&provenance.children, &value_children(value)),
630 }
631}
632
633fn wire_value_unmapped(value: &WireValue) -> bool {
634 value.span.is_none()
635 && value.identifier_span.is_none()
636 && value.children.iter().all(wire_value_unmapped)
637}
638
639fn push_declarations<T: std::fmt::Debug>(
640 program: &Program,
641 nodes: &[T],
642 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
643 node: impl Fn(usize, Option<WireSpan>, Option<WireSpan>) -> MappedNode,
644 output: &mut Vec<MappedNode>,
645) {
646 for index in 0..nodes.len() {
647 let declaration = program.declaration_provenance(&recorded, nodes, index);
648 if declaration.span.is_some() || declaration.name_span.is_some() {
649 output.push(node(
650 index,
651 declaration.span.map(WireSpan::from),
652 declaration.name_span.map(WireSpan::from),
653 ));
654 }
655 }
656}
657
658#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
659struct Artifact {
660 format: String,
661 text: String,
662 files: Vec<WireFile>,
663 shape: Shape,
664 spans: Vec<MappedNode>,
665}
666
667#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
668struct WireFile {
669 path: String,
670}
671
672#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
673struct Shape {
674 global_variables: usize,
675 player_variables: usize,
676 subroutines: usize,
677 rules: Vec<RuleShape>,
678}
679
680#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
681struct RuleShape {
682 conditions: usize,
683 actions: usize,
684}
685
686impl Shape {
687 fn of(program: &Program) -> Self {
688 Self {
689 global_variables: program.global_variables.len(),
690 player_variables: program.player_variables.len(),
691 subroutines: program.subroutines.len(),
692 rules: program
693 .rules
694 .iter()
695 .map(|rule| RuleShape {
696 conditions: rule.conditions.len(),
697 actions: rule.actions.len(),
698 })
699 .collect(),
700 }
701 }
702
703 fn check(&self, program: &Program) -> Result<(), SourceMapError> {
704 let found = Self::of(program);
705 if self.global_variables != found.global_variables {
706 return Err(SourceMapError::GlobalVariableCount {
707 expected: self.global_variables,
708 found: found.global_variables,
709 });
710 }
711 if self.player_variables != found.player_variables {
712 return Err(SourceMapError::PlayerVariableCount {
713 expected: self.player_variables,
714 found: found.player_variables,
715 });
716 }
717 if self.subroutines != found.subroutines {
718 return Err(SourceMapError::SubroutineCount {
719 expected: self.subroutines,
720 found: found.subroutines,
721 });
722 }
723 if self.rules.len() != found.rules.len() {
724 return Err(SourceMapError::RuleCount {
725 expected: self.rules.len(),
726 found: found.rules.len(),
727 });
728 }
729 for (rule, (expected, found)) in self.rules.iter().zip(&found.rules).enumerate() {
730 if expected.conditions != found.conditions {
731 return Err(SourceMapError::ConditionCount {
732 rule,
733 expected: expected.conditions,
734 found: found.conditions,
735 });
736 }
737 if expected.actions != found.actions {
738 return Err(SourceMapError::ActionCount {
739 rule,
740 expected: expected.actions,
741 found: found.actions,
742 });
743 }
744 }
745 Ok(())
746 }
747}
748
749#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
750#[serde(tag = "node", rename_all = "snake_case")]
751enum MappedNode {
752 Rule {
753 rule: usize,
754 #[serde(default, skip_serializing_if = "Option::is_none")]
755 span: Option<WireSpan>,
756 #[serde(default, skip_serializing_if = "Option::is_none")]
757 name_span: Option<WireSpan>,
758 #[serde(default, skip_serializing_if = "Option::is_none")]
759 event_name_span: Option<WireSpan>,
760 },
761 Condition {
762 rule: usize,
763 condition: usize,
764 #[serde(default, skip_serializing_if = "Option::is_none")]
765 span: Option<WireSpan>,
766 #[serde(default, skip_serializing_if = "Option::is_none")]
767 identifier_span: Option<WireSpan>,
768 #[serde(default, skip_serializing_if = "Vec::is_empty")]
769 children: Vec<WireValue>,
770 },
771 Action {
772 rule: usize,
773 action: usize,
774 #[serde(default, skip_serializing_if = "Option::is_none")]
775 span: Option<WireSpan>,
776 #[serde(default, skip_serializing_if = "Option::is_none")]
777 identifier_span: Option<WireSpan>,
778 },
779 ActionArgument {
780 rule: usize,
781 action: usize,
782 argument: usize,
783 #[serde(default, skip_serializing_if = "Option::is_none")]
784 span: Option<WireSpan>,
785 #[serde(default, skip_serializing_if = "Option::is_none")]
786 identifier_span: Option<WireSpan>,
787 #[serde(default, skip_serializing_if = "Vec::is_empty")]
788 children: Vec<WireValue>,
789 },
790 GlobalVariable {
791 index: usize,
792 #[serde(default, skip_serializing_if = "Option::is_none")]
793 span: Option<WireSpan>,
794 #[serde(default, skip_serializing_if = "Option::is_none")]
795 name_span: Option<WireSpan>,
796 },
797 PlayerVariable {
798 index: usize,
799 #[serde(default, skip_serializing_if = "Option::is_none")]
800 span: Option<WireSpan>,
801 #[serde(default, skip_serializing_if = "Option::is_none")]
802 name_span: Option<WireSpan>,
803 },
804 Subroutine {
805 index: usize,
806 #[serde(default, skip_serializing_if = "Option::is_none")]
807 span: Option<WireSpan>,
808 #[serde(default, skip_serializing_if = "Option::is_none")]
809 name_span: Option<WireSpan>,
810 },
811}
812
813#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
814struct WireValue {
815 #[serde(default, skip_serializing_if = "Option::is_none")]
816 span: Option<WireSpan>,
817 #[serde(default, skip_serializing_if = "Option::is_none")]
818 identifier_span: Option<WireSpan>,
819 #[serde(default, skip_serializing_if = "Vec::is_empty")]
820 children: Vec<WireValue>,
821}
822
823#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
824struct WireSpan {
825 file: usize,
826 start: WirePosition,
827 end: WirePosition,
828}
829
830#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
831struct WirePosition {
832 line: u32,
833 column: u32,
834}
835
836impl From<Span> for WireSpan {
837 fn from(span: Span) -> Self {
838 Self {
839 file: span.file.index(),
840 start: span.start.into(),
841 end: span.end.into(),
842 }
843 }
844}
845
846impl From<WireSpan> for Span {
847 fn from(wire: WireSpan) -> Self {
848 Span::new(
849 FileId::from_index(wire.file),
850 wire.start.into(),
851 wire.end.into(),
852 )
853 }
854}
855
856impl From<Position> for WirePosition {
857 fn from(position: Position) -> Self {
858 Self {
859 line: position.line,
860 column: position.col,
861 }
862 }
863}
864
865impl From<WirePosition> for Position {
866 fn from(position: WirePosition) -> Self {
867 Position::new(position.line, position.column)
868 }
869}