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}