1use crate::{
2 ipld_error::IpldError,
3 parse::span::Span,
4};
5
6use sp_cid::Cid;
7use sp_ipld::Ipld;
8
9use sp_std::{
10 borrow::ToOwned,
11 convert::TryInto,
12 fmt,
13};
14
15use alloc::string::String;
16
17#[derive(PartialEq, Clone, Copy, Debug)]
19pub struct Position {
20 pub input: Cid,
21 pub from_offset: u64,
22 pub from_line: u64,
23 pub from_column: u64,
24 pub upto_offset: u64,
25 pub upto_line: u64,
26 pub upto_column: u64,
27}
28
29#[derive(PartialEq, Clone, Copy)]
31pub enum Pos {
32 None,
33 Some(Position),
34}
35impl fmt::Debug for Pos {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "") }
37}
38
39impl Position {
40 pub fn range(self, input: String) -> String {
43 let mut res = String::new();
44 let gutter = format!("{}", self.upto_line).len();
45 let pad = format!("{: >gutter$}", self.from_line, gutter = gutter).len()
46 + 3
47 + self.from_column as usize;
48 res.push_str(&format!("{}▼\n", " ".to_owned().repeat(pad)));
49 for (line_number, line) in input.lines().enumerate() {
50 if ((line_number as u64 + 1) >= self.from_line)
51 && ((line_number as u64 + 1) <= self.upto_line)
52 {
53 res.push_str(&format!(
54 "{: >gutter$} | {}\n",
55 line_number + 1,
56 line,
57 gutter = gutter
58 ));
59 }
60 }
61 let pad = format!("{: >gutter$}", self.upto_line, gutter = gutter).len()
62 + 3
63 + self.upto_column as usize;
64 res.push_str(&format!("{}▲", " ".to_owned().repeat(pad)));
65 res
66 }
67
68 pub fn to_ipld(self) -> Ipld {
70 Ipld::List(vec![
71 Ipld::Link(self.input),
72 Ipld::Integer(self.from_offset as i128),
73 Ipld::Integer(self.from_line as i128),
74 Ipld::Integer(self.from_column as i128),
75 Ipld::Integer(self.upto_offset as i128),
76 Ipld::Integer(self.upto_line as i128),
77 Ipld::Integer(self.upto_column as i128),
78 ])
79 }
80
81 pub fn from_upto(input: Cid, from: Span, upto: Span) -> Self {
83 Self {
84 input,
85 from_offset: (from.location_offset() as u64),
86 from_line: from.location_line() as u64,
87 from_column: from.get_utf8_column() as u64,
88 upto_offset: (upto.location_offset() as u64),
89 upto_line: upto.location_line() as u64,
90 upto_column: upto.get_utf8_column() as u64,
91 }
92 }
93
94 pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
96 match ipld {
97 Ipld::List(xs) => match xs.as_slice() {
98 #[rustfmt::skip]
99 [Ipld::Link(cid),
100 Ipld::Integer(from_offset),
101 Ipld::Integer(from_line),
102 Ipld::Integer(from_column),
103 Ipld::Integer(upto_offset),
104 Ipld::Integer(upto_line),
105 Ipld::Integer(upto_column),
106 ] => {
107 let from_offset: u64 =
108 (*from_offset).try_into().map_err(IpldError::U64)?;
109 let from_line: u64 =
110 (*from_line).try_into().map_err(IpldError::U64)?;
111 let from_column: u64 =
112 (*from_column).try_into().map_err(IpldError::U64)?;
113 let upto_offset: u64 =
114 (*upto_offset).try_into().map_err(IpldError::U64)?;
115 let upto_line: u64 =
116 (*upto_line).try_into().map_err(IpldError::U64)?;
117 let upto_column: u64 =
118 (*upto_column).try_into().map_err(IpldError::U64)?;
119 Ok(Position {
120 input: *cid,
121 from_offset,
122 from_line,
123 from_column,
124 upto_offset,
125 upto_line,
126 upto_column})
127 }
128 xs => Err(IpldError::Position(Ipld::List(xs.to_owned()))),
129 },
130 xs => Err(IpldError::Position(xs.to_owned())),
131 }
132 }
133}
134
135impl Pos {
136 pub fn to_ipld(self) -> Ipld {
138 match self {
139 Self::None => Ipld::Null,
140 Self::Some(x) => Position::to_ipld(x),
141 }
142 }
143
144 pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
146 match ipld {
147 Ipld::Null => Ok(Self::None),
148 xs => {
149 let pos = Position::from_ipld(&xs)?;
150 Ok(Self::Some(pos))
151 }
152 }
153 }
154
155 pub fn from_upto(input: Cid, from: Span, upto: Span) -> Self {
156 Pos::Some(Position::from_upto(input, from, upto))
157 }
158}
159
160impl fmt::Display for Position {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(
163 f,
164 "{}@{}:{}-{}:{}",
165 self.input,
166 self.from_line,
167 self.from_column,
168 self.upto_line,
169 self.upto_column
170 )
171 }
172}
173
174#[cfg(test)]
175pub mod tests {
176 use super::*;
177 use quickcheck::{
178 Arbitrary,
179 Gen,
180 };
181
182 use crate::tests::arbitrary_cid;
183
184 impl Arbitrary for Position {
185 fn arbitrary(g: &mut Gen) -> Self {
186 Position {
187 input: arbitrary_cid(g),
188 from_offset: Arbitrary::arbitrary(g),
189 from_line: Arbitrary::arbitrary(g),
190 from_column: Arbitrary::arbitrary(g),
191 upto_offset: Arbitrary::arbitrary(g),
192 upto_line: Arbitrary::arbitrary(g),
193 upto_column: Arbitrary::arbitrary(g),
194 }
195 }
196 }
197
198 impl Arbitrary for Pos {
199 fn arbitrary(g: &mut Gen) -> Self {
200 let x: bool = Arbitrary::arbitrary(g);
201 if x { Self::None } else { Self::Some(Arbitrary::arbitrary(g)) }
202 }
203 }
204
205 #[quickcheck]
206 fn position_ipld(x: Position) -> bool {
207 match Position::from_ipld(&x.to_ipld()) {
208 Ok(y) => x == y,
209 _ => false,
210 }
211 }
212
213 #[quickcheck]
214 fn pos_ipld(x: Pos) -> bool {
215 match Pos::from_ipld(&x.to_ipld()) {
216 Ok(y) => x == y,
217 _ => false,
218 }
219 }
220}