1use std::io::{self, BufRead, Write};
2
3pub struct Converter {
4 state: State,
5 blank_line_count: usize,
6 buffered_lines: String,
7 warnings: Vec<Warning>,
8}
9
10use super::Warning;
11
12#[derive(Copy, Clone, PartialEq, Eq, Debug)]
13enum State { MarkdownBlank, MarkdownText, MarkdownMeta, Rust, }
14impl Converter {
15 pub fn new() -> Converter {
16 Converter {
17 state: State::MarkdownBlank,
18 blank_line_count: 0,
19 buffered_lines: String::new(),
20 warnings: vec![],
21 }
22 }
23}
24
25pub enum Exception {
26 IoError(io::Error),
27 Warnings(Vec<Warning>),
28}
29
30impl From<io::Error> for Exception {
31 fn from(e: io::Error) -> Self {
32 Exception::IoError(e)
33 }
34}
35
36impl Converter {
37 pub fn convert<R:io::Read, W:io::Write>(mut self, r:R, mut w:W) -> Result<(), Exception> {
38 let source = io::BufReader::new(r);
39 for line in source.lines() {
40 let line = (line)?;
41 (self.handle(&line, &mut w))?;
42 }
43 if self.warnings.is_empty() {
44 Ok(())
45 } else {
46 Err(Exception::Warnings(self.warnings))
47 }
48 }
49
50 pub fn handle(&mut self, line: &str, w: &mut dyn Write) -> io::Result<()> {
51 let str9 = line.chars().take(9).collect::<String>();
52 let str7 = line.chars().take(7).collect::<String>();
53 match (self.state, &str7[..], &str9[..]) {
54 (State::MarkdownBlank, "```rust", _) |
55 (State::MarkdownText, "```rust", _) => {
56 self.buffered_lines = String::new();
57 let rest = &line.chars().skip(7).collect::<String>();
58 if rest != "" {
59 (self.transition(w, State::MarkdownMeta))?;
60 (self.meta_note(&rest, w))?;
61 }
62 self.transition(w, State::Rust)
63 }
64 (State::MarkdownBlank, _, "```{.rust") |
65 (State::MarkdownText, _, "```{.rust") => {
66 self.buffered_lines = String::new();
67 let rest = &line.chars().skip(9).collect::<String>();
68 if rest != "" {
69 (self.transition(w, State::MarkdownMeta))?;
70 (self.meta_note(&format!(" {{{}", rest), w))?;
71 }
72 self.transition(w, State::Rust)
73 }
74 (State::Rust, "```", _) => {
75 self.transition(w, State::MarkdownBlank)
76 }
77
78 (_, "", _) => {
83 self.blank_line(w)
84 }
85
86 _ => {
87 let open_pat = "[";
89 let close_pat = "]: https://play.rust-lang.org/?code=";
90 if let (Some(open), Some(close)) = (line.find(open_pat), line.find(close_pat)) {
91 let expect = super::encode_to_url(&self.buffered_lines);
99 let actual = &line[(close+3)..];
100 if expect != actual {
101 self.warnings.push(Warning::EncodedUrlMismatch {
102 actual: actual.to_string(),
103 expect: expect
104 })
105 }
106 self.name_block(line, &line[open+1..close], w)
107 } else {
108 self.nonblank_line(line, w)
109 }
110 }
111 }
112 }
113
114 pub fn meta_note(&mut self, note: &str, w: &mut dyn Write) -> io::Result<()> {
115 assert!(note != "");
116 self.nonblank_line(note, w)
117 }
118
119 pub fn name_block(&mut self, _line: &str, name: &str, w: &mut dyn Write) -> io::Result<()> {
120 assert!(name != "");
121 writeln!(w, "//@@@ {}", name)
122 }
123
124 pub fn nonblank_line(&mut self, line: &str, w: &mut dyn Write) -> io::Result<()> {
125 let (blank_prefix, line_prefix) = match self.state {
126 State::MarkdownBlank => ("", "//@ "),
127 State::MarkdownText => ("//@", "//@ "),
128 State::MarkdownMeta => ("//@", "//@@"),
129 State::Rust => ("", ""),
130 };
131 for _ in 0..self.blank_line_count {
132 (writeln!(w, "{}", blank_prefix))?;
133
134 }
135 self.blank_line_count = 0;
136
137 match self.state {
138 State::MarkdownBlank =>
139 (self.transition(w, State::MarkdownText))?,
140 State::MarkdownMeta |
141 State::MarkdownText => {}
142 State::Rust => {
143 self.buffered_lines.push_str("\n");
144 self.buffered_lines.push_str(line);
145 }
146 }
147
148 writeln!(w, "{}{}", line_prefix, line)
149 }
150
151 fn blank_line(&mut self, _w: &mut dyn Write) -> io::Result<()> {
152 match self.state {
153 State::Rust => {
154 self.buffered_lines.push_str("\n");
155 }
156 State::MarkdownBlank |
157 State::MarkdownMeta |
158 State::MarkdownText => {}
159 }
160 self.blank_line_count += 1;
161 Ok(())
162 }
163
164 fn finish_section(&mut self, w: &mut dyn Write) -> io::Result<()> {
165 for _ in 0..self.blank_line_count {
166 (writeln!(w, ""))?;
167 }
168 self.blank_line_count = 0;
169 Ok(())
170 }
171
172 fn transition(&mut self, w: &mut dyn Write, s: State) -> io::Result<()> {
173 match s {
174 State::MarkdownMeta => {
175 assert!(self.state != State::Rust);
176 (self.finish_section(w))?;
177 }
178 State::Rust => {
179 assert!(self.state != State::Rust);
180 self.buffered_lines = String::new();
181 }
182 State::MarkdownText => {
183 assert_eq!(self.state, State::MarkdownBlank);
184 (self.finish_section(w))?;
185 }
186 State::MarkdownBlank => {
187 assert_eq!(self.state, State::Rust);
188 (self.finish_section(w))?;
189 }
190 }
191 self.state = s;
192 Ok(())
193 }
194}