Crate source_text

source ·
Expand description

Enable API’s that consume &str to retrieve the text string from any LoadSource source while tracking the origin.

Terminology

This crate uses some terminology to help in disambiguation:

  • “text” - an &str to be “processed” by application code; in contrast to &str used for other purposes such as naming the origin of a “text” or used in error messages.
  • “source” - a Source value which tracks both a text and the name of its origin.

Example: Parsable

Suppose we have a config file parser which parses a configuration into a Config struct. We want to be able to either parse strings in memory or load and parse config files from disk, with error messages indicating the source:

use indoc::indoc; // For cleanly formatting test assertions.
use source_text::Parsable;

#[derive(Debug)]
pub struct Config {
    // ...
}

impl Parsable for Config {
    fn parse_text(text: &str) -> anyhow::Result<Self> {
        if text.is_empty() {
            Err(anyhow::Error::msg("empty input\n"))
        } else {
            todo!("implement an `&str` parser...");
        }
    }
}

let err1 = Config::parse_source("").err().unwrap();

assert_eq!(
    format!("{:?}", err1).trim_end(),
    indoc! {
    r#"
       Error in <input>:

       Caused by:
           empty input
    "#
    }.trim_end()
);

let configpath = std::path::Path::new("/__this_path_should_not_exist__");
let err2 = Config::parse_source(configpath).err().unwrap();

assert_eq!(
    format!("{:?}", err2).trim_end(),
    indoc! { r#"
        Error in "/__this_path_should_not_exist__":

        Caused by:
            No such file or directory (os error 2)
    "# }.trim_end(),
);

Example: process_text

If Parsable does not fit your usage, you can wrap any fn(&str) -> anyhow::Result<T> with process_text:

use indoc::indoc; // For cleanly formatting test assertions.

fn count_lines<S>(source: S) -> anyhow::Result<usize>
    where S: source_text::LoadSource,
{
    source_text::process_text(source, |text: &str| {
        Ok(text.lines().count())
    })
}

fn main() {
    let linecount = count_lines("Hello\nWorld!").unwrap();
    assert_eq!(2, linecount);

    let path = std::path::Path::new("/__this_path_should_not_exist__");
    let err = count_lines(path).err().unwrap();

    assert_eq!(
        format!("{:?}", err).trim_end(),
        indoc! { r#"
            Error in "/__this_path_should_not_exist__":

            Caused by:
                No such file or directory (os error 2)
        "# }.trim_end(),
    );
}

Structs

  • An OwnedSource owns a text string with an optional name denoting the origin.
  • A Source owns or refers to a text string with an optional name denoting the origin.

Traits

  • Any type that can load a Source synchronously.
  • A type that can be parsed from a text &str; this provides a parse_source default method for parsing any LoadSource value such as &str or &Path:

Functions

  • Process any Source with a callback, annotating the error with the source’s origin name.
  • Process any text &str with a callback, annotating the error with the source’s origin name.