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 115 116 117 118 119 120 121 122 123 124
use std::pin::Pin; use futures::{Stream, StreamExt}; use crate::layers::domain::entities::{Example, Printable}; pub struct AsciidoctorSettings { pub callout_token: String } pub struct AsciidoctorExample { inner: Example } impl Printable for AsciidoctorExample { fn print(&self) -> String { self.inner.print() } fn file_name(&self) -> String { format!("{}.adoc", self.inner.name) } } pub fn map_to_asciidoctor(input: Pin<Box<dyn Stream<Item=Example>>>, settings: AsciidoctorSettings) -> Pin<Box<dyn Stream<Item=Result<AsciidoctorExample, String>>>> { Box::pin(input.map(move |example| { let header = create_asciidoc_source_header(&settings, &example); let footer = create_asciidoc_source_footer(&settings); let content = transform_callouts(example.content, &settings.callout_token)?; let callouts = content.1 .into_iter() .map(|callout| format!("<{}> {}", callout.number, callout.text)) .collect(); Ok(AsciidoctorExample { inner: Example { name: example.name, content: vec![ header, content.0, footer, callouts ].into_iter().flatten().collect(), title: example.title, language: example.language, } }) })) } fn create_asciidoc_source_header(_settings: &AsciidoctorSettings, example: &Example) -> Vec<String> { let title = match &example.title { Some(title) => vec![format!(".{}", title)], _ => vec![] }; vec![ title, vec![format!("[source{}]", match &example.language { Some(language) => format!(",{}", language), _ => "".into() })], vec!["----".into()] ].into_iter().flatten().collect() } fn create_asciidoc_source_footer(_settings: &AsciidoctorSettings) -> Vec<String> { vec![ "----".into() ] } struct Callout { text: String, number: usize } fn transform_callouts(input: Vec<String>, callout_token: &String) -> Result<(Vec<String>, Vec<Callout>), String> { let mut callout_number = 1; let mut output = Vec::new(); let mut callouts= Vec::new(); for mut line in input { loop { if line.contains(callout_token) { line = line.replacen(callout_token, format!("<{}>", callout_number).as_str(), 1); let extract = extract_first_callout(line, callout_number) .map_err(|e| format!("Failed extracting callout from {}", e))?; line = extract.0; callouts.push(extract.1); callout_number += 1; } else { break; } } output.push(line); } Ok((output, callouts)) } fn extract_first_callout(mut input: String, idx: usize) -> Result<(String, Callout), String> { lazy_static::lazy_static! { static ref CALLOUT_RE: regex::Regex = regex::Regex::new("\\{value=\"(.+)\"\\}").unwrap(); } for val in CALLOUT_RE.captures_iter(input.clone().as_str()) { let value = val.get(1); if let Some(value) = value { input = CALLOUT_RE.replace(input.as_str(), "").to_string(); return Ok((input, Callout { text: value.as_str().to_string().clone(), number: idx })) } } Err(input) }