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 {
492 settings.clear_spans();
493 }
494 program.provenance = Some(Box::new(provenance));
495 program.record_identities();
496 Ok(())
497 }
498
499 fn span(&self, wire: WireSpan) -> Result<Span, SourceMapError> {
500 if wire.file >= self.files.len() {
501 return Err(SourceMapError::UnknownFile(wire.file));
502 }
503 let span = Span::from(wire);
504 if !span.is_valid() {
505 return Err(SourceMapError::InvalidSpan(span));
506 }
507 Ok(span)
508 }
509
510 fn declaration(
511 &self,
512 span: Option<WireSpan>,
513 name_span: Option<WireSpan>,
514 ) -> Result<DeclarationProvenance, SourceMapError> {
515 if span.is_none() && name_span.is_none() {
516 return Err(SourceMapError::EmptyEntry);
517 }
518 Ok(DeclarationProvenance {
519 span: span.map(|span| self.span(span)).transpose()?,
520 name_span: name_span.map(|span| self.span(span)).transpose()?,
521 ..DeclarationProvenance::default()
522 })
523 }
524
525 fn mapped_children(
526 &self,
527 wire: &[WireValue],
528 value: &Value,
529 ) -> Result<Vec<ValueProvenance>, SourceMapError> {
530 let children = value_children(value);
531 let mut mapped = wire
532 .iter()
533 .zip(children.iter().copied())
534 .map(|(wire, value)| self.mapped_value(wire, value))
535 .collect::<Result<Vec<_>, _>>()?;
536 mapped.resize_with(children.len(), ValueProvenance::default);
537 Ok(mapped)
538 }
539
540 fn mapped_value(
541 &self,
542 wire: &WireValue,
543 value: &Value,
544 ) -> Result<ValueProvenance, SourceMapError> {
545 Ok(ValueProvenance {
546 span: wire.span.map(|span| self.span(span)).transpose()?,
547 identifier: wire
548 .identifier_span
549 .map(|span| self.span(span))
550 .transpose()?,
551 children: self.mapped_children(&wire.children, value)?,
552 ..ValueProvenance::default()
553 })
554 }
555}
556
557impl MappedText {
558 pub fn new(text: impl Into<String>, map: SourceMap) -> Self {
559 Self {
560 text: text.into(),
561 map,
562 }
563 }
564
565 pub fn to_json(&self) -> String {
567 let artifact = Artifact {
568 format: MAPPED_TEXT_V1.to_string(),
569 text: self.text.clone(),
570 files: self
571 .map
572 .files
573 .iter()
574 .map(|path| WireFile { path: path.clone() })
575 .collect(),
576 shape: self.map.shape.clone(),
577 spans: self.map.spans.clone(),
578 };
579 serde_json::to_string(&artifact).expect("mapped text serializes to JSON")
580 }
581
582 pub fn from_json(json: &str) -> Result<Self, SourceMapError> {
587 let value: serde_json::Value = serde_json::from_str(json)
588 .map_err(|error| SourceMapError::Malformed(error.to_string()))?;
589 match value.get("format").and_then(serde_json::Value::as_str) {
590 Some(MAPPED_TEXT_V1) => {}
591 Some(other) => return Err(SourceMapError::UnsupportedFormat(other.to_string())),
592 None => return Err(SourceMapError::Malformed("missing format".to_string())),
593 }
594 let artifact: Artifact = serde_json::from_value(value)
595 .map_err(|error| SourceMapError::Malformed(error.to_string()))?;
596 Ok(Self {
597 text: artifact.text,
598 map: SourceMap {
599 files: artifact.files.into_iter().map(|file| file.path).collect(),
600 shape: artifact.shape,
601 spans: artifact.spans,
602 },
603 })
604 }
605}
606
607fn wire_children(children: &[ValueProvenance], values: &[&Value]) -> Vec<WireValue> {
608 if children.len() != values.len() {
609 return Vec::new();
610 }
611 let mut wire: Vec<WireValue> = children
612 .iter()
613 .zip(values.iter().copied())
614 .map(|(child, value)| wire_value(child, value))
615 .collect();
616 let last = wire
617 .iter()
618 .rposition(|child| !wire_value_unmapped(child))
619 .map(|index| index + 1)
620 .unwrap_or(0);
621 wire.truncate(last);
622 wire
623}
624
625fn wire_value(provenance: &ValueProvenance, value: &Value) -> WireValue {
626 let (span, identifier) = if provenance.identity == value_identity(value) {
627 (provenance.span, provenance.identifier)
628 } else {
629 Default::default()
630 };
631 WireValue {
632 span: span.map(WireSpan::from),
633 identifier_span: identifier.map(WireSpan::from),
634 children: wire_children(&provenance.children, &value_children(value)),
635 }
636}
637
638fn wire_value_unmapped(value: &WireValue) -> bool {
639 value.span.is_none()
640 && value.identifier_span.is_none()
641 && value.children.iter().all(wire_value_unmapped)
642}
643
644fn push_declarations<T: std::fmt::Debug>(
645 program: &Program,
646 nodes: &[T],
647 recorded: impl Fn(&ProgramProvenance) -> &[DeclarationProvenance],
648 node: impl Fn(usize, Option<WireSpan>, Option<WireSpan>) -> MappedNode,
649 output: &mut Vec<MappedNode>,
650) {
651 for index in 0..nodes.len() {
652 let declaration = program.declaration_provenance(&recorded, nodes, index);
653 if declaration.span.is_some() || declaration.name_span.is_some() {
654 output.push(node(
655 index,
656 declaration.span.map(WireSpan::from),
657 declaration.name_span.map(WireSpan::from),
658 ));
659 }
660 }
661}
662
663#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
664struct Artifact {
665 format: String,
666 text: String,
667 files: Vec<WireFile>,
668 shape: Shape,
669 spans: Vec<MappedNode>,
670}
671
672#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
673struct WireFile {
674 path: String,
675}
676
677#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
678struct Shape {
679 global_variables: usize,
680 player_variables: usize,
681 subroutines: usize,
682 rules: Vec<RuleShape>,
683}
684
685#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
686struct RuleShape {
687 conditions: usize,
688 actions: usize,
689}
690
691impl Shape {
692 fn of(program: &Program) -> Self {
693 Self {
694 global_variables: program.global_variables.len(),
695 player_variables: program.player_variables.len(),
696 subroutines: program.subroutines.len(),
697 rules: program
698 .rules
699 .iter()
700 .map(|rule| RuleShape {
701 conditions: rule.conditions.len(),
702 actions: rule.actions.len(),
703 })
704 .collect(),
705 }
706 }
707
708 fn check(&self, program: &Program) -> Result<(), SourceMapError> {
709 let found = Self::of(program);
710 if self.global_variables != found.global_variables {
711 return Err(SourceMapError::GlobalVariableCount {
712 expected: self.global_variables,
713 found: found.global_variables,
714 });
715 }
716 if self.player_variables != found.player_variables {
717 return Err(SourceMapError::PlayerVariableCount {
718 expected: self.player_variables,
719 found: found.player_variables,
720 });
721 }
722 if self.subroutines != found.subroutines {
723 return Err(SourceMapError::SubroutineCount {
724 expected: self.subroutines,
725 found: found.subroutines,
726 });
727 }
728 if self.rules.len() != found.rules.len() {
729 return Err(SourceMapError::RuleCount {
730 expected: self.rules.len(),
731 found: found.rules.len(),
732 });
733 }
734 for (rule, (expected, found)) in self.rules.iter().zip(&found.rules).enumerate() {
735 if expected.conditions != found.conditions {
736 return Err(SourceMapError::ConditionCount {
737 rule,
738 expected: expected.conditions,
739 found: found.conditions,
740 });
741 }
742 if expected.actions != found.actions {
743 return Err(SourceMapError::ActionCount {
744 rule,
745 expected: expected.actions,
746 found: found.actions,
747 });
748 }
749 }
750 Ok(())
751 }
752}
753
754#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
755#[serde(tag = "node", rename_all = "snake_case")]
756enum MappedNode {
757 Rule {
758 rule: usize,
759 #[serde(default, skip_serializing_if = "Option::is_none")]
760 span: Option<WireSpan>,
761 #[serde(default, skip_serializing_if = "Option::is_none")]
762 name_span: Option<WireSpan>,
763 #[serde(default, skip_serializing_if = "Option::is_none")]
764 event_name_span: Option<WireSpan>,
765 },
766 Condition {
767 rule: usize,
768 condition: usize,
769 #[serde(default, skip_serializing_if = "Option::is_none")]
770 span: Option<WireSpan>,
771 #[serde(default, skip_serializing_if = "Option::is_none")]
772 identifier_span: Option<WireSpan>,
773 #[serde(default, skip_serializing_if = "Vec::is_empty")]
774 children: Vec<WireValue>,
775 },
776 Action {
777 rule: usize,
778 action: usize,
779 #[serde(default, skip_serializing_if = "Option::is_none")]
780 span: Option<WireSpan>,
781 #[serde(default, skip_serializing_if = "Option::is_none")]
782 identifier_span: Option<WireSpan>,
783 },
784 ActionArgument {
785 rule: usize,
786 action: usize,
787 argument: usize,
788 #[serde(default, skip_serializing_if = "Option::is_none")]
789 span: Option<WireSpan>,
790 #[serde(default, skip_serializing_if = "Option::is_none")]
791 identifier_span: Option<WireSpan>,
792 #[serde(default, skip_serializing_if = "Vec::is_empty")]
793 children: Vec<WireValue>,
794 },
795 GlobalVariable {
796 index: usize,
797 #[serde(default, skip_serializing_if = "Option::is_none")]
798 span: Option<WireSpan>,
799 #[serde(default, skip_serializing_if = "Option::is_none")]
800 name_span: Option<WireSpan>,
801 },
802 PlayerVariable {
803 index: usize,
804 #[serde(default, skip_serializing_if = "Option::is_none")]
805 span: Option<WireSpan>,
806 #[serde(default, skip_serializing_if = "Option::is_none")]
807 name_span: Option<WireSpan>,
808 },
809 Subroutine {
810 index: usize,
811 #[serde(default, skip_serializing_if = "Option::is_none")]
812 span: Option<WireSpan>,
813 #[serde(default, skip_serializing_if = "Option::is_none")]
814 name_span: Option<WireSpan>,
815 },
816}
817
818#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
819struct WireValue {
820 #[serde(default, skip_serializing_if = "Option::is_none")]
821 span: Option<WireSpan>,
822 #[serde(default, skip_serializing_if = "Option::is_none")]
823 identifier_span: Option<WireSpan>,
824 #[serde(default, skip_serializing_if = "Vec::is_empty")]
825 children: Vec<WireValue>,
826}
827
828#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
829struct WireSpan {
830 file: usize,
831 start: WirePosition,
832 end: WirePosition,
833}
834
835#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
836struct WirePosition {
837 line: u32,
838 column: u32,
839}
840
841impl From<Span> for WireSpan {
842 fn from(span: Span) -> Self {
843 Self {
844 file: span.file.index(),
845 start: span.start.into(),
846 end: span.end.into(),
847 }
848 }
849}
850
851impl From<WireSpan> for Span {
852 fn from(wire: WireSpan) -> Self {
853 Span::new(
854 FileId::from_index(wire.file),
855 wire.start.into(),
856 wire.end.into(),
857 )
858 }
859}
860
861impl From<Position> for WirePosition {
862 fn from(position: Position) -> Self {
863 Self {
864 line: position.line,
865 column: position.col,
866 }
867 }
868}
869
870impl From<WirePosition> for Position {
871 fn from(position: WirePosition) -> Self {
872 Position::new(position.line, position.column)
873 }
874}