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