pub trait Content: AsRef<str> + Debug + Eq + PartialEq + Default + From<String> {
    fn header(&self) -> &str;
    fn body(&self) -> &str;

    fn open(path: &Path) -> Result<Self, Error>
    where
        Self: Sized
, { ... } fn from_string_with_cr(input: String) -> Self { ... } fn save_as(&self, new_file_path: &Path) -> Result<(), Error> { ... } fn as_str(&self) -> &str { ... } fn is_empty(&self) -> bool { ... } fn split(content: &str) -> (&str, &str) { ... } }
Expand description

Provides cheap access to the header with header(), the body with body(), and the whole raw text with as_str().

use tpnote_lib::content::Content;
use tpnote_lib::content::ContentString;
let input = "---\ntitle: \"My note\"\n---\nMy body";
let c = ContentString::from(String::from(input));

assert_eq!(c.header(), r#"title: "My note""#);
assert_eq!(c.body(), r#"My body"#);
assert_eq!(c.as_str(), input);

// A test without front matter leads to an empty header:
let c = ContentString::from(String::from("No header"));

assert_eq!(c.header(), "");
assert_eq!(c.body(), "No header");
assert_eq!(c.as_str(), "No header");

The Content trait allows to plug in you own storage back end if ContentString does not suit you. Note: you can overwrite Content::open() and Content::save_as() also (note shown).

use tpnote_lib::content::Content;
use std::string::String;

#[derive(Debug, Eq, PartialEq, Default)]
struct MyString(String);
impl Content for MyString {
    /// This sample implementation may be too expensive.
    /// Better precalculate this in `Self::from()`.
    fn header(&self) -> &str {
        Self::split(&self.as_str()).0
    }
    fn body(&self) -> &str {
        Self::split(&self.as_str()).1
    }
}

impl From<String> for MyString {
    fn from(input: String) -> Self {
        Self(input)    
    }
}

impl AsRef<str> for MyString {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

let input = "---\ntitle: \"My note\"\n---\nMy body";
let s = MyString::from(input.to_string());

assert_eq!(s.header(), r#"title: "My note""#);
assert_eq!(s.body(), r#"My body"#);
assert_eq!(s.as_str(), input);

Required Methods

Return a reference to theader part in between ---

Return the body below the second ---.

Provided Methods

Reads the file at path and stores the content Content. Possible \r\n are replaced by \n. This trait has a default implementation, the empty content.

use tpnote_lib::content::Content;
use tpnote_lib::content::ContentString;
use std::env::temp_dir;

// Prepare test.
let raw = "---\ntitle: \"My note\"\n---\nMy body";
let notefile = temp_dir().join("20221030-hello -- world.md");
let _ = std::fs::write(&notefile, raw.as_bytes());

// Start test.
let c = ContentString::open(&notefile).unwrap();

assert_eq!(c.header(), r#"title: "My note""#);
assert_eq!(c.body(), "My body");

Constructor that reads a structured document with a YAML header and body. All \r\n are converted to \n if there are any. If not, no memory allocation occurs and the buffer remains untouched.

use tpnote_lib::content::Content;
use tpnote_lib::content::ContentString;
let c = ContentString::from_string_with_cr(String::from(
    "---\r\ntitle: \"My note\"\r\n---\r\nMy\nbody\r\n"));

assert_eq!(c.header(), r#"title: "My note""#);
assert_eq!(c.body(), "My\nbody\n");

// A test without front matter leads to an empty header:
let c = ContentString::from(String::from("No header"));

assert_eq!(c.borrow_dependent().header, "");
assert_eq!(c.borrow_dependent().body, r#"No header"#);

Writes the note to disk with new_file_path as filename. If new_file_path contains missing directories, they will be created on the fly.

use std::path::Path;
use std::env::temp_dir;
use std::fs;
use tpnote_lib::content::Content;
use tpnote_lib::content::ContentString;
let c = ContentString::from(
     String::from("prelude\n\n---\ntitle: \"My note\"\n---\nMy body"));
let outfile = temp_dir().join("mynote.md");
#[cfg(not(target_family = "windows"))]
let expected = "\u{feff}prelude\n\n---\ntitle: \"My note\"\n---\nMy body\n";
#[cfg(target_family = "windows")]
let expected = "\u{feff}prelude\r\n\r\n---\r\ntitle: \"My note\"\r\n---\r\nMy body\r\n";

c.save_as(&outfile).unwrap();
let result = fs::read_to_string(&outfile).unwrap();

assert_eq!(result, expected);
fs::remove_file(&outfile);

Accesses the whole content with all ---. Contract: The content does not contain any \r\n. If your content contains \r\n use the from_string_with_cr() constructor. Possible BOM at the first position is not returned.

True if the header and body is empty.

Helper function that splits the content into header and body. The header, if present, is trimmed (trim()), the body is kept as it is. Any BOM (byte order mark) at the beginning is ignored.

  1. Ignore \u{feff} if present
  2. Ignore ---\n or ignore all bytes until\n\n---\n,
  3. followed by header bytes,
  4. optionally followed by \n,
  5. followed by \n---\n or \n...\n,
  6. optionally followed by some \t and/or some ,
  7. optionally followed by \n. The remaining bytes are the “body”.

Alternatively, a YAML metadata block may occur anywhere in the document, but if it is not at the beginning, it must be preceded by a blank line:

  1. skip all text (BEFORE_HEADER_MAX_IGNORED_CHARS) until you find "\n\n---"
  2. followed by header bytes,
  3. same as above …

Implementors

Add header() and body() implementation.