add_ed/buffer/buffer.rs
1use super::*;
2
3use crate::{EdError, Result};
4
5/// Declare a type over Vec<PubLine>, to be able to add some utility methods
6///
7/// Unlike [`Buffer`] it is possible to easily and safely edit a Clipboard via
8/// its `AsRef<Vec<PubLine>>` implementation. You are advised to use this,
9/// combined with `.into()`, to safely interact with `Buffer` instances. See
10/// example code in [`Buffer`] for how this should be done.
11///
12/// Needed due to orphan rules.
13#[derive(Clone, Debug, PartialEq, Eq)]
14pub struct Clipboard {
15 inner: Vec<PubLine>,
16}
17impl Clipboard {
18 pub fn new() -> Self {
19 Self{ inner: Vec::new() }
20 }
21}
22
23impl std::ops::Deref for Clipboard {
24 type Target = Vec<PubLine>;
25 fn deref(&self) -> &Self::Target {
26 &self.inner
27 }
28}
29impl std::ops::DerefMut for Clipboard {
30 fn deref_mut(&mut self) -> &mut Self::Target {
31 &mut self.inner
32 }
33}
34impl<'a> From<&'a [Line]> for Clipboard {
35 fn from(l: &'a [Line]) -> Self {
36 let mut tmp = Vec::new();
37 for line in l {
38 tmp.push(line.into());
39 }
40 Self{
41 inner: tmp,
42 }
43 }
44}
45impl<'a> TryFrom<&'a [(char, &str)]> for Clipboard {
46 type Error = LineTextError;
47 fn try_from(l: &'a [(char,&str)]) -> core::result::Result<Self, Self::Error> {
48 let mut tmp = Vec::new();
49 for line in l {
50 tmp.push(line.try_into()?);
51 }
52 Ok(Self{
53 inner: tmp,
54 })
55 }
56}
57impl<'a> TryFrom<&'a [&str]> for Clipboard {
58 type Error = LineTextError;
59 fn try_from(l: &'a [&str]) -> core::result::Result<Self, Self::Error> {
60 let mut tmp = Vec::new();
61 for line in l {
62 tmp.push(line.try_into()?);
63 }
64 Ok(Self{
65 inner: tmp,
66 })
67 }
68}
69
70impl Into<Vec<Line>> for &Clipboard {
71 fn into(self) -> Vec<Line> {
72 let mut tmp = Vec::new();
73 for line in &self.inner {
74 tmp.push(line.into());
75 }
76 tmp
77 }
78}
79
80/// Declare a type over Vec<Line>, to be able to add some utility methods
81///
82/// Though `Deref<Target = Vec<Line>>` gives free access to the Buffer internals
83/// the restrictions upon Line makes it a bit difficult to make use of this. The
84/// intended method is to convert to and from [`Clipboard`] or [`PubLine`] as
85/// shown below.
86///
87/// Examples of how to construct Line instances to insert into the Buffer:
88/// ```
89/// use add_ed::{
90/// Buffer,
91/// Clipboard,
92/// PubLine,
93/// };
94///
95/// // Note that all the Results we unwrap will only occur if the text is
96/// // invalid for the Buffer, wherein text must be newline terminated and not
97/// // contain any other newlines.
98///
99/// let mut buffer = Buffer::default();
100/// // Note that we can create a PubLine by tag+text tuples
101/// let pub_line: PubLine = ('a', "test\n").try_into().expect("Invalid line");
102/// buffer.push((&pub_line).into());
103/// // Or just from &str
104/// let pub_line: PubLine = "data\n".try_into().expect("Invalid line");
105/// buffer.push((&pub_line).into());
106/// // And when you want to add a single line from &str you don't need to
107/// // create an intermediate PubLine, since Line implements from &str
108/// buffer.push("&str\n".try_into().expect("Invalid line"));
109/// ```
110///
111/// Examples of how to copy out and insert multiple lines of data:
112/// ```
113/// use add_ed::{
114/// Buffer,
115/// Clipboard,
116/// PubLine,
117/// };
118///
119/// // Note that all the Results we unwrap will only occur if the text is
120/// // invalid for the Buffer, wherein text must be newline terminated and not
121/// // contain any other newlines.
122///
123/// let mut buffer = Buffer::default();
124/// // Since we can construct a Clipboard from a slice of (char, &str)
125/// let pub_lines: Clipboard = (&vec![('b', "more\n"),('\0', "data\n")][..])
126/// .try_into().expect("Invalid line");
127/// buffer.append(&mut (&pub_lines).into());
128/// // And of course you don't have to give tags if you don't want to
129/// let pub_lines: Clipboard = (&vec!["last\n","data\n"][..])
130/// .try_into().expect("Invalid line");
131/// buffer.append(&mut (&pub_lines).into());
132/// // Getting data out in clipboard format is quite easy (and generally the
133/// // way to go, unless you are just moving Lines around).
134/// let fetched_data: Clipboard = (&buffer[..]).into();
135/// // If you want you can also use the iterators on Buffer
136/// let fetched_data: Vec<String> = buffer.get_lines((1,buffer.len()))
137/// .expect("Invalid selection")
138/// .map(|s| s.to_owned())
139/// .collect()
140/// ;
141/// ```
142#[derive(Debug, PartialEq)]
143pub struct Buffer {
144 pub inner: Vec<Line>,
145}
146impl std::ops::Deref for Buffer {
147 type Target = Vec<Line>;
148 fn deref(&self) -> &Self::Target {
149 &self.inner
150 }
151}
152impl std::ops::DerefMut for Buffer {
153 fn deref_mut(&mut self) -> &mut Self::Target {
154 &mut self.inner
155 }
156}
157// Manually implement a special clone for History
158impl Snapshot for Buffer {
159 fn create_snapshot(&self) -> Self {
160 let mut new_inner = Vec::new();
161 for line in self.inner.iter() {
162 new_inner.push(line.create_snapshot());
163 }
164 Self{ inner: new_inner }
165 }
166}
167impl Default for Buffer {
168 fn default() -> Self{ Self{ inner: Vec::new() } }
169}
170impl Buffer {
171 /// Verify that an index is valid to operate on
172 ///
173 /// Doesn't mean that there exists a line at the index.
174 /// Note the related [`Ed::verify_line`] and [`Ed::verify_selection`].
175 pub fn verify_index(
176 &self,
177 index: usize,
178 ) -> Result<()> {
179 let buffer_len = self.len();
180 if index > buffer_len {
181 Err(EdError::IndexTooBig{index, buffer_len})
182 } else {
183 Ok(())
184 }
185 }
186 /// Verfy that a line exists at given index
187 ///
188 /// Note the related [`Ed::verify_index`] and [`Ed::verify_selection`].
189 pub fn verify_line(
190 &self,
191 index: usize,
192 ) -> Result<()> {
193 if index == 0 { Err(EdError::Line0Invalid) }
194 else { self.verify_index(index) }
195 }
196 /// Verify that all the lines in selection exist
197 ///
198 /// Note the related [`Ed::verify_index`] [`Ed::verify_line`].
199 pub fn verify_selection(
200 &self,
201 selection: (usize, usize),
202 ) -> Result<()> {
203 self.verify_line(selection.0)?;
204 self.verify_line(selection.1)?;
205 if selection.0 > selection.1 {
206 Err(EdError::SelectionEmpty(selection))
207 } else {
208 Ok(())
209 }
210 }
211
212 /// Get the lines in the given selection
213 ///
214 /// Returns an iterator over &str to save on allocations.
215 pub fn get_lines(
216 &self,
217 selection: (usize, usize),
218 ) -> Result<LinesIter> {
219 self.verify_selection(selection)?;
220 Ok(self[selection.0 - 1 .. selection.1]
221 .iter()
222 .map(get_lines_helper as fn(&Line) -> &str)
223 .into()
224 )
225 }
226 /// Get the lines in the given selection with their tags
227 ///
228 /// Returns an iterator of (char, &str) to save on allocations.
229 pub fn get_tagged_lines(
230 &self,
231 selection: (usize, usize),
232 ) -> Result<TaggedLinesIter> {
233 self.verify_selection(selection)?;
234 Ok(self[selection.0 - 1 .. selection.1]
235 .iter()
236 .map(get_tagged_lines_helper as fn(&Line) -> (char, &str))
237 .into()
238 )
239 }
240}
241
242// These functions need to be declared, because the map iterator over a closure
243// has an un-name-able type (and we don't wish to use generics towards IO for
244// performance and being able to make dyn IO).
245fn get_lines_helper(line: &Line) -> &str {
246 &line.text[..]
247}
248fn get_tagged_lines_helper(line: &Line) -> (char, &str) {
249 (line.tag(), &line.text[..])
250}