1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//! 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(),
//! );
//! }
//! ```
pub use LoadSource;
pub use OwnedSource;
pub use Parsable;
pub use ;
pub use Source;
/// Describe optional names consistently between [Source] and [OwnedSource].