ireal_parser/
lib.rs

1//! # iReal Parser
2//!
3//! `ireal_parser` is a library that provides functionality to parse iReal Pro
4//! URLs and chord progressions according to the [iReal Pro
5//! specification](https://www.irealpro.com/ireal-pro-file-format).
6//!
7//! ## Features
8//!
9//! - Parse iReal Pro URLs into a [`Song`] struct with fields like title,
10//!   composer, style, and key signature.
11//! - Parse iReal Pro progressions into a [`Progression`] struct containing
12//!   [`ProgressionElement`]s.
13//! - Convert [`Song`], [`Progression`], and [`ProgressionElement`] structs back
14//!   to iReal Pro URL and progression string formats.
15//!
16//! ## Usage
17//!
18//! Add `ireal_parser` as a dependency in your `Cargo.toml`, then use the
19//! parsing functions provided by the crate:
20//!
21//! ```rust
22//! use ireal_parser::parse_irealbook_url;
23//!
24//! // Parse an iReal Pro URL into a `Song` struct
25//! let song = parse_irealbook_url("irealbook://Song Title=LastName FirstName=Style=Ab=n=T44*A{C^7 |A-7 |D-9 |G7#5 }").unwrap();
26//! ```
27//!
28//! For more information, refer to the [iReal Pro
29//! specification](https://www.irealpro.com/ireal-pro-file-format).
30
31mod error;
32pub use error::*;
33
34mod time_signature;
35pub use time_signature::*;
36
37mod chord;
38pub use chord::*;
39
40mod staff_text;
41pub use staff_text::*;
42
43mod progression;
44pub use progression::*;
45
46mod song;
47pub use song::*;
48
49#[cfg(test)]
50mod tests {
51    use super::*;
52
53    #[test]
54    fn test_parse_irealbook_progression() {
55        let input = "{C |A- |D- |G7 }[C |A- |D- |G7 Z";
56        let p = parse_irealbook_progression(input).unwrap();
57        assert_eq!(input, p.to_string());
58
59        let input = "T44[C |A- T98|D- |G7 Z";
60        let p = parse_irealbook_progression(input).unwrap();
61        assert_eq!(input, p.to_string());
62
63        let input = "[T44C | x | x | x |D-7 |G7 | r| Z";
64        let _p = parse_irealbook_progression(input).unwrap();
65
66        let input = "*A[C |A- |SD- |G7 QZ";
67        let p = parse_irealbook_progression(input).unwrap();
68        assert_eq!(input, p.to_string());
69
70        let input = "T44{C |A- |N1D- |G7 } |N2D- G7 |C6 Z";
71        let _p = parse_irealbook_progression(input).unwrap();
72
73        let input = "[T44<*74Solo Section:>C |A- |D- |G7 Z";
74        let p = parse_irealbook_progression(input).unwrap();
75        assert_eq!(input, p.to_string());
76
77        let input = "[*AT44C |A- |D- |G7 |E- |A- |D- |G7 ZY[*BC |A- |D- |G7 Z";
78        let p = parse_irealbook_progression(input).unwrap();
79        assert_eq!(input, p.to_string());
80    }
81
82    #[test]
83    fn test_parse_a_walking_thing() {
84        let input = "irealbook://A Walkin Thing=Carter Benny=Medium Swing=D-=n=\
85                     {*AT44D- D-/C |Bh7, Bb7(A7b9) |D-/A G-7 |D-/F sEh,A7,|Y\
86                     |lD- D-/C |Bh7, Bb7(A7b9) |D-/A G-7 |N1D-/F sEh,A7} Y|N2sD-,G-,lD- ]\
87                     [*BC-7 F7 |Bb^7 |C-7 F7 |Bb^7 n |\
88                     |C-7 F7 |Bb^7 |B-7 E7 |A7,p,p,p,]\
89                     [*AD- D-/C |Bh7, Bb7(A7b9) |D-/A G-7 |D-/F sEh,A7,|\
90                     |lD- D-/C |Bh7, Bb7(A7b9) |D-/A G-7 |D-/F sEh,A7Z";
91        let _p = parse_irealbook_url(input).unwrap();
92    }
93
94    #[test]
95    fn test_parse_invalid_irealbook_url() {
96        let input = "irealbook://Song Title=LastName FirstName=Ab=n=T44";
97        let parsed = parse_irealbook_url(input);
98        assert!(matches!(parsed, Err(Error::InvalidUrl)));
99    }
100
101    #[test]
102    fn test_parse_irealbook_url() {
103        let input = "irealbook://Song Title=LastName FirstName=Style=Ab=n=T44";
104        let _parsed = parse_irealbook_url(input).unwrap();
105    }
106}