inkling/line/parse/
kind.rs1use crate::{
4 consts::DIVERT_MARKER,
5 error::{parse::line::LineError, utils::MetaData},
6 line::{
7 parse::{parse_choice, parse_gather, parse_internal_line},
8 InternalChoice, InternalLine,
9 },
10};
11
12#[derive(Clone, Debug, PartialEq)]
13pub enum ParsedLineKind {
20 Choice {
21 level: u32,
23 choice_data: InternalChoice,
25 },
26 Gather {
27 level: u32,
29 line: InternalLine,
31 },
32 Line(InternalLine),
34}
35
36#[cfg(test)]
37impl ParsedLineKind {
38 pub fn choice(level: u32, choice_data: InternalChoice) -> Self {
40 ParsedLineKind::Choice { level, choice_data }
41 }
42
43 pub fn gather(level: u32, line: InternalLine) -> Self {
45 ParsedLineKind::Gather { level, line }
46 }
47
48 pub fn line(line: InternalLine) -> Self {
50 ParsedLineKind::Line(line)
51 }
52}
53
54pub fn parse_line(content: &str, meta_data: &MetaData) -> Result<ParsedLineKind, LineError> {
56 if let Some(choice) = parse_choice(content, meta_data).transpose() {
57 choice
58 } else if let Some(gather) = parse_gather(content, meta_data).transpose() {
59 gather
60 } else {
61 parse_internal_line(content, meta_data).map(|line| ParsedLineKind::Line(line))
62 }
63 .map_err(|kind| LineError {
64 line: content.to_string(),
65 kind,
66 meta_data: meta_data.clone(),
67 })
68}
69
70pub fn parse_markers_and_text(line: &str, marker: char) -> Option<(u32, &str)> {
72 if line.trim_start().starts_with(marker) {
73 let (markers, line_text) = split_markers_from_text(line, marker);
74 let num = markers.matches(|c| c == marker).count() as u32;
75
76 Some((num, line_text))
77 } else {
78 None
79 }
80}
81
82fn split_markers_from_text(line: &str, marker: char) -> (&str, &str) {
84 let split_at = line.find(|c: char| !(c == marker || c.is_whitespace()));
85
86 match split_at {
87 Some(i) => line.split_at(i),
88 None => (line, ""),
89 }
90}
91
92pub fn split_at_divert_marker(content: &str) -> (&str, &str) {
94 if let Some(i) = content.find(DIVERT_MARKER) {
95 content.split_at(i)
96 } else {
97 (content, "")
98 }
99}
100
101#[cfg(test)]
102pub mod tests {
103 use super::*;
104
105 #[test]
106 fn simple_line_parses_to_line() {
107 let line = parse_line("Hello, World!", &().into()).unwrap();
108 let comparison = parse_internal_line("Hello, World!", &().into()).unwrap();
109
110 assert_eq!(line, ParsedLineKind::Line(comparison));
111 }
112
113 #[test]
114 fn line_with_choice_markers_parses_to_choice() {
115 let line = parse_line("* Hello, World!", &().into()).unwrap();
116
117 match line {
118 ParsedLineKind::Choice { .. } => (),
119 other => panic!("expected `ParsedLineKind::Choice` but got {:?}", other),
120 }
121 }
122
123 #[test]
124 fn line_with_gather_markers_parses_to_gather() {
125 let line = parse_line("- Hello, World!", &().into()).unwrap();
126
127 match line {
128 ParsedLineKind::Gather { .. } => (),
129 other => panic!("expected `ParsedLineKind::Gather` but got {:?}", other),
130 }
131 }
132
133 #[test]
134 fn choices_are_parsed_before_gathers() {
135 let line = parse_line("* - Hello, World!", &().into()).unwrap();
136
137 match line {
138 ParsedLineKind::Choice { .. } => (),
139 other => panic!("expected `ParsedLineKind::Choice` but got {:?}", other),
140 }
141 }
142}