est/
lib.rs

1//!# Estienne
2//!
3//!<img src="https://user-images.githubusercontent.com/6587811/190536556-aec1ba71-0aef-4878-9c1f-a9727e647083.png" alt="A digital image generated by an AI of a bearded man reading from a large book in the style of medieval painting." width=200 align=left hspace="20" vspace="20">
4//!
5//![![Rust](https://github.com/JoelMon/Estienne/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/JoelMon/Estienne/actions/workflows/rust.yml)
6//![![Project Status: WIP – Initial development is in progress, but there has not yet been a stable, usable release suitable for the public.](https://www.repostatus.org/badges/latest/wip.svg)](https://www.repostatus.org/#wip)
7//!
8//!Estienne is a simple to use library for interacting with Biblical verses from text.
9//!The focus of Estienne is to provide a library for building applications that need to interact with files that contain Biblical scriptures such as lecture notes.
10//!No special syntax needs to be used in conjunction with scriptures in order for Estienne to parse the text successfully.
11//!
12//!Estienne is still in its initial stages and not ready for use.
13//!
14//!## The Name
15//!The library is named after Robert Estienne, a French theologian of the early Christian era.
16//!He is best remembered for being the first to print the New Testament divided with numbered verses.
17//![Read More](https://www.jw.org/finder?wtlocale=E&docid=2016167&srctype=wol&srcid=share&par=14)
18//!
19//!## The Purpose
20//!The purpose of the library is to provide a simple way to manipulate Biblical verses within a line of text.
21//!The API will allow for easy manipulations of the verses, such as:
22//!- Returning a list of verses found in a line of text
23//!- Allowing a verses to be:
24//!   - prefixed with arbitrary text
25//!   - suffixed with arbitrary text
26//!   - etc.
27//!
28//!## Contributing
29//!Contributions are welcomed, but please be aware that the project is still in its prototype phase and large portions of code might change at any moment.
30//!Feel free to open an issue if you have any questions or suggestions.
31
32pub mod locales;
33mod parsers;
34mod url;
35use locales::nwt_en::Site;
36use locales::BibleError;
37pub use parsers::surround::{ScriptSlice, ScriptureCollection, Locations};
38
39
40#[allow(non_camel_case_types)]
41#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
42pub enum Locale {
43    /// American English
44    en_us,
45    es_es,
46}
47
48
49/// Adds a prefix and postfix around each scripture found in and returns the modified string.
50///
51/// ## Example 
52/// ```
53/// use est::surround;
54/// 
55/// let text: &str = "The scripture John 3:16 is known by a lot of people.";
56/// let expected:String = "The scripture <strong>John 3:16</strong> is known by a lot of people.".to_string();
57/// assert_eq!(expected, surround(text, "<strong>", "</strong>").unwrap());
58/// 
59/// ```
60pub fn surround<'a, S: Into<String> + Clone>(
61    text: S,
62    prefix: &'a str,
63    postfix: &'a str,
64) -> Result<String, BibleError> {
65    Ok(parsers::surround::Script::new(text)
66        .prefix(prefix)
67        .postfix(postfix)
68        .surround()
69        .get_text())
70}
71
72/// Adds Markdown link syntax around found scriptures to an Online Bible and the modified string is returned.
73/// When possible, it will link directly to the scripture being referenced.
74/// 
75/// The url function takes a Site enum coresponding to the translation being used.
76/// As an example, if you were building links for the english version of the online NWT translation,
77/// you would use `est::locales::nwt_en::Site::JwOrg` to build the proper link.
78/// 
79/// ## Example
80/// ```
81/// use est;
82/// use est::locales::nwt_en::Site::JwOrg;
83/// 
84/// let text: &str = "All friends should practice Proverbs 17:17!";
85/// let expected:String = "All friends should practice [Proverbs 17:17](https://www.jw.org/en/library/bible/study-bible/books/proverbs/17/#v20017017)!".to_string();
86/// assert_eq!(expected, est::url(&JwOrg, text).unwrap());
87/// ```
88pub fn url<S: Into<String> + Clone>(site: &Site, text: S) -> Result<String, BibleError> {
89    // TODO: Flip the order of the paramaters around, text should be first to follow the pattern set with the other functions.
90    Ok(parsers::surround::Script::new(text)
91        .url(site)?
92        .get_text())
93}
94
95/// Returns a vector of the scriptures found in the string passed in.
96/// 
97/// ## Example
98/// ```
99/// use est;
100/// 
101/// let text: &str = "A popular scripture is John 3:16, it is quoted often.";
102/// let expected:Vec<String> = vec!["John 3:16".to_string()];
103/// assert_eq!(expected, est::get_scriptures(text).unwrap());
104/// ```
105pub fn get_scriptures<S: Into<String> + Clone>(string: S) -> Result<ScriptureCollection, BibleError> {
106    parsers::surround::Script::new(string).get_scriptures()
107}
108
109
110/// Returns a Location struct containing the start and end index of each scripture found and the original string passed in.
111/// This function helps you process the scriptures in whatever manner that you need.
112/// 
113/// ## Example
114/// ```
115/// use est::Locations;
116/// 
117/// let text = "John 3:16 is well known and if you know it, then it's easy to remember Timothy 3:16, another important scripture.";
118/// let expect = Locations{ slices: vec![(0, 9), (71,83)], string: text.into() };
119/// assert_eq!(expect, est::get_locations(text));
120/// ```
121///
122/// If no scriptures are found in the string then `slices` will be an empty vec.
123/// ```
124/// use est::Locations;
125/// 
126/// let text = "This string contains no scripture.";
127/// let expect = Locations{ slices: vec![], string: text.into() };
128/// assert_eq!(expect, est::get_locations(text));
129/// ```
130pub fn get_locations<'a, S: Into<String> + Clone>(string: S) -> Locations {
131    parsers::surround::Script::new(string).get_locations()
132}
133
134#[cfg(test)]
135mod lib_test {
136    use super::*;
137    use pretty_assertions::assert_eq;
138
139    #[test]
140    fn t_single_url() {
141        let input: &str = "A popular scriptures is Re 12:12. It is quoted often.";
142        let expect: String = "A popular scriptures is [Re 12:12](https://www.jw.org/en/library/bible/study-bible/books/revelation/12/#v66012012). It is quoted often.".to_string();
143        let got: String = url(&Site::JwOrg, input).unwrap();
144        assert_eq!(got, expect)
145    }
146
147    #[test]
148    fn t_single_ranged_url() {
149        let input: &str = "A popular scriptures is Job 36:26-28. It is quoted often.";
150        let expect: String = "A popular scriptures is [Job 36:26-28](https://www.jw.org/en/library/bible/study-bible/books/job/36/#v18036026-v18036028). It is quoted often.".to_string();
151        let got: String = url(&Site::JwOrg, input).unwrap();
152        assert_eq!(got, expect)
153    }
154
155    #[test]
156    fn t_multipal_url() {
157        let input: &str = "Three well-known Bible scriptures are Proverbs 3:5, John 3:16, and Romans 8:28";
158        let expect: String = "Three well-known Bible scriptures are [Proverbs 3:5](https://www.jw.org/en/library/bible/study-bible/books/proverbs/3/#v20003005), [John 3:16](https://www.jw.org/en/library/bible/study-bible/books/john/3/#v43003016), and [Romans 8:28](https://www.jw.org/en/library/bible/study-bible/books/romans/8/#v45008028)".to_string();
159        let got: String = url(&Site::JwOrg, input).unwrap();
160        assert_eq!(got, expect)
161    }
162
163    #[test]
164    fn t_add_element_prefix_single() {
165        let input: &str = "Another popular scripture is John 3:16, it's quoted often.";
166        let expect: &str = "Another popular scripture is **John 3:16]], it's quoted often.";
167        let got = surround(input, "**", "]]").unwrap();
168        assert_eq!(got, expect);
169    }
170
171    #[test]
172    // Test if a String can be passed in as input.
173    fn t_add_element_prefix_single_to_string() {
174        let input: String = "Another popular scripture is John 3:16, it's quoted often.".into();
175        let expect: &str = "Another popular scripture is **John 3:16]], it's quoted often.";
176        let got = surround(input, "**", "]]").unwrap();
177        assert_eq!(got, expect);
178    }
179
180    #[test]
181    fn t_add_element_prefix_multi() {
182        let input:&str = "Other popular scriptures include John 3:16, Matthew 24:14, and Psalm 83:18, they are quoted often.";
183        let expect:&str = "Other popular scriptures include **John 3:16]], **Matthew 24:14]], and **Psalm 83:18]], they are quoted often.";
184        let got = surround(input, "**", "]]").unwrap();
185        assert_eq!(got, expect);
186    }
187
188    #[test]
189    fn t_add_element_prefix_ranged_multi() {
190        let input:&str = "Other popular scriptures include John 3:16, 17, Matthew 24:14-16, and Psalm 83:18, 17-20, they are quoted often.";
191        let expect:&str = "Other popular scriptures include **John 3:16, 17]], **Matthew 24:14-16]], and **Psalm 83:18, 17-20]], they are quoted often.";
192        let got = surround(input, "**", "]]").unwrap();
193        assert_eq!(got, expect);
194    }
195
196    #[test]
197    fn t_add_element_prefix_multi_no_scriptures() {
198        let input: &str = "There are no scriptures in this line.";
199        let expect: &str = "There are no scriptures in this line.";
200        let got = surround(input, "**", "]]").unwrap();
201        assert_eq!(got, expect);
202    }
203
204    #[test]
205    fn t_url_invalid_book() {
206        let input: &str = "A popular scriptures is Mary 12:12. It is quoted often.";
207        let result= url(&Site::JwOrg, input);
208        assert!(result.is_err());
209        assert_eq!(
210            result.unwrap_err(),
211            BibleError::BookNotFound("Mary".to_string())
212        );
213    }
214
215    #[test]
216    fn t_url_no_scripture() {
217        let input: &str = "This is not a scripture.";
218        let result = url(&Site::JwOrg, input);
219        assert!(result.is_ok());
220    }
221
222    #[test]
223    fn t_get_multiple_scriptures() {
224        let input: &str = "A popular scripture is John 3:16. Another is Matthew 24:14.";
225        let result = get_scriptures(input).unwrap();
226        assert_eq!(result, vec!["John 3:16", "Matthew 24:14"]);
227    }
228}