a1_notation/a1/
mod.rs

1//! # A1
2//!
3//! `A1` is the most encompassing and generic of the structs included in this package - it can be
4//! any kind of range (cell, range, column range, etc) also includes the sheet name if set.  It can
5//! perform all of the most general operations like shifting up/down/left/right and `contains`
6//! set operation against another `A1`.  
7//!
8//! Unless you need the specificity of another type you should prefer to write code that operates
9//! in terms of `A1`s.  Also when parsing `str`s, this crate will generally return results in
10//! terms of `A1`s rather than the other types.
11//!
12//! ### Links
13//!
14//! * [Google Sheets API Overview](https://developers.google.com/sheets/api/guides/concepts)
15//! * [Refer to Cells and Ranges by Using A1 Notation](https://learn.microsoft.com/en-us/office/vba/excel/concepts/cells-and-ranges/refer-to-cells-and-ranges-by-using-a1-notation)
16//!
17use crate::RangeOrCell;
18use std::str;
19
20mod display;
21mod from_str;
22mod into_iterator;
23mod iterator;
24
25#[cfg_attr(
26    feature = "rkyv",
27    derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize)
28)]
29#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
30#[derive(Clone, Debug, PartialEq)]
31pub struct A1 {
32    pub sheet_name: Option<String>,
33    pub reference: RangeOrCell,
34}
35
36impl A1 {
37    /// Is `other` completely contained within `self`?  They also must be in the same sheet
38    /// (meaning `self.sheet_name` == `other.sheet_name`).
39    pub fn contains(&self, other: &Self) -> bool {
40        self.sheet_name == other.sheet_name && self.reference.contains(&other.reference)
41    }
42
43    /// Returns a new `A1` shifted downwards by `rows` rows.
44    pub fn shift_down(self, rows: usize) -> Self {
45        Self {
46            reference: self.reference.shift_down(rows),
47            ..self
48        }
49    }
50
51    /// Returns a new `A1` shifted left by `columns` columns.
52    pub fn shift_left(self, columns: usize) -> Self {
53        Self {
54            reference: self.reference.shift_left(columns),
55            ..self
56        }
57    }
58
59    /// Returns a new `A1` shifted right by `columns` columns.
60    pub fn shift_right(self, columns: usize) -> Self {
61        Self {
62            reference: self.reference.shift_right(columns),
63            ..self
64        }
65    }
66
67    /// Returns a new `A1` shifted up by `rows` rows.
68    pub fn shift_up(self, rows: usize) -> Self {
69        Self {
70            reference: self.reference.shift_up(rows),
71            ..self
72        }
73    }
74
75    /// Clone into a new `A1` with the given `sheet_name`
76    pub fn with_sheet_name(self, sheet_name: &str) -> Self {
77        Self {
78            sheet_name: Some(sheet_name.to_owned()),
79            ..self
80        }
81    }
82
83    /// Return a new `A1` with the given X position set.  If the `reference` already has an `x`
84    /// component, it will be overwritten in the returned value.
85    pub fn with_x(self, x: usize) -> Self {
86        Self {
87            reference: self.reference.with_x(x),
88            ..self
89        }
90    }
91
92    /// Return a new `A1` with the given Y position set.  If the `reference` already has an `y`
93    /// component, it will be overwritten in the returned value.
94    pub fn with_y(self, y: usize) -> Self {
95        Self {
96            reference: self.reference.with_y(y),
97            ..self
98        }
99    }
100
101    pub fn without_sheet_name(self) -> Self {
102        Self {
103            sheet_name: None,
104            ..self
105        }
106    }
107}
108
109#[cfg(test)]
110mod tests {
111    use crate::*;
112
113    #[test]
114    fn contains_different_name() {
115        let a1_a = A1 {
116            sheet_name: Some("Something".to_string()),
117            reference: RangeOrCell::Cell((1, 1).into()),
118        };
119        let a1_b = A1 {
120            sheet_name: Some("Something else".to_string()),
121            reference: RangeOrCell::Cell((1, 1).into()),
122        };
123
124        assert!(!a1_a.contains(&a1_b));
125    }
126
127    #[test]
128    fn contains_true() {
129        let a1_a = A1 {
130            sheet_name: None,
131            reference: RangeOrCell::Cell((1, 1).into()),
132        };
133        let a1_b = A1 {
134            sheet_name: None,
135            reference: RangeOrCell::Cell((1, 1).into()),
136        };
137
138        assert!(a1_a.contains(&a1_b));
139    }
140
141    #[test]
142    fn shift_down() {
143        let a1 = A1 {
144            sheet_name: Some("Test1".to_string()),
145            reference: RangeOrCell::Cell((1, 1).into()),
146        };
147
148        assert_eq!("Test1!B3", a1.shift_down(1).to_string());
149    }
150
151    #[test]
152    fn shift_left() {
153        let a1 = A1 {
154            sheet_name: Some("Test1".to_string()),
155            reference: RangeOrCell::Cell((1, 1).into()),
156        };
157
158        assert_eq!("Test1!A2", a1.shift_left(1).to_string());
159    }
160
161    #[test]
162    fn shift_right() {
163        let a1 = A1 {
164            sheet_name: None,
165            reference: RangeOrCell::Cell((1, 1).into()),
166        };
167
168        assert_eq!("C2", a1.shift_right(1).to_string());
169    }
170
171    #[test]
172    fn shift_up() {
173        let a1 = A1 {
174            sheet_name: None,
175            reference: RangeOrCell::Cell((1, 1).into()),
176        };
177
178        assert_eq!("B1", a1.shift_up(1).to_string());
179    }
180
181    #[test]
182    fn with_sheet_name() {
183        let a1 = A1 {
184            sheet_name: None,
185            reference: RangeOrCell::Cell((1, 1).into()),
186        };
187
188        assert_eq!(
189            Some("foo".to_string()),
190            a1.with_sheet_name("foo").sheet_name
191        );
192    }
193
194    #[test]
195    fn with_x() {
196        let a1 = A1 {
197            sheet_name: None,
198            reference: RangeOrCell::Cell((1, 1).into()),
199        };
200
201        assert_eq!("F2", a1.with_x(5).to_string());
202    }
203
204    #[test]
205    fn with_y() {
206        let a1 = A1 {
207            sheet_name: None,
208            reference: RangeOrCell::Cell((1, 1).into()),
209        };
210
211        assert_eq!("B22", a1.with_y(21).to_string());
212    }
213
214    #[test]
215    fn without_sheet_name() {
216        let a1 = A1 {
217            sheet_name: Some("foo".to_string()),
218            reference: RangeOrCell::Cell((1, 1).into()),
219        };
220
221        assert_eq!(None, a1.without_sheet_name().sheet_name);
222    }
223}