a1_notation/lib.rs
1//! # a1_notation
2//!
3//! A library for parsing to and from A1 spreadsheet notation. A1 notation uses letters A-Z for
4//! columns and a one-based number for the row. So for example at position `(0, 0)` of a spreadsheet
5//! (the top left corner) is "A1". `(1, 1)` is "B2", `(1, 0)` is "B1", etc.
6//!
7//! ## Instantiating `A1`s
8//!
9//! The most common need is to parse a string:
10//!
11//! ```
12//! use a1_notation::{A1, Address, Column, RangeOrCell, Row};
13//! use std::str::FromStr;
14//!
15//! let a1 = A1::from_str("A1").unwrap();
16//! // it parses it into an instance:
17//! assert_eq!(a1,
18//! A1 {
19//! sheet_name: None,
20//! reference: RangeOrCell::Cell(Address {
21//! column: Column { absolute: false, x: 0 },
22//! row: Row { absolute: false, y: 0 },
23//! }),
24//! });
25//! // and can display it back:
26//! assert_eq!(&a1.to_string(), "A1");
27//!
28//! // you can also just call `a1_notation::new`:
29//! let from_col_a_to_d = a1_notation::new("Foo!A:D").unwrap();
30//! assert_eq!(from_col_a_to_d,
31//! A1 {
32//! sheet_name: Some("Foo".to_string()),
33//! reference: RangeOrCell::ColumnRange {
34//! from: Column { absolute: false, x: 0 },
35//! to: Column { absolute: false, x: 3 },
36//! },
37//! });
38//!
39//! assert_eq!(&from_col_a_to_d.to_string(), "Foo!A:D");
40//! ```
41//!
42//! If you have zero-based coordinates and want to represent them as A1, there are several `fn`s
43//! for instantiating:
44//!
45//! ```
46//! # use a1_notation::*;
47//! // to create a reference to a specific cell:
48//! assert_eq!(&a1_notation::cell(2, 2).to_string(), "C3");
49//!
50//! // a reference to an entire column
51//! assert_eq!(&a1_notation::column(5).to_string(), "F:F");
52//!
53//! // or an entire row
54//! assert_eq!(&a1_notation::row(5).to_string(), "6:6");
55//!
56//! // and finally a range between two cells:
57//! assert_eq!(&a1_notation::range((0, 0), (4, 4)).to_string(), "A1:E5");
58//! ```
59//!
60//! ## Contains
61//!
62//! Given all the various combinations or cells, ranges, row ranges, column ranges and
63//! non-contiguous ranges you can calculate if one reference contains another.
64//!
65//! ```
66//! # use a1_notation::*;
67//! // a column contains any cell in that column:
68//! let col_a = a1_notation::new("A:A").unwrap();
69//! let a1 = a1_notation::new("A1").unwrap();
70//! assert!(col_a.contains(&a1));
71//!
72//! // likewise, a row range contains anything between it:
73//! let top_5_rows = a1_notation::new("1:5").unwrap();
74//! let b2 = a1_notation::new("B2").unwrap();
75//! assert!(top_5_rows.contains(&b2));
76//!
77//! // and a range between two points works as you'd expect (it forms a rectangle)
78//! let c3_to_j20 = a1_notation::new("C3:J20").unwrap();
79//! let d5 = a1_notation::new("D5").unwrap();
80//! assert!(c3_to_j20.contains(&d5));
81//! ```
82//!
83//! ## Into/From/AsRef impls
84//!
85//! As much as possible it implements `Into`/`From` and `AsRef` to convert between the various
86//! structs. Generally you can go from more specific to less specific but not the other way
87//! around. You typically should work with `A1` structs but you can also use these traits to work
88//! with these lower level ones and cast them upwards.
89//!
90//! ```
91//! # use a1_notation::*;
92//! // an address can act as a column or row using AsRef:
93//! let a1 = Address::new(0, 0);
94//! assert_eq!(&Column::new(0), a1.as_ref());
95//! assert_eq!(&Row::new(0), a1.as_ref());
96//!
97//! // addresses, columns and rows can `into()` "upwards" to an A1 or RangeOrCell
98//! let col_b = Column::new(1);
99//! assert_eq!(
100//! RangeOrCell::ColumnRange {
101//! from: Column::new(1),
102//! to: Column::new(1),
103//! },
104//! col_b.into());
105//!
106//! assert_eq!(
107//! A1 {
108//! sheet_name: None,
109//! reference: RangeOrCell::ColumnRange {
110//! from: Column::new(1),
111//! to: Column::new(1),
112//! },
113//! },
114//! col_b.into());
115//! ```
116//!
117//! ## Shifting
118//!
119//! You can move references (and ranges) around:
120//!
121//! ```
122//! # use a1_notation::*;
123//! // A1 -> D1 -> D3 -> C3
124//! assert_eq!(
125//! &a1_notation::cell(0, 0)
126//! .shift_right(3)
127//! .shift_down(2)
128//! .shift_left(1)
129//! .to_string(),
130//! "C3");
131//! ```
132//!
133//! ## Iterators
134//!
135//! You can iterate through the various types of ranges.
136//!
137//! ```
138//! # use crate::*;
139//! // a cell just emits itself (once)
140//! assert_eq!(
141//! a1_notation::cell(0, 0)
142//! .iter().map(|r| r.to_string()).collect::<Vec<_>>(),
143//! vec!["A1"]);
144//!
145//! // a column range iterates column-wise
146//! assert_eq!(
147//! a1_notation::new("D:G").unwrap()
148//! .iter().map(|r| r.to_string()).collect::<Vec<_>>(),
149//! vec!["D:D", "E:E", "F:F", "G:G"]);
150//!
151//! // and a row range goes row-wise
152//! assert_eq!(
153//! a1_notation::new("3:6").unwrap()
154//! .iter().map(|r| r.to_string()).collect::<Vec<_>>(),
155//! vec!["3:3", "4:4", "5:5", "6:6"]);
156//!
157//! // a grid-based range goes row-by-row
158//! assert_eq!(
159//! a1_notation::new("A1:C3").unwrap()
160//! .iter().map(|r| r.to_string()).collect::<Vec<_>>(),
161//! vec![
162//! "A1", "B1", "C1",
163//! "A2", "B2", "C2",
164//! "A3", "B3", "C3",
165//! ]);
166//! ```
167//!
168//! ### A1 Reference Examples
169//!
170//! Here is a table illustrating A1 references:
171//!
172//! | **Reference** | **Meaning** |
173//! |:----------------|:--------------------------|
174//! | `"A1"` | Cell A1 |
175//! | `"A1:B5"` | Cells A1 through B5 |
176//! | `"C5:D9,G9:H16"`| A multiple-area selection |
177//! | `"A:A"` | Column A |
178//! | `"1:1"` | Row 1 |
179//! | `"A:C"` | Columns A through C |
180//! | `"1:5"` | Rows 1 through 5 |
181//! | `"1:1,3:3,8:8"` | Rows 1, 3, and 8 |
182//! | `"A:A,C:C,F:F"` | Columns A, C, and F |
183//!
184//
185// TODO:
186//
187// * implement `IntoIterator` for `RangeOrCell`
188//
189use std::str::FromStr;
190
191mod a1;
192mod address;
193mod column;
194mod error;
195mod range_or_cell;
196mod row;
197
198pub use a1::A1;
199pub use address::Address;
200pub use column::Column;
201pub use error::Error;
202pub use range_or_cell::RangeOrCell;
203pub use row::Row;
204
205pub type Result<T> = std::result::Result<T, Error>;
206
207pub type Index = usize;
208
209pub(crate) static ALPHA: [char; 26] = [
210 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
211 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
212];
213
214/// Create an `A1` referencing a cell at a given address (x/y)
215pub fn cell(x: Index, y: Index) -> A1 {
216 A1 {
217 sheet_name: None,
218 reference: RangeOrCell::Cell(Address::new(x, y)),
219 }
220}
221
222/// Parse an A1-style string into an `A1`
223pub fn new(s: &str) -> Result<A1> {
224 A1::from_str(s)
225}
226
227/// Create a range between two cells
228pub fn range<A: Into<Address>>(from: A, to: A) -> A1 {
229 A1 {
230 sheet_name: None,
231 reference: RangeOrCell::Range {
232 from: from.into(),
233 to: to.into(),
234 },
235 }
236}
237
238/// An entire column
239pub fn column<C: Into<Column>>(x: C) -> A1 {
240 A1 {
241 sheet_name: None,
242 reference: RangeOrCell::column(x),
243 }
244}
245
246/// A range between two columns
247pub fn column_range<R: Into<Column>>(xa: R, xb: R) -> A1 {
248 A1 {
249 sheet_name: None,
250 reference: RangeOrCell::column_range(xa, xb),
251 }
252}
253
254/// An entire row
255pub fn row<R: Into<Row>>(y: R) -> A1 {
256 A1 {
257 sheet_name: None,
258 reference: RangeOrCell::row(y),
259 }
260}
261
262/// A range between two rows
263pub fn row_range<R: Into<Row>>(ya: R, yb: R) -> A1 {
264 A1 {
265 sheet_name: None,
266 reference: RangeOrCell::row_range(ya, yb),
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn cell() {
276 let a1 = crate::cell(2, 3);
277
278 assert_eq!(a1.sheet_name, None);
279 assert_eq!(a1.reference, RangeOrCell::Cell(Address::new(2, 3)));
280 }
281
282 #[test]
283 fn new() {
284 let a1 = crate::new("Foo!A1").unwrap();
285
286 assert_eq!(a1.sheet_name, Some("Foo".to_string()));
287 assert_eq!(a1.reference, RangeOrCell::Cell(Address::new(0, 0)));
288 }
289
290 #[test]
291 fn range() {
292 let a1 = crate::range((0, 0), (22, 33));
293
294 assert_eq!(a1.sheet_name, None);
295 assert_eq!(
296 a1.reference,
297 RangeOrCell::Range {
298 from: Address::new(0, 0),
299 to: Address::new(22, 33),
300 }
301 );
302 }
303
304 #[test]
305 fn column() {
306 let a1 = crate::column(22);
307
308 assert_eq!(a1.sheet_name, None);
309 assert_eq!(
310 a1.reference,
311 RangeOrCell::ColumnRange {
312 from: Column::new(22),
313 to: Column::new(22),
314 }
315 );
316 }
317
318 #[test]
319 fn column_range() {
320 let a1 = crate::column_range(1, 420);
321
322 assert_eq!(a1.sheet_name, None);
323 assert_eq!(
324 a1.reference,
325 RangeOrCell::ColumnRange {
326 from: Column::new(1),
327 to: Column::new(420),
328 }
329 );
330 }
331
332 #[test]
333 fn row() {
334 let a1 = crate::row(11);
335
336 assert_eq!(a1.sheet_name, None);
337 assert_eq!(
338 a1.reference,
339 RangeOrCell::RowRange {
340 from: Row::new(11),
341 to: Row::new(11),
342 }
343 );
344 }
345
346 #[test]
347 fn row_range() {
348 let a1 = crate::row_range(0, 42);
349
350 assert_eq!(a1.sheet_name, None);
351 assert_eq!(
352 a1.reference,
353 RangeOrCell::RowRange {
354 from: Row::new(0),
355 to: Row::new(42),
356 }
357 );
358 }
359}