Repository: rust-lang/annotate-snippets-rs
Pull request #32 (2020-04-03 21:37:03)

Fix no copy when cast `Snippet` to `DisplayList` and #29
With this pull request the "no copy" process is completed. All the crate is free copy :smile: 
I have changed from `yaml` to `toml` because `yaml` does not implement `serde::Deserialize`



Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 086db9d..d205c63 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -20,7 +20,7 @@ yansi-term = { version = "0.1", optional = true }
 
 [dev-dependencies]
 glob = "0.3"
-serde_yaml = "0.8"
+toml = "0.5"
 serde = { version = "1.0", features = ["derive"] }
 difference = "2.0"
 yansi-term = "0.1"
diff --git a/benches/simple.rs b/benches/simple.rs
index a0da903..4c13a8f 100644
--- a/benches/simple.rs
+++ b/benches/simple.rs
@@ -33,27 +33,26 @@ fn create_snippet() {
             }
             _ => continue,
         }
-    }"#
-            .to_string(),
+    }"#,
             line_start: 51,
-            origin: Some("src/format.rs".to_string()),
+            origin: Some("src/format.rs"),
             fold: false,
             annotations: vec![
                 SourceAnnotation {
-                    label: "expected `Option<String>` because of return type".to_string(),
+                    label: "expected `Option<String>` because of return type",
                     annotation_type: AnnotationType::Warning,
                     range: (5, 19),
                 },
                 SourceAnnotation {
-                    label: "expected enum `std::option::Option`".to_string(),
+                    label: "expected enum `std::option::Option`",
                     annotation_type: AnnotationType::Error,
                     range: (26, 724),
                 },
             ],
         }],
         title: Some(Annotation {
-            label: Some("mismatched types".to_string()),
-            id: Some("E0308".to_string()),
+            label: Some("mismatched types"),
+            id: Some("E0308"),
             annotation_type: AnnotationType::Error,
         }),
         footer: vec![],
