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