diff --git a/examples/expected_type.rs b/examples/expected_type.rs
index b12ebad..89f0ddc 100644
--- a/examples/expected_type.rs
+++ b/examples/expected_type.rs
@@ -6,7 +6,7 @@ use annotate_snippets::{
 fn main() {
     let snippet = Snippet {
         title: Some(Annotation {
-            label: Some("expected type, found `22`".to_string()),
+            label: Some("expected type, found `22`"),
             id: None,
             annotation_type: AnnotationType::Error,
         }),
@@ -14,20 +14,19 @@ fn main() {
         slices: vec![Slice {
             source: r#"                annotations: vec![SourceAnnotation {
                 label: "expected struct `annotate_snippets::snippet::Slice`, found reference"
-                    .to_string(),
-                range: <22, 25>,"#
-                .to_string(),
+                    ,
+                range: <22, 25>,"#,
             line_start: 26,
-            origin: Some("examples/footer.rs".to_string()),
+            origin: Some("examples/footer.rs"),
             fold: true,
             annotations: vec![
                 SourceAnnotation {
-                    label: "".to_string(),
+                    label: "",
                     annotation_type: AnnotationType::Error,
                     range: (205, 207),
                 },
                 SourceAnnotation {
-                    label: "while parsing this struct".to_string(),
+                    label: "while parsing this struct",
                     annotation_type: AnnotationType::Info,
                     range: (34, 50),
                 },
diff --git a/examples/footer.rs b/examples/footer.rs
index 4e331d3..f3c15c4 100644
--- a/examples/footer.rs
+++ b/examples/footer.rs
@@ -6,26 +6,24 @@ use annotate_snippets::{
 fn main() {
     let snippet = Snippet {
         title: Some(Annotation {
-            label: Some("mismatched types".to_string()),
-            id: Some("E0308".to_string()),
+            label: Some("mismatched types"),
+            id: Some("E0308"),
             annotation_type: AnnotationType::Error,
         }),
         footer: vec![Annotation {
             label: Some(
-                "expected type: `snippet::Annotation`\n   found type: `__&__snippet::Annotation`"
-                    .to_string(),
+                "expected type: `snippet::Annotation`\n   found type: `__&__snippet::Annotation`",
             ),
             id: None,
             annotation_type: AnnotationType::Note,
         }],
         slices: vec![Slice {
-            source: "        slices: vec![\"A\",".to_string(),
+            source: "        slices: vec![\"A\",",
             line_start: 13,
-            origin: Some("src/multislice.rs".to_string()),
+            origin: Some("src/multislice.rs"),
             fold: false,
             annotations: vec![SourceAnnotation {
-                label: "expected struct `annotate_snippets::snippet::Slice`, found reference"
-                    .to_string(),
+                label: "expected struct `annotate_snippets::snippet::Slice`, found reference",
                 range: (21, 24),
                 annotation_type: AnnotationType::Error,
             }],
diff --git a/examples/format.rs b/examples/format.rs
index d8c73ee..98b77a1 100644
--- a/examples/format.rs
+++ b/examples/format.rs
@@ -27,27 +27,26 @@ fn main() {
             }
             _ => continue,
         }
-    }"#
-            .to_string(),
+    }"#,
             line_start: 51,
-            origin: Some("src/format.rs".to_string()),
+            origin: Some("src/format.rs"),
             fold: false,
             annotations: vec![
                 SourceAnnotation {
-                    label: "expected `Option<String>` because of return type".to_string(),
+                    label: "expected `Option<String>` because of return type",
                     annotation_type: AnnotationType::Warning,
                     range: (5, 19),
                 },
                 SourceAnnotation {
-                    label: "expected enum `std::option::Option`".to_string(),
+                    label: "expected enum `std::option::Option`",
                     annotation_type: AnnotationType::Error,
                     range: (26, 724),
                 },
             ],
         }],
         title: Some(Annotation {
-            label: Some("mismatched types".to_string()),
-            id: Some("E0308".to_string()),
+            label: Some("mismatched types"),
+            id: Some("E0308"),
             annotation_type: AnnotationType::Error,
         }),
         footer: vec![],
diff --git a/examples/multislice.rs b/examples/multislice.rs
index fe71977..5675a07 100644
--- a/examples/multislice.rs
+++ b/examples/multislice.rs
@@ -6,23 +6,23 @@ use annotate_snippets::{
 fn main() {
     let snippet = Snippet {
         title: Some(Annotation {
-            label: Some("mismatched types".to_string()),
+            label: Some("mismatched types"),
             id: None,
             annotation_type: AnnotationType::Error,
         }),
         footer: vec![],
         slices: vec![
             Slice {
-                source: "Foo".to_string(),
+                source: "Foo",
                 line_start: 51,
-                origin: Some("src/format.rs".to_string()),
+                origin: Some("src/format.rs"),
                 fold: false,
                 annotations: vec![],
             },
             Slice {
-                source: "Faa".to_string(),
+                source: "Faa",
                 line_start: 129,
-                origin: Some("src/display.rs".to_string()),
+                origin: Some("src/display.rs"),
                 fold: false,
                 annotations: vec![],
             },
diff --git a/src/display_list/from_snippet.rs b/src/display_list/from_snippet.rs
index d9e1dd3..b3ba5a3 100644
--- a/src/display_list/from_snippet.rs
+++ b/src/display_list/from_snippet.rs
@@ -2,7 +2,55 @@
 use super::*;
 use crate::{formatter::get_term_style, snippet};
 
-fn format_label(label: Option<&str>, style: Option<DisplayTextStyle>) -> Vec<DisplayTextFragment> {
+struct CursorLines<'a>(&'a str);
+
+impl<'a> CursorLines<'a> {
+    fn new(src: &str) -> CursorLines<'_> {
+        CursorLines(src)
+    }
+}
+
+enum EndLine {
+    EOF = 0,
+    CRLF = 1,
+    LF = 2,
+}
+
+impl<'a> Iterator for CursorLines<'a> {
+    type Item = (&'a str, EndLine);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        if self.0.is_empty() {
+            None
+        } else {
+            self.0
+                .find('\n')
+                .map(|x| {
+                    let ret = if 0 < x {
+                        if self.0.as_bytes()[x - 1] == b'\r' {
+                            (&self.0[..x - 1], EndLine::LF)
+                        } else {
+                            (&self.0[..x], EndLine::CRLF)
+                        }
+                    } else {
+                        ("", EndLine::CRLF)
+                    };
+                    self.0 = &self.0[x + 1..];
+                    ret
+                })
+                .or_else(|| {
+                    let ret = Some((&self.0[..], EndLine::EOF));
+                    self.0 = "";
+                    ret
+                })
+        }
+    }
+}
+
+fn format_label(
+    label: Option<&str>,
+    style: Option<DisplayTextStyle>,
+) -> Vec<DisplayTextFragment<'_>> {
     let mut result = vec![];
     if let Some(label) = label {
         for (idx, element) in label.split("__").enumerate() {
@@ -17,7 +65,7 @@ fn format_label(label: Option<&str>, style: Option<DisplayTextStyle>) -> Vec<Dis
                 }
             };
             result.push(DisplayTextFragment {
-                content: element.to_string(),
+                content: element,
                 style: element_style,
             });
         }
@@ -25,7 +73,7 @@ fn format_label(label: Option<&str>, style: Option<DisplayTextStyle>) -> Vec<Dis
     result
 }
 
-fn format_title(annotation: snippet::Annotation) -> DisplayLine {
+fn format_title(annotation: snippet::Annotation<'_>) -> DisplayLine<'_> {
     let label = annotation.label.unwrap_or_default();
     DisplayLine::Raw(DisplayRawLine::Annotation {
         annotation: Annotation {
@@ -38,7 +86,7 @@ fn format_title(annotation: snippet::Annotation) -> DisplayLine {
     })
 }
 
-fn format_annotation(annotation: snippet::Annotation) -> Vec<DisplayLine> {
+fn format_annotation(annotation: snippet::Annotation<'_>) -> Vec<DisplayLine<'_>> {
     let mut result = vec![];
     let label = annotation.label.unwrap_or_default();
     for (i, line) in label.lines().enumerate() {
@@ -55,7 +103,11 @@ fn format_annotation(annotation: snippet::Annotation) -> Vec<DisplayLine> {
     result
 }
 
-fn format_slice(mut slice: snippet::Slice, is_first: bool, has_footer: bool) -> Vec<DisplayLine> {
+fn format_slice(
+    mut slice: snippet::Slice<'_>,
+    is_first: bool,
+    has_footer: bool,
+) -> Vec<DisplayLine<'_>> {
     let main_range = slice.annotations.get(0).map(|x| x.range.0);
     let row = slice.line_start;
     let origin = slice.origin.take();
@@ -70,13 +122,13 @@ fn format_slice(mut slice: snippet::Slice, is_first: bool, has_footer: bool) ->
     result
 }
 
-fn format_header(
-    origin: Option<String>,
+fn format_header<'a>(
+    origin: Option<&'a str>,
     main_range: Option<usize>,
     mut row: usize,
-    body: &[DisplayLine],
+    body: &[DisplayLine<'_>],
     is_first: bool,
-) -> Option<DisplayLine> {
+) -> Option<DisplayLine<'a>> {
     let display_header = if is_first {
         DisplayHeaderType::Initial
     } else {
@@ -117,17 +169,19 @@ fn format_header(
     None
 }
 
-fn fold_body(body: &[DisplayLine]) -> Vec<DisplayLine> {
-    let mut new_body = vec![];
+fn fold_body(mut body: Vec<DisplayLine<'_>>) -> Vec<DisplayLine<'_>> {
+    enum Line {
+        Fold(usize),
+        Source(usize),
+    };
 
+    let mut lines = vec![];
     let mut no_annotation_lines_counter = 0;
-    let mut idx = 0;
 
-    while idx < body.len() {
-        match body[idx] {
+    for (idx, line) in body.iter().enumerate() {
+        match line {
             DisplayLine::Source {
                 line: DisplaySourceLine::Annotation { .. },
-                ref inline_marks,
                 ..
             } => {
                 if no_annotation_lines_counter > 2 {
@@ -143,40 +197,71 @@ fn fold_body(body: &[DisplayLine]) -> Vec<DisplayLine> {
                     } else {
                         1
                     };
-                    for item in body.iter().take(fold_start + pre_len).skip(fold_start) {
-                        new_body.push(item.clone());
+                    for (i, _) in body
+                        .iter()
+                        .enumerate()
+                        .take(fold_start + pre_len)
+                        .skip(fold_start)
+                    {
+                        lines.push(Line::Source(i));
                     }
-                    new_body.push(DisplayLine::Fold {
-                        inline_marks: inline_marks.clone(),
-                    });
-                    for item in body.iter().take(fold_end).skip(fold_end - post_len) {
-                        new_body.push(item.clone());
+                    lines.push(Line::Fold(idx));
+                    for (i, _) in body
+                        .iter()
+                        .enumerate()
+                        .take(fold_end)
+                        .skip(fold_end - post_len)
+                    {
+                        lines.push(Line::Source(i));
                     }
                 } else {
                     let start = idx - no_annotation_lines_counter;
-                    for item in body.iter().take(idx).skip(start) {
-                        new_body.push(item.clone());
+                    for (i, _) in body.iter().enumerate().take(idx).skip(start) {
+                        lines.push(Line::Source(i));
                     }
                 }
                 no_annotation_lines_counter = 0;
             }
             DisplayLine::Source { .. } => {
                 no_annotation_lines_counter += 1;
-                idx += 1;
                 continue;
             }
             _ => {
                 no_annotation_lines_counter += 1;
             }
         }
-        new_body.push(body[idx].clone());
-        idx += 1;
+        lines.push(Line::Source(idx));
+    }
+
+    let mut new_body = vec![];
+    let mut removed = 0;
+    for line in lines {
+        match line {
+            Line::Source(i) => {
+                new_body.push(body.remove(i - removed));
+                removed += 1;
+            }
+            Line::Fold(i) => {
+                if let DisplayLine::Source {
+                    line: DisplaySourceLine::Annotation { .. },
+                    ref inline_marks,
+                    ..
+                } = body.get(i - removed).unwrap()
+                {
+                    new_body.push(DisplayLine::Fold {
+                        inline_marks: inline_marks.clone(),
+                    })
+                } else {
+                    unreachable!()
+                }
+            }
+        }
     }
 
     new_body
 }
 
-fn format_body(slice: snippet::Slice, has_footer: bool) -> Vec<DisplayLine> {
+fn format_body(slice: snippet::Slice<'_>, has_footer: bool) -> Vec<DisplayLine<'_>> {
     let source_len = slice.source.chars().count();
     if let Some(bigger) = slice.annotations.iter().find_map(|x| {
         if source_len < x.range.1 {
@@ -196,24 +281,20 @@ fn format_body(slice: snippet::Slice, has_footer: bool) -> Vec<DisplayLine> {
     let mut current_index = 0;
     let mut line_index_ranges = vec![];
 
-    let lines = slice.source.lines();
-    let lines_len = lines.clone().count();
-    for (i, line) in lines.enumerate() {
+    for (line, end_line) in CursorLines::new(slice.source) {
         let line_length = line.chars().count();
         let line_range = (current_index, current_index + line_length);
         body.push(DisplayLine::Source {
             lineno: Some(current_line),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: line.to_string(),
+                text: line,
                 range: line_range,
             },
         });
         line_index_ranges.push(line_range);
         current_line += 1;
-        if i + 1 < lines_len {
-            current_index += line_length + 1;
-        }
+        current_index += line_length + end_line as usize;
     }
 
     let mut annotation_line_count = 0;
@@ -361,7 +442,7 @@ fn format_body(slice: snippet::Slice, has_footer: bool) -> Vec<DisplayLine> {
     }
 
     if slice.fold {
-        body = fold_body(&body);
+        body = fold_body(body);
     }
 
     body.insert(
@@ -388,16 +469,15 @@ fn format_body(slice: snippet::Slice, has_footer: bool) -> Vec<DisplayLine> {
     body
 }
 
-// TODO: From reference to DisplayList<'a>
-impl From<snippet::Snippet> for DisplayList {
+impl<'a> From<snippet::Snippet<'a>> for DisplayList<'a> {
     fn from(
         snippet::Snippet {
             title,
             footer,
             slices,
             opt,
-        }: snippet::Snippet,
-    ) -> Self {
+        }: snippet::Snippet<'a>,
+    ) -> DisplayList<'a> {
         let mut body = vec![];
         if let Some(annotation) = title {
             body.push(format_title(annotation));
diff --git a/src/display_list/structs.rs b/src/display_list/structs.rs
index 07c52ae..8f6d8fc 100644
--- a/src/display_list/structs.rs
+++ b/src/display_list/structs.rs
@@ -3,14 +3,14 @@ use std::fmt;
 use crate::formatter::{get_term_style, style::Stylesheet};
 
 /// List of lines to be displayed.
-pub struct DisplayList {
-    pub body: Vec<DisplayLine>,
+pub struct DisplayList<'a> {
+    pub body: Vec<DisplayLine<'a>>,
     pub stylesheet: Box<dyn Stylesheet>,
     pub anonymized_line_numbers: bool,
 }
 
-impl From<Vec<DisplayLine>> for DisplayList {
-    fn from(body: Vec<DisplayLine>) -> Self {
+impl<'a> From<Vec<DisplayLine<'a>>> for DisplayList<'a> {
+    fn from(body: Vec<DisplayLine<'a>>) -> DisplayList<'a> {
         Self {
             body,
             anonymized_line_numbers: false,
@@ -19,13 +19,13 @@ impl From<Vec<DisplayLine>> for DisplayList {
     }
 }
 
-impl PartialEq for DisplayList {
+impl<'a> PartialEq for DisplayList<'a> {
     fn eq(&self, other: &Self) -> bool {
         self.body == other.body && self.anonymized_line_numbers == other.anonymized_line_numbers
     }
 }
 
-impl fmt::Debug for DisplayList {
+impl<'a> fmt::Debug for DisplayList<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         f.debug_struct("DisplayList")
             .field("body", &self.body)
@@ -41,42 +41,42 @@ pub struct FormatOptions {
 }
 
 /// Inline annotation which can be used in either Raw or Source line.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Annotation {
+#[derive(Debug, PartialEq)]
+pub struct Annotation<'a> {
     pub annotation_type: DisplayAnnotationType,
-    pub id: Option<String>,
-    pub label: Vec<DisplayTextFragment>,
+    pub id: Option<&'a str>,
+    pub label: Vec<DisplayTextFragment<'a>>,
 }
 
 /// A single line used in `DisplayList`.
-#[derive(Debug, Clone, PartialEq)]
-pub enum DisplayLine {
+#[derive(Debug, PartialEq)]
+pub enum DisplayLine<'a> {
     /// A line with `lineno` portion of the slice.
     Source {
         lineno: Option<usize>,
         inline_marks: Vec<DisplayMark>,
-        line: DisplaySourceLine,
+        line: DisplaySourceLine<'a>,
     },
 
     /// A line indicating a folded part of the slice.
     Fold { inline_marks: Vec<DisplayMark> },
 
     /// A line which is displayed outside of slices.
-    Raw(DisplayRawLine),
+    Raw(DisplayRawLine<'a>),
 }
 
 /// A source line.
-#[derive(Debug, Clone, PartialEq)]
-pub enum DisplaySourceLine {
+#[derive(Debug, PartialEq)]
+pub enum DisplaySourceLine<'a> {
     /// A line with the content of the Slice.
     Content {
-        text: String,
+        text: &'a str,
         range: (usize, usize), // meta information for annotation placement.
     },
 
     /// An annotation line which is displayed in context of the slice.
     Annotation {
-        annotation: Annotation,
+        annotation: Annotation<'a>,
         range: (usize, usize),
         annotation_type: DisplayAnnotationType,
         annotation_part: DisplayAnnotationPart,
@@ -88,19 +88,19 @@ pub enum DisplaySourceLine {
 
 /// Raw line - a line which does not have the `lineno` part and is not considered
 /// a part of the snippet.
-#[derive(Debug, Clone, PartialEq)]
-pub enum DisplayRawLine {
+#[derive(Debug, PartialEq)]
+pub enum DisplayRawLine<'a> {
     /// A line which provides information about the location of the given
     /// slice in the project structure.
     Origin {
-        path: String,
+        path: &'a str,
         pos: Option<(usize, usize)>,
         header_type: DisplayHeaderType,
     },
 
     /// An annotation line which is not part of any snippet.
     Annotation {
-        annotation: Annotation,
+        annotation: Annotation<'a>,
 
         /// If set to `true`, the annotation will be aligned to the
         /// lineno delimiter of the snippet.
@@ -114,9 +114,9 @@ pub enum DisplayRawLine {
 }
 
 /// An inline text fragment which any label is composed of.
-#[derive(Debug, Clone, PartialEq)]
-pub struct DisplayTextFragment {
-    pub content: String,
+#[derive(Debug, PartialEq)]
+pub struct DisplayTextFragment<'a> {
+    pub content: &'a str,
     pub style: DisplayTextStyle,
 }
 
diff --git a/src/formatter/mod.rs b/src/formatter/mod.rs
index f90d583..bf698a3 100644
--- a/src/formatter/mod.rs
+++ b/src/formatter/mod.rs
@@ -19,7 +19,7 @@ fn format_repeat_char(c: char, n: usize, f: &mut fmt::Formatter<'_>) -> fmt::Res
 }
 
 #[inline]
-fn is_annotation_empty(annotation: &Annotation) -> bool {
+fn is_annotation_empty(annotation: &Annotation<'_>) -> bool {
     annotation
         .label
         .iter()
@@ -42,7 +42,7 @@ pub fn get_term_style(_color: bool) -> Box<dyn Stylesheet> {
     Box::new(NoColorStylesheet)
 }
 
-impl fmt::Display for DisplayList {
+impl<'a> fmt::Display for DisplayList<'a> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         let lineno_width = self.body.iter().fold(0, |max, line| match line {
             DisplayLine::Source {
@@ -76,7 +76,7 @@ impl fmt::Display for DisplayList {
     }
 }
 
-impl DisplayList {
+impl<'a> DisplayList<'a> {
     const ANONYMIZED_LINE_NUM: &'static str = "LL";
     const ERROR_TXT: &'static str = "error";
     const HELP_TXT: &'static str = "help";
@@ -123,7 +123,7 @@ impl DisplayList {
 
     fn format_label(
         &self,
-        label: &[DisplayTextFragment],
+        label: &[DisplayTextFragment<'_>],
         f: &mut fmt::Formatter<'_>,
     ) -> fmt::Result {
         let emphasis_style = self.stylesheet.get_style(StyleClass::Emphasis);
@@ -139,7 +139,7 @@ impl DisplayList {
 
     fn format_annotation(
         &self,
-        annotation: &Annotation,
+        annotation: &Annotation<'_>,
         continuation: bool,
         in_source: bool,
         f: &mut fmt::Formatter<'_>,
@@ -191,7 +191,7 @@ impl DisplayList {
     #[inline]
     fn format_source_line(
         &self,
-        line: &DisplaySourceLine,
+        line: &DisplaySourceLine<'_>,
         f: &mut fmt::Formatter<'_>,
     ) -> fmt::Result {
         match line {
@@ -259,7 +259,7 @@ impl DisplayList {
     #[inline]
     fn format_raw_line(
         &self,
-        line: &DisplayRawLine,
+        line: &DisplayRawLine<'_>,
         lineno_width: usize,
         f: &mut fmt::Formatter<'_>,
     ) -> fmt::Result {
@@ -318,7 +318,7 @@ impl DisplayList {
     #[inline]
     fn format_line(
         &self,
-        dl: &DisplayLine,
+        dl: &DisplayLine<'_>,
         lineno_width: usize,
         inline_marks_width: usize,
         f: &mut fmt::Formatter<'_>,
diff --git a/src/snippet.rs b/src/snippet.rs
index 0a703b2..bc7ba00 100644
--- a/src/snippet.rs
+++ b/src/snippet.rs
@@ -7,23 +7,23 @@
 //!
 //! Snippet {
 //!     title: Some(Annotation {
-//!         label: Some("mismatched types".to_string()),
+//!         label: Some("mismatched types"),
 //!         id: None,
 //!         annotation_type: AnnotationType::Error,
 //!     }),
 //!     footer: vec![],
 //!     slices: vec![
 //!         Slice {
-//!             source: "Foo".to_string(),
+//!             source: "Foo",
 //!             line_start: 51,
-//!             origin: Some("src/format.rs".to_string()),
+//!             origin: Some("src/format.rs"),
 //!             fold: false,
 //!             annotations: vec![],
 //!         },
 //!         Slice {
-//!             source: "Faa".to_string(),
+//!             source: "Faa",
 //!             line_start: 129,
-//!             origin: Some("src/display.rs".to_string()),
+//!             origin: Some("src/display.rs"),
 //!             fold: false,
 //!             annotations: vec![],
 //!         },
@@ -34,11 +34,11 @@
 use crate::display_list::FormatOptions;
 
 /// Primary structure provided for formatting
-#[derive(Debug, Default, Clone)]
-pub struct Snippet {
-    pub title: Option<Annotation>,
-    pub footer: Vec<Annotation>,
-    pub slices: Vec<Slice>,
+#[derive(Debug, Default)]
+pub struct Snippet<'a> {
+    pub title: Option<Annotation<'a>>,
+    pub footer: Vec<Annotation<'a>>,
+    pub slices: Vec<Slice<'a>>,
     pub opt: FormatOptions,
 }
 
@@ -47,12 +47,12 @@ pub struct Snippet {
 ///
 /// One `Slice` is meant to represent a single, continuous,
 /// slice of source code that you want to annotate.
-#[derive(Debug, Clone)]
-pub struct Slice {
-    pub source: String,
+#[derive(Debug)]
+pub struct Slice<'a> {
+    pub source: &'a str,
     pub line_start: usize,
-    pub origin: Option<String>,
-    pub annotations: Vec<SourceAnnotation>,
+    pub origin: Option<&'a str>,
+    pub annotations: Vec<SourceAnnotation<'a>>,
     /// If set explicitly to `true`, the snippet will fold
     /// parts of the slice that don't contain any annotations.
     pub fold: bool,
@@ -71,18 +71,18 @@ pub enum AnnotationType {
 }
 
 /// An annotation for a `Slice`.
-#[derive(Debug, Clone)]
-pub struct SourceAnnotation {
+#[derive(Debug)]
+pub struct SourceAnnotation<'a> {
     pub range: (usize, usize),
-    pub label: String,
+    pub label: &'a str,
     pub annotation_type: AnnotationType,
 }
 
 /// An annotation for a `Snippet`.
-#[derive(Debug, Clone)]
-pub struct Annotation {
+#[derive(Debug)]
+pub struct Annotation<'a> {
     /// Identifier of the annotation. Usually error code like "E0308".
-    pub id: Option<String>,
-    pub label: Option<String>,
+    pub id: Option<&'a str>,
+    pub label: Option<&'a str>,
     pub annotation_type: AnnotationType,
 }


Test patch:
diff --git a/tests/dl_from_snippet.rs b/tests/dl_from_snippet.rs
index 993d336..2ed9902 100644
--- a/tests/dl_from_snippet.rs
+++ b/tests/dl_from_snippet.rs
@@ -1,11 +1,12 @@
+use annotate_snippets::display_list::DisplayList;
 use annotate_snippets::{display_list as dl, formatter::get_term_style, snippet};
 
 #[test]
 fn test_format_title() {
     let input = snippet::Snippet {
         title: Some(snippet::Annotation {
-            id: Some("E0001".to_string()),
-            label: Some("This is a title".to_string()),
+            id: Some("E0001"),
+            label: Some("This is a title"),
             annotation_type: snippet::AnnotationType::Error,
         }),
         footer: vec![],
@@ -16,9 +17,9 @@ fn test_format_title() {
         body: vec![dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
             annotation: dl::Annotation {
                 annotation_type: dl::DisplayAnnotationType::Error,
-                id: Some("E0001".to_string()),
+                id: Some("E0001"),
                 label: vec![dl::DisplayTextFragment {
-                    content: "This is a title".to_string(),
+                    content: "This is a title",
                     style: dl::DisplayTextStyle::Emphasis,
                 }],
             },
@@ -33,14 +34,14 @@ fn test_format_title() {
 
 #[test]
 fn test_format_slice() {
-    let line_1 = "This is line 1".to_string();
-    let line_2 = "This is line 2".to_string();
-    let source = vec![line_1.clone(), line_2.clone()].join("\n");
+    let line_1 = "This is line 1";
+    let line_2 = "This is line 2";
+    let source = vec![line_1, line_2].join("\n");
     let input = snippet::Snippet {
         title: None,
         footer: vec![],
         slices: vec![snippet::Slice {
-            source: source.clone(),
+            source: &source,
             line_start: 5402,
             origin: None,
             annotations: vec![],
@@ -59,7 +60,7 @@ fn test_format_slice() {
                 lineno: Some(5402),
                 inline_marks: vec![],
                 line: dl::DisplaySourceLine::Content {
-                    text: line_1.clone(),
+                    text: line_1,
                     range: (0, line_1.len()),
                 },
             },
@@ -85,25 +86,25 @@ fn test_format_slice() {
 
 #[test]
 fn test_format_slices_continuation() {
-    let src_0 = "This is slice 1".to_string();
+    let src_0 = "This is slice 1";
     let src_0_len = src_0.len();
-    let src_1 = "This is slice 2".to_string();
+    let src_1 = "This is slice 2";
     let src_1_len = src_1.len();
     let input = snippet::Snippet {
         title: None,
         footer: vec![],
         slices: vec![
             snippet::Slice {
-                source: src_0.clone(),
+                source: src_0,
                 line_start: 5402,
-                origin: Some("file1.rs".to_string()),
+                origin: Some("file1.rs"),
                 annotations: vec![],
                 fold: false,
             },
             snippet::Slice {
-                source: src_1.clone(),
+                source: src_1,
                 line_start: 2,
-                origin: Some("file2.rs".to_string()),
+                origin: Some("file2.rs"),
                 annotations: vec![],
                 fold: false,
             },
@@ -113,7 +114,7 @@ fn test_format_slices_continuation() {
     let output = dl::DisplayList {
         body: vec![
             dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
-                path: "file1.rs".to_string(),
+                path: "file1.rs",
                 pos: None,
                 header_type: dl::DisplayHeaderType::Initial,
             }),
@@ -136,7 +137,7 @@ fn test_format_slices_continuation() {
                 line: dl::DisplaySourceLine::Empty,
             },
             dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
-                path: "file2.rs".to_string(),
+                path: "file2.rs",
                 pos: None,
                 header_type: dl::DisplayHeaderType::Continuation,
             }),
@@ -167,21 +168,21 @@ fn test_format_slices_continuation() {
 
 #[test]
 fn test_format_slice_annotation_standalone() {
-    let line_1 = "This is line 1".to_string();
-    let line_2 = "This is line 2".to_string();
-    let source = vec![line_1.clone(), line_2.clone()].join("\n");
+    let line_1 = "This is line 1";
+    let line_2 = "This is line 2";
+    let source = vec![line_1, line_2].join("\n");
     // In line 2
     let range = (22, 24);
     let input = snippet::Snippet {
         title: None,
         footer: vec![],
         slices: vec![snippet::Slice {
-            source: source.clone(),
+            source: &source,
             line_start: 5402,
             origin: None,
             annotations: vec![snippet::SourceAnnotation {
                 range,
-                label: "Test annotation".to_string(),
+                label: "Test annotation",
                 annotation_type: snippet::AnnotationType::Info,
             }],
             fold: false,
@@ -200,7 +201,7 @@ fn test_format_slice_annotation_standalone() {
                 inline_marks: vec![],
                 line: dl::DisplaySourceLine::Content {
                     range: (0, line_1.len()),
-                    text: line_1.clone(),
+                    text: line_1,
                 },
             },
             dl::DisplayLine::Source {
@@ -219,7 +220,7 @@ fn test_format_slice_annotation_standalone() {
                         annotation_type: dl::DisplayAnnotationType::Info,
                         id: None,
                         label: vec![dl::DisplayTextFragment {
-                            content: "Test annotation".to_string(),
+                            content: "Test annotation",
                             style: dl::DisplayTextStyle::Regular,
                         }],
                     },
@@ -246,7 +247,7 @@ fn test_format_label() {
         title: None,
         footer: vec![snippet::Annotation {
             id: None,
-            label: Some("This __is__ a title".to_string()),
+            label: Some("This __is__ a title"),
             annotation_type: snippet::AnnotationType::Error,
         }],
         slices: vec![],
@@ -259,15 +260,15 @@ fn test_format_label() {
                 id: None,
                 label: vec![
                     dl::DisplayTextFragment {
-                        content: "This ".to_string(),
+                        content: "This ",
                         style: dl::DisplayTextStyle::Regular,
                     },
                     dl::DisplayTextFragment {
-                        content: "is".to_string(),
+                        content: "is",
                         style: dl::DisplayTextStyle::Emphasis,
                     },
                     dl::DisplayTextFragment {
-                        content: " a title".to_string(),
+                        content: " a title",
                         style: dl::DisplayTextStyle::Regular,
                     },
                 ],
@@ -284,8 +285,8 @@ fn test_format_label() {
 #[test]
 #[should_panic]
 fn test_i26() {
-    let source = "short".to_string();
-    let label = "label".to_string();
+    let source = "short";
+    let label = "label";
     let input = snippet::Snippet {
         title: None,
         footer: vec![],
@@ -305,3 +306,96 @@ fn test_i26() {
 
     let _ = dl::DisplayList::from(input);
 }
+
+#[test]
+fn test_i_29() {
+    let snippets = snippet::Snippet {
+        title: Some(snippet::Annotation {
+            id: None,
+            label: Some("oops"),
+            annotation_type: snippet::AnnotationType::Error,
+        }),
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            source: "First line\r\nSecond oops line",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (19, 23),
+                label: "oops",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: true,
+        }],
+        opt: Default::default(),
+    };
+
+    let expected = DisplayList {
+        body: vec![
+            dl::DisplayLine::Raw(dl::DisplayRawLine::Annotation {
+                annotation: dl::Annotation {
+                    annotation_type: dl::DisplayAnnotationType::Error,
+                    id: None,
+                    label: vec![dl::DisplayTextFragment {
+                        content: "oops",
+                        style: dl::DisplayTextStyle::Emphasis,
+                    }],
+                },
+                source_aligned: false,
+                continuation: false,
+            }),
+            dl::DisplayLine::Raw(dl::DisplayRawLine::Origin {
+                path: "<current file>",
+                pos: Some((2, 8)),
+                header_type: dl::DisplayHeaderType::Initial,
+            }),
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(1),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: "First line",
+                    range: (0, 10),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: Some(2),
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Content {
+                    text: "Second oops line",
+                    range: (12, 28),
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Annotation {
+                    annotation: dl::Annotation {
+                        annotation_type: dl::DisplayAnnotationType::None,
+                        id: None,
+                        label: vec![dl::DisplayTextFragment {
+                            content: "oops",
+                            style: dl::DisplayTextStyle::Regular,
+                        }],
+                    },
+                    range: (7, 11),
+                    annotation_type: dl::DisplayAnnotationType::Error,
+                    annotation_part: dl::DisplayAnnotationPart::Standalone,
+                },
+            },
+            dl::DisplayLine::Source {
+                lineno: None,
+                inline_marks: vec![],
+                line: dl::DisplaySourceLine::Empty,
+            },
+        ],
+        stylesheet: get_term_style(false),
+        anonymized_line_numbers: false,
+    };
+
+    assert_eq!(DisplayList::from(snippets), expected);
+}
diff --git a/tests/fixtures/no-color/multiline_annotation.toml b/tests/fixtures/no-color/multiline_annotation.toml
new file mode 100644
index 0000000..bdb577f
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation.toml
@@ -0,0 +1,40 @@
+[[slices]]
+source = """
+) -> Option<String> {
+    for ann in annotations {
+        match (ann.range.0, ann.range.1) {
+            (None, None) => continue,
+            (Some(start), Some(end)) if start > end_index || end < start_index => continue,
+            (Some(start), Some(end)) if start >= start_index && end <= end_index => {
+                let label = if let Some(ref label) = ann.label {
+                    format!(" {}", label)
+                } else {
+                    String::from("")
+                };
+
+                return Some(format!(
+                    "{}{}{}",
+                    " ".repeat(start - start_index),
+                    "^".repeat(end - start),
+                    label
+                ));
+            }
+            _ => continue,
+        }
+    }
+"""
+line_start = 51
+origin = "src/format.rs"
+fold = true
+[[slices.annotations]]
+label = "expected `std::option::Option<std::string::String>` because of return type"
+annotation_type = "Warning"
+range = [5, 19]
+[[slices.annotations]]
+label = "expected enum `std::option::Option`, found ()"
+annotation_type = "Error"
+range = [22, 765]
+[title]
+label = "mismatched types"
+id = "E0308"
+annotation_type =  "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation.yaml b/tests/fixtures/no-color/multiline_annotation.yaml
deleted file mode 100644
index 0622764..0000000
--- a/tests/fixtures/no-color/multiline_annotation.yaml
+++ /dev/null
@@ -1,38 +0,0 @@
-slices:
-  - source: |-
-      ) -> Option<String> {
-          for ann in annotations {
-              match (ann.range.0, ann.range.1) {
-                  (None, None) => continue,
-                  (Some(start), Some(end)) if start > end_index || end < start_index => continue,
-                  (Some(start), Some(end)) if start >= start_index && end <= end_index => {
-                      let label = if let Some(ref label) = ann.label {
-                          format!(" {}", label)
-                      } else {
-                          String::from("")
-                      };
-
-                      return Some(format!(
-                          "{}{}{}",
-                          " ".repeat(start - start_index),
-                          "^".repeat(end - start),
-                          label
-                      ));
-                  }
-                  _ => continue,
-              }
-          }
-    line_start: 51
-    origin: "src/format.rs"
-    fold: true
-    annotations:
-      - label: expected `std::option::Option<std::string::String>` because of return type
-        annotation_type: Warning
-        range: [5, 19]
-      - label: expected enum `std::option::Option`, found ()
-        annotation_type: Error
-        range: [22, 765]
-title:
-  label: mismatched types
-  id: E0308
-  annotation_type: Error
diff --git a/tests/fixtures/no-color/multiline_annotation2.toml b/tests/fixtures/no-color/multiline_annotation2.toml
new file mode 100644
index 0000000..6ec0b1f
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation2.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+                        if let DisplayLine::Source {
+                            ref mut inline_marks,
+                        } = body[body_idx]
+"""
+line_start = 139
+origin = "src/display_list.rs"
+fold = false
+[[slices.annotations]]
+label = "missing fields `lineno`, `content`"
+annotation_type = "Error"
+range = [31, 127]
+
+[title]
+label = "pattern does not mention fields `lineno`, `content`"
+id = "E0027"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation2.yaml b/tests/fixtures/no-color/multiline_annotation2.yaml
deleted file mode 100644
index c140ffe..0000000
--- a/tests/fixtures/no-color/multiline_annotation2.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-slices:
-  - source: |1
-                             if let DisplayLine::Source {
-                                 ref mut inline_marks,
-                             } = body[body_idx]
-    line_start: 139
-    origin: "src/display_list.rs"
-    fold: false
-    annotations:
-      - label: missing fields `lineno`, `content`
-        annotation_type: Error
-        range: [31, 127]
-title:
-  label: pattern does not mention fields `lineno`, `content`
-  id: E0027
-  annotation_type: Error
diff --git a/tests/fixtures/no-color/multiline_annotation3.toml b/tests/fixtures/no-color/multiline_annotation3.toml
new file mode 100644
index 0000000..21bbcd8
--- /dev/null
+++ b/tests/fixtures/no-color/multiline_annotation3.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+This is an exampl
+e of an edge case of an annotation overflowing
+to exactly one character on next line.
+"""
+line_start = 26
+origin = "foo.txt"
+fold = false
+[[slices.annotations]]
+label = "this should not be on separate lines"
+annotation_type = "Error"
+range = [11, 18]
+
+[title]
+label = "spacing error found"
+id = "E####"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/multiline_annotation3.yaml b/tests/fixtures/no-color/multiline_annotation3.yaml
deleted file mode 100644
index a807512..0000000
--- a/tests/fixtures/no-color/multiline_annotation3.yaml
+++ /dev/null
@@ -1,16 +0,0 @@
-slices:
-  - source: |-
-      This is an exampl
-      e of an edge case of an annotation overflowing
-      to exactly one character on next line.
-    line_start: 26
-    origin: foo.txt
-    fold: false
-    annotations:
-      - label: this should not be on separate lines
-        annotation_type: Error
-        range: [11, 18]
-title:
-  label: spacing error found
-  id: E####
-  annotation_type: Error
diff --git a/tests/fixtures/no-color/multiple_annotations.toml b/tests/fixtures/no-color/multiple_annotations.toml
new file mode 100644
index 0000000..84efc5f
--- /dev/null
+++ b/tests/fixtures/no-color/multiple_annotations.toml
@@ -0,0 +1,25 @@
+[[slices]]
+source = """
+fn add_title_line(result: &mut Vec<String>, main_annotation: Option<&Annotation>) {
+    if let Some(annotation) = main_annotation {
+        result.push(format_title_line(
+            &annotation.annotation_type,
+            None,
+            &annotation.label,
+        ));
+    }
+}
+"""
+line_start = 96
+[[slices.annotations]]
+label = "Variable defined here"
+annotation_type = "Error"
+range = [100, 110]
+[[slices.annotations]]
+label = "Referenced here"
+annotation_type = "Error"
+range = [184, 194]
+[[slices.annotations]]
+label = "Referenced again here"
+annotation_type = "Error"
+range = [243, 253]
diff --git a/tests/fixtures/no-color/multiple_annotations.yaml b/tests/fixtures/no-color/multiple_annotations.yaml
deleted file mode 100644
index 4ea7f9c..0000000
--- a/tests/fixtures/no-color/multiple_annotations.yaml
+++ /dev/null
@@ -1,23 +0,0 @@
-slices:
-  - source: |-
-      fn add_title_line(result: &mut Vec<String>, main_annotation: Option<&Annotation>) {
-          if let Some(annotation) = main_annotation {
-              result.push(format_title_line(
-                  &annotation.annotation_type,
-                  None,
-                  &annotation.label,
-              ));
-          }
-      }
-    line_start: 96
-    annotations:
-      - label: Variable defined here
-        annotation_type: Error
-        range: [100, 110]
-      - label: Referenced here
-        annotation_type: Error
-        range: [184, 194]
-      - label: Referenced again here
-        annotation_type: Error
-        range: [243, 253]
-title: null
diff --git a/tests/fixtures/no-color/simple.toml b/tests/fixtures/no-color/simple.toml
new file mode 100644
index 0000000..6c38674
--- /dev/null
+++ b/tests/fixtures/no-color/simple.toml
@@ -0,0 +1,18 @@
+[[slices]]
+source = """
+        })
+
+        for line in &self.body {"""
+line_start = 169
+origin = "src/format_color.rs"
+[[slices.annotations]]
+label = "unexpected token"
+annotation_type = "Error"
+range = [20, 23]
+[[slices.annotations]]
+label = "expected one of `.`, `;`, `?`, or an operator here"
+annotation_type = "Warning"
+range = [10, 11]
+[title]
+label = "expected one of `.`, `;`, `?`, or an operator, found `for`"
+annotation_type = "Error"
diff --git a/tests/fixtures/no-color/simple.yaml b/tests/fixtures/no-color/simple.yaml
deleted file mode 100644
index 697789e..0000000
--- a/tests/fixtures/no-color/simple.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-slices:
-  - source: |1
-             })
-
-             for line in &self.body {
-    line_start: 169
-    origin: src/format_color.rs
-    annotations:
-      - label: unexpected token
-        annotation_type: Error
-        range: [20, 23]
-      - label: expected one of `.`, `;`, `?`, or an operator here
-        annotation_type: Warning
-        range: [10, 11]
-title:
-  label: expected one of `.`, `;`, `?`, or an operator, found `for`
-  annotation_type: Error
diff --git a/tests/fixtures_test.rs b/tests/fixtures_test.rs
index 905b18f..e471521 100644
--- a/tests/fixtures_test.rs
+++ b/tests/fixtures_test.rs
@@ -4,8 +4,7 @@ mod snippet;
 use crate::snippet::SnippetDef;
 use annotate_snippets::{display_list::DisplayList, snippet::Snippet};
 use glob::glob;
-use serde::Deserialize;
-use std::{error::Error, fs::File, io, io::prelude::*, path::Path};
+use std::{error::Error, fs::File, io, io::prelude::*};
 
 fn read_file(path: &str) -> Result<String, io::Error> {
     let mut f = File::open(path)?;
@@ -14,24 +13,20 @@ fn read_file(path: &str) -> Result<String, io::Error> {
     Ok(s.trim_end().to_string())
 }
 
-fn read_fixture<P: AsRef<Path>>(path: P) -> Result<Snippet, Box<dyn Error>> {
-    #[derive(Deserialize)]
-    struct Wrapper(#[serde(with = "SnippetDef")] Snippet);
-
-    let file = File::open(path)?;
-    let u = serde_yaml::from_reader(file).map(|Wrapper(a)| a)?;
-    Ok(u)
+fn read_fixture<'de>(src: &'de str) -> Result<Snippet<'de>, Box<dyn Error>> {
+    Ok(toml::from_str(src).map(|a: SnippetDef| a.into())?)
 }
 
 #[test]
 fn test_fixtures() {
-    for entry in glob("./tests/fixtures/no-color/**/*.yaml").expect("Failed to read glob pattern") {
+    for entry in glob("./tests/fixtures/no-color/**/*.toml").expect("Failed to read glob pattern") {
         let p = entry.expect("Error while getting an entry");
-        let path_in = p.to_str().expect("Can't print path");
 
-        let path_out = path_in.replace(".yaml", ".txt");
+        let path_in = p.to_str().expect("Can't print path");
+        let path_out = path_in.replace(".toml", ".txt");
 
-        let snippet = read_fixture(path_in).expect("Failed to read file");
+        let src = read_file(&path_in).expect("Failed to read file");
+        let snippet = read_fixture(&src).expect("Failed to read file");
         let expected_out = read_file(&path_out).expect("Failed to read file");
 
         let dl = DisplayList::from(snippet);
diff --git a/tests/formatter.rs b/tests/formatter.rs
index f0a6bae..5c7211d 100644
--- a/tests/formatter.rs
+++ b/tests/formatter.rs
@@ -1,4 +1,5 @@
 use annotate_snippets::display_list::*;
+use annotate_snippets::snippet::{self, Snippet};
 
 #[test]
 fn test_source_empty() {
@@ -18,7 +19,7 @@ fn test_source_content() {
             lineno: Some(56),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "This is an example".to_string(),
+                text: "This is an example",
                 range: (0, 19),
             },
         },
@@ -26,7 +27,7 @@ fn test_source_content() {
             lineno: Some(57),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "of content lines".to_string(),
+                text: "of content lines",
                 range: (0, 19),
             },
         },
@@ -49,7 +50,7 @@ fn test_source_annotation_standalone_singleline() {
                 annotation_type: DisplayAnnotationType::None,
                 id: None,
                 label: vec![DisplayTextFragment {
-                    content: String::from("Example string"),
+                    content: "Example string",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -73,7 +74,7 @@ fn test_source_annotation_standalone_multiline() {
                     annotation_type: DisplayAnnotationType::Help,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("Example string"),
+                        content: "Example string",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -90,7 +91,7 @@ fn test_source_annotation_standalone_multiline() {
                     annotation_type: DisplayAnnotationType::Help,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("Second line"),
+                        content: "Second line",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -118,7 +119,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::Info,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("Example string"),
+                        content: "Example string",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -135,7 +136,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::Info,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("Second line"),
+                        content: "Second line",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -152,7 +153,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::Warning,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("This is a note"),
+                        content: "This is a note",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -169,7 +170,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::Warning,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("Second line of the warning"),
+                        content: "Second line of the warning",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -186,7 +187,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::Info,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("This is an info"),
+                        content: "This is an info",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -203,7 +204,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::Help,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("This is help"),
+                        content: "This is help",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -220,7 +221,7 @@ fn test_source_annotation_standalone_multi_annotation() {
                     annotation_type: DisplayAnnotationType::None,
                     id: None,
                     label: vec![DisplayTextFragment {
-                        content: String::from("This is an annotation of type none"),
+                        content: "This is an annotation of type none",
                         style: DisplayTextStyle::Regular,
                     }],
                 },
@@ -240,7 +241,7 @@ fn test_fold_line() {
             lineno: Some(5),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "This is line 5".to_string(),
+                text: "This is line 5",
                 range: (0, 19),
             },
         },
@@ -251,7 +252,7 @@ fn test_fold_line() {
             lineno: Some(10021),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "... and now we're at line 10021".to_string(),
+                text: "... and now we're at line 10021",
                 range: (0, 19),
             },
         },
@@ -266,7 +267,7 @@ fn test_fold_line() {
 #[test]
 fn test_raw_origin_initial_nopos() {
     let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
-        path: "src/test.rs".to_string(),
+        path: "src/test.rs",
         pos: None,
         header_type: DisplayHeaderType::Initial,
     })]);
@@ -277,7 +278,7 @@ fn test_raw_origin_initial_nopos() {
 #[test]
 fn test_raw_origin_initial_pos() {
     let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
-        path: "src/test.rs".to_string(),
+        path: "src/test.rs",
         pos: Some((23, 15)),
         header_type: DisplayHeaderType::Initial,
     })]);
@@ -288,7 +289,7 @@ fn test_raw_origin_initial_pos() {
 #[test]
 fn test_raw_origin_continuation() {
     let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
-        path: "src/test.rs".to_string(),
+        path: "src/test.rs",
         pos: Some((23, 15)),
         header_type: DisplayHeaderType::Continuation,
     })]);
@@ -301,9 +302,9 @@ fn test_raw_annotation_unaligned() {
     let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
         annotation: Annotation {
             annotation_type: DisplayAnnotationType::Error,
-            id: Some("E0001".to_string()),
+            id: Some("E0001"),
             label: vec![DisplayTextFragment {
-                content: String::from("This is an error"),
+                content: "This is an error",
                 style: DisplayTextStyle::Regular,
             }],
         },
@@ -320,9 +321,9 @@ fn test_raw_annotation_unaligned_multiline() {
         DisplayLine::Raw(DisplayRawLine::Annotation {
             annotation: Annotation {
                 annotation_type: DisplayAnnotationType::Warning,
-                id: Some("E0001".to_string()),
+                id: Some("E0001"),
                 label: vec![DisplayTextFragment {
-                    content: String::from("This is an error"),
+                    content: "This is an error",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -332,9 +333,9 @@ fn test_raw_annotation_unaligned_multiline() {
         DisplayLine::Raw(DisplayRawLine::Annotation {
             annotation: Annotation {
                 annotation_type: DisplayAnnotationType::Warning,
-                id: Some("E0001".to_string()),
+                id: Some("E0001"),
                 label: vec![DisplayTextFragment {
-                    content: String::from("Second line of the error"),
+                    content: "Second line of the error",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -354,9 +355,9 @@ fn test_raw_annotation_aligned() {
     let dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Annotation {
         annotation: Annotation {
             annotation_type: DisplayAnnotationType::Error,
-            id: Some("E0001".to_string()),
+            id: Some("E0001"),
             label: vec![DisplayTextFragment {
-                content: String::from("This is an error"),
+                content: "This is an error",
                 style: DisplayTextStyle::Regular,
             }],
         },
@@ -373,9 +374,9 @@ fn test_raw_annotation_aligned_multiline() {
         DisplayLine::Raw(DisplayRawLine::Annotation {
             annotation: Annotation {
                 annotation_type: DisplayAnnotationType::Warning,
-                id: Some("E0001".to_string()),
+                id: Some("E0001"),
                 label: vec![DisplayTextFragment {
-                    content: String::from("This is an error"),
+                    content: "This is an error",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -385,9 +386,9 @@ fn test_raw_annotation_aligned_multiline() {
         DisplayLine::Raw(DisplayRawLine::Annotation {
             annotation: Annotation {
                 annotation_type: DisplayAnnotationType::Warning,
-                id: Some("E0001".to_string()),
+                id: Some("E0001"),
                 label: vec![DisplayTextFragment {
-                    content: String::from("Second line of the error"),
+                    content: "Second line of the error",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -410,7 +411,7 @@ fn test_different_annotation_types() {
                 annotation_type: DisplayAnnotationType::Note,
                 id: None,
                 label: vec![DisplayTextFragment {
-                    content: String::from("This is a note"),
+                    content: "This is a note",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -422,7 +423,7 @@ fn test_different_annotation_types() {
                 annotation_type: DisplayAnnotationType::None,
                 id: None,
                 label: vec![DisplayTextFragment {
-                    content: String::from("This is just a string"),
+                    content: "This is just a string",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -434,7 +435,7 @@ fn test_different_annotation_types() {
                 annotation_type: DisplayAnnotationType::None,
                 id: None,
                 label: vec![DisplayTextFragment {
-                    content: String::from("Second line of none type annotation"),
+                    content: "Second line of none type annotation",
                     style: DisplayTextStyle::Regular,
                 }],
             },
@@ -470,7 +471,7 @@ fn test_anon_lines() {
             lineno: Some(56),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "This is an example".to_string(),
+                text: "This is an example",
                 range: (0, 19),
             },
         },
@@ -478,7 +479,7 @@ fn test_anon_lines() {
             lineno: Some(57),
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "of content lines".to_string(),
+                text: "of content lines",
                 range: (0, 19),
             },
         },
@@ -491,7 +492,7 @@ fn test_anon_lines() {
             lineno: None,
             inline_marks: vec![],
             line: DisplaySourceLine::Content {
-                text: "abc".to_string(),
+                text: "abc",
                 range: (0, 19),
             },
         },
@@ -507,7 +508,7 @@ fn test_anon_lines() {
 #[test]
 fn test_raw_origin_initial_pos_anon_lines() {
     let mut dl = DisplayList::from(vec![DisplayLine::Raw(DisplayRawLine::Origin {
-        path: "src/test.rs".to_string(),
+        path: "src/test.rs",
         pos: Some((23, 15)),
         header_type: DisplayHeaderType::Initial,
     })]);
@@ -516,3 +517,36 @@ fn test_raw_origin_initial_pos_anon_lines() {
     dl.anonymized_line_numbers = true;
     assert_eq!(dl.to_string(), "--> src/test.rs:23:15");
 }
+
+#[test]
+fn test_i_29() {
+    let snippets = Snippet {
+        title: Some(snippet::Annotation {
+            id: None,
+            label: Some("oops"),
+            annotation_type: snippet::AnnotationType::Error,
+        }),
+        footer: vec![],
+        slices: vec![snippet::Slice {
+            source: "First line\r\nSecond oops line",
+            line_start: 1,
+            origin: Some("<current file>"),
+            annotations: vec![snippet::SourceAnnotation {
+                range: (19, 23),
+                label: "oops",
+                annotation_type: snippet::AnnotationType::Error,
+            }],
+            fold: true,
+        }],
+        opt: Default::default(),
+    };
+    let expected = r#"error: oops
+ --> <current file>:2:8
+  |
+1 | First line
+2 | Second oops line
+  |        ^^^^ oops
+  |"#;
+
+    assert_eq!(DisplayList::from(snippets).to_string(), expected);
+}
diff --git a/tests/snippet/mod.rs b/tests/snippet/mod.rs
index 258c16f..c334874 100644
--- a/tests/snippet/mod.rs
+++ b/tests/snippet/mod.rs
@@ -6,19 +6,38 @@ use annotate_snippets::{
 };
 
 #[derive(Deserialize)]
-#[serde(remote = "Snippet")]
-pub struct SnippetDef {
+pub struct SnippetDef<'a> {
     #[serde(deserialize_with = "deserialize_annotation")]
     #[serde(default)]
-    pub title: Option<Annotation>,
+    #[serde(borrow)]
+    pub title: Option<Annotation<'a>>,
     #[serde(deserialize_with = "deserialize_annotations")]
     #[serde(default)]
-    pub footer: Vec<Annotation>,
+    #[serde(borrow)]
+    pub footer: Vec<Annotation<'a>>,
     #[serde(deserialize_with = "deserialize_opt")]
     #[serde(default)]
     pub opt: FormatOptions,
     #[serde(deserialize_with = "deserialize_slices")]
-    pub slices: Vec<Slice>,
+    #[serde(borrow)]
+    pub slices: Vec<Slice<'a>>,
+}
+
+impl<'a> Into<Snippet<'a>> for SnippetDef<'a> {
+    fn into(self) -> Snippet<'a> {
+        let SnippetDef {
+            title,
+            footer,
+            opt,
+            slices,
+        } = self;
+        Snippet {
+            title,
+            footer,
+            slices,
+            opt,
+        }
+    }
 }
 
 fn deserialize_opt<'de, D>(deserializer: D) -> Result<FormatOptions, D::Error>
@@ -38,34 +57,46 @@ pub struct FormatOptionsDef {
     pub anonymized_line_numbers: bool,
 }
 
-fn deserialize_slices<'de, D>(deserializer: D) -> Result<Vec<Slice>, D::Error>
+fn deserialize_slices<'de, D>(deserializer: D) -> Result<Vec<Slice<'de>>, D::Error>
 where
     D: Deserializer<'de>,
 {
     #[derive(Deserialize)]
-    struct Wrapper(#[serde(with = "SliceDef")] Slice);
+    struct Wrapper<'a>(
+        #[serde(with = "SliceDef")]
+        #[serde(borrow)]
+        Slice<'a>,
+    );
 
     let v = Vec::deserialize(deserializer)?;
     Ok(v.into_iter().map(|Wrapper(a)| a).collect())
 }
 
-fn deserialize_annotation<'de, D>(deserializer: D) -> Result<Option<Annotation>, D::Error>
+fn deserialize_annotation<'de, D>(deserializer: D) -> Result<Option<Annotation<'de>>, D::Error>
 where
     D: Deserializer<'de>,
 {
     #[derive(Deserialize)]
-    struct Wrapper(#[serde(with = "AnnotationDef")] Annotation);
+    struct Wrapper<'a>(
+        #[serde(with = "AnnotationDef")]
+        #[serde(borrow)]
+        Annotation<'a>,
+    );
 
     Option::<Wrapper>::deserialize(deserializer)
         .map(|opt_wrapped: Option<Wrapper>| opt_wrapped.map(|wrapped: Wrapper| wrapped.0))
 }
 
-fn deserialize_annotations<'de, D>(deserializer: D) -> Result<Vec<Annotation>, D::Error>
+fn deserialize_annotations<'de, D>(deserializer: D) -> Result<Vec<Annotation<'de>>, D::Error>
 where
     D: Deserializer<'de>,
 {
     #[derive(Deserialize)]
-    struct Wrapper(#[serde(with = "AnnotationDef")] Annotation);
+    struct Wrapper<'a>(
+        #[serde(with = "AnnotationDef")]
+        #[serde(borrow)]
+        Annotation<'a>,
+    );
 
     let v = Vec::deserialize(deserializer)?;
     Ok(v.into_iter().map(|Wrapper(a)| a).collect())
@@ -73,24 +104,31 @@ where
 
 #[derive(Deserialize)]
 #[serde(remote = "Slice")]
-pub struct SliceDef {
-    pub source: String,
+pub struct SliceDef<'a> {
+    #[serde(borrow)]
+    pub source: &'a str,
     pub line_start: usize,
-    pub origin: Option<String>,
+    #[serde(borrow)]
+    pub origin: Option<&'a str>,
     #[serde(deserialize_with = "deserialize_source_annotations")]
-    pub annotations: Vec<SourceAnnotation>,
+    #[serde(borrow)]
+    pub annotations: Vec<SourceAnnotation<'a>>,
     #[serde(default)]
     pub fold: bool,
 }
 
 fn deserialize_source_annotations<'de, D>(
     deserializer: D,
-) -> Result<Vec<SourceAnnotation>, D::Error>
+) -> Result<Vec<SourceAnnotation<'de>>, D::Error>
 where
     D: Deserializer<'de>,
 {
     #[derive(Deserialize)]
-    struct Wrapper(#[serde(with = "SourceAnnotationDef")] SourceAnnotation);
+    struct Wrapper<'a>(
+        #[serde(with = "SourceAnnotationDef")]
+        #[serde(borrow)]
+        SourceAnnotation<'a>,
+    );
 
     let v = Vec::deserialize(deserializer)?;
     Ok(v.into_iter().map(|Wrapper(a)| a).collect())
@@ -98,18 +136,21 @@ where
 
 #[derive(Serialize, Deserialize)]
 #[serde(remote = "SourceAnnotation")]
-pub struct SourceAnnotationDef {
+pub struct SourceAnnotationDef<'a> {
     pub range: (usize, usize),
-    pub label: String,
+    #[serde(borrow)]
+    pub label: &'a str,
     #[serde(with = "AnnotationTypeDef")]
     pub annotation_type: AnnotationType,
 }
 
 #[derive(Serialize, Deserialize)]
 #[serde(remote = "Annotation")]
-pub struct AnnotationDef {
-    pub id: Option<String>,
-    pub label: Option<String>,
+pub struct AnnotationDef<'a> {
+    #[serde(borrow)]
+    pub id: Option<&'a str>,
+    #[serde(borrow)]
+    pub label: Option<&'a str>,
     #[serde(with = "AnnotationTypeDef")]
     pub annotation_type: AnnotationType,
 }