vimwiki_core/lang/
mod.rs

1pub mod elements;
2pub mod output;
3pub mod parsers;
4
5use derive_more::Display;
6use elements::*;
7use parsers::{vimwiki, IResult, Span};
8
9/// Parse a value from a `Language`
10pub trait FromLanguage<'a>: Sized {
11    type Error;
12
13    /// Parses a `Language` to return a value of this type
14    fn from_language(language: Language<'a>) -> Result<Self, Self::Error>;
15}
16
17/// Represents a raw, unparsed representation of some language
18/// (vimwiki, markdown, mediawiki)
19///
20/// ## Examples
21///
22/// ```
23/// use vimwiki::{Language, Page};
24///
25/// // Load some language as a string
26/// let language = Language::from_vimwiki_str(r#"
27/// = My Header =
28///
29/// Some paragraph with *decorations* and [[links]] that you would normally
30/// see in a vimwiki file.
31/// "#);
32///
33/// // Parse the input as a page using vimwiki format
34/// let page: Page = language.parse().unwrap();
35/// ```
36///
37#[derive(Copy, Clone, Debug, Eq, PartialEq, Display)]
38pub enum Language<'a> {
39    Vimwiki(&'a str),
40    Markdown(&'a str),
41    Mediawiki(&'a str),
42}
43
44impl<'a> Language<'a> {
45    /// Wraps provided `&str` as a `Language` for *vimwiki*
46    pub fn from_vimwiki_str(inner: &'a str) -> Self {
47        Self::Vimwiki(inner)
48    }
49
50    /// Wraps provided `&str` as a `Language` for *markdown*
51    pub fn from_markdown_str(inner: &'a str) -> Self {
52        Self::Markdown(inner)
53    }
54
55    /// Wraps provided `&str` as a `Language` for *mediawiki*
56    pub fn from_mediawiki_str(inner: &'a str) -> Self {
57        Self::Mediawiki(inner)
58    }
59
60    /// Whether or not this represents a vimwiki format
61    pub fn is_vimwiki(&self) -> bool {
62        matches!(self, Self::Vimwiki(_))
63    }
64
65    /// Whether or not this represents a markdown format
66    pub fn is_markdown(&self) -> bool {
67        matches!(self, Self::Markdown(_))
68    }
69
70    /// Whether or not this represents a mediawiki format
71    pub fn is_mediawiki(&self) -> bool {
72        matches!(self, Self::Mediawiki(_))
73    }
74
75    pub fn as_inner(&self) -> &str {
76        match self {
77            Self::Vimwiki(x) => x,
78            Self::Markdown(x) => x,
79            Self::Mediawiki(x) => x,
80        }
81    }
82
83    /// Borrows this language and parses it into another type
84    pub fn parse<F: FromLanguage<'a>>(&self) -> Result<F, F::Error> {
85        FromLanguage::from_language(*self)
86    }
87}
88
89macro_rules! impl_from_language {
90    ($t:ty, $f:expr) => {
91        impl<'a> FromLanguage<'a> for $t {
92            type Error = parsers::Error<'a>;
93
94            fn from_language(l: Language<'a>) -> Result<Self, Self::Error> {
95                match l {
96                    Language::Vimwiki(x) => Ok($f(Span::from(x))?.1),
97                    _ => Err(parsers::Error::unsupported()),
98                }
99            }
100        }
101    };
102}
103
104// Top-level types
105impl_from_language!(Page<'a>, vimwiki::page);
106impl_from_language!(Located<BlockElement<'a>>, vimwiki::blocks::block_element);
107impl_from_language!(
108    Located<InlineElementContainer<'a>>,
109    vimwiki::blocks::inline::inline_element_container
110);
111impl_from_language!(
112    Located<InlineElement<'a>>,
113    vimwiki::blocks::inline::inline_element
114);
115
116// Blockquotes
117impl_from_language!(
118    Located<Blockquote<'a>>,
119    vimwiki::blocks::blockquotes::blockquote
120);
121
122// Code
123impl_from_language!(
124    Located<CodeInline<'a>>,
125    vimwiki::blocks::inline::code::code_inline
126);
127
128// Comments
129impl_from_language!(
130    Located<Comment<'a>>,
131    vimwiki::blocks::inline::comments::comment
132);
133impl_from_language!(
134    Located<LineComment<'a>>,
135    vimwiki::blocks::inline::comments::line_comment
136);
137impl_from_language!(
138    Located<MultiLineComment<'a>>,
139    vimwiki::blocks::inline::comments::multi_line_comment
140);
141
142// Definitions (NOTE: Generic LocatedElement def above handles term & def)
143impl_from_language!(
144    Located<DefinitionList<'a>>,
145    vimwiki::blocks::definitions::definition_list
146);
147// impl_from_language!(Located<Definition>, vimwiki::definition);
148// impl_from_language!(Located<Term>, vimwiki::term);
149
150// Dividers
151impl_from_language!(Located<Divider>, vimwiki::blocks::dividers::divider);
152
153// Headers
154impl_from_language!(Located<Header<'a>>, vimwiki::blocks::headers::header);
155
156// Links
157impl_from_language!(Located<Link<'a>>, vimwiki::blocks::inline::links::link);
158
159// Lists
160impl_from_language!(Located<List<'a>>, vimwiki::blocks::lists::list);
161impl_from_language!(Located<ListItem<'a>>, parse_list_item);
162fn parse_list_item<'a>(input: Span<'a>) -> IResult<Located<ListItem<'a>>> {
163    nom::combinator::map(
164        vimwiki::blocks::lists::list_item,
165        |(_, item): (usize, Located<ListItem>)| item,
166    )(input)
167}
168
169// Math
170impl_from_language!(
171    Located<MathInline<'a>>,
172    vimwiki::blocks::inline::math::math_inline
173);
174impl_from_language!(Located<MathBlock<'a>>, vimwiki::blocks::math::math_block);
175
176// Paragraphs
177impl_from_language!(
178    Located<Paragraph<'a>>,
179    vimwiki::blocks::paragraphs::paragraph
180);
181
182// Placeholders
183impl_from_language!(
184    Located<Placeholder<'a>>,
185    vimwiki::blocks::placeholders::placeholder
186);
187
188// Preformatted Text
189impl_from_language!(Located<CodeBlock<'a>>, vimwiki::blocks::code::code_block);
190
191// Tables
192impl_from_language!(Located<Table<'a>>, vimwiki::blocks::tables::table);
193
194// Tags
195impl_from_language!(Located<Tags<'a>>, vimwiki::blocks::inline::tags::tags);
196
197// Typefaces
198impl_from_language!(
199    Located<Text<'a>>,
200    vimwiki::blocks::inline::typefaces::text
201);
202impl_from_language!(
203    Located<DecoratedText<'a>>,
204    vimwiki::blocks::inline::typefaces::decorated_text
205);
206impl_from_language!(
207    Located<Keyword>,
208    vimwiki::blocks::inline::typefaces::keyword
209);
210
211#[cfg(test)]
212mod tests {
213    use super::*;
214
215    /// Contains tests for the vimwiki language parsers
216    mod vimwiki {
217        use super::*;
218
219        #[test]
220        fn parse_to_page() {
221            let input = Language::from_vimwiki_str("some text");
222            let _result: Page = input.parse().expect("Failed to parse");
223        }
224
225        #[test]
226        fn parse_to_located_block_element() {
227            let input = Language::from_vimwiki_str("some text");
228            let _result: Located<BlockElement> =
229                input.parse().expect("Failed to parse");
230        }
231
232        #[test]
233        fn parse_to_located_inline_element_container() {
234            let input = Language::from_vimwiki_str("some text");
235            let _result: Located<InlineElementContainer> =
236                input.parse().expect("Failed to parse");
237        }
238
239        #[test]
240        fn parse_to_located_inline_element() {
241            let input = Language::from_vimwiki_str("some text");
242            let _result: Located<InlineElement> =
243                input.parse().expect("Failed to parse");
244        }
245
246        #[test]
247        fn parse_to_located_blockquote() {
248            let input = Language::from_vimwiki_str("> some text");
249            let _result: Located<Blockquote> =
250                input.parse().expect("Failed to parse");
251        }
252
253        #[test]
254        fn parse_to_located_code_inline() {
255            let input = Language::from_vimwiki_str("`code`");
256            let _result: Located<CodeInline> =
257                input.parse().expect("Failed to parse");
258        }
259
260        #[test]
261        fn parse_to_located_comment() {
262            let input = Language::from_vimwiki_str("%% some comment");
263            let _result: Located<Comment> =
264                input.parse().expect("Failed to parse");
265        }
266
267        #[test]
268        fn parse_to_located_line_comment() {
269            let input = Language::from_vimwiki_str("%% some comment");
270            let _result: Located<LineComment> =
271                input.parse().expect("Failed to parse");
272        }
273
274        #[test]
275        fn parse_to_located_multi_line_comment() {
276            let input = Language::from_vimwiki_str("%%+ some comment +%%");
277            let _result: Located<MultiLineComment> =
278                input.parse().expect("Failed to parse");
279        }
280
281        #[test]
282        fn parse_to_located_definition_list() {
283            let input = Language::from_vimwiki_str("term:: definition");
284            let _result: Located<DefinitionList> =
285                input.parse().expect("Failed to parse");
286        }
287
288        #[test]
289        fn parse_to_located_divider() {
290            let input = Language::from_vimwiki_str("----");
291            let _result: Located<Divider> =
292                input.parse().expect("Failed to parse");
293        }
294
295        #[test]
296        fn parse_to_located_header() {
297            let input = Language::from_vimwiki_str("= header =");
298            let _result: Located<Header> =
299                input.parse().expect("Failed to parse");
300        }
301
302        #[test]
303        fn parse_to_located_link() {
304            let input = Language::from_vimwiki_str("[[link]]");
305            let _result: Located<Link> =
306                input.parse().expect("Failed to parse");
307        }
308
309        #[test]
310        fn parse_to_located_list() {
311            let input = Language::from_vimwiki_str("- some list item");
312            let _result: Located<List> =
313                input.parse().expect("Failed to parse");
314        }
315
316        #[test]
317        fn parse_to_located_math_inline() {
318            let input = Language::from_vimwiki_str("$math$");
319            let _result: Located<MathInline> =
320                input.parse().expect("Failed to parse");
321        }
322
323        #[test]
324        fn parse_to_located_math_block() {
325            let input = Language::from_vimwiki_str("{{$\nmath\n}}$");
326            let _result: Located<MathBlock> =
327                input.parse().expect("Failed to parse");
328        }
329
330        #[test]
331        fn parse_to_located_paragraph() {
332            let input = Language::from_vimwiki_str("some text");
333            let _result: Located<Paragraph> =
334                input.parse().expect("Failed to parse");
335        }
336
337        #[test]
338        fn parse_to_located_placeholder() {
339            let input = Language::from_vimwiki_str("%title some text");
340            let _result: Located<Placeholder> =
341                input.parse().expect("Failed to parse");
342        }
343
344        #[test]
345        fn parse_to_located_code_block() {
346            let input = Language::from_vimwiki_str("{{{\nsome code\n}}}");
347            let _result: Located<CodeBlock> =
348                input.parse().expect("Failed to parse");
349        }
350
351        #[test]
352        fn parse_to_located_table() {
353            let input = Language::from_vimwiki_str("|cell|");
354            let _result: Located<Table> =
355                input.parse().expect("Failed to parse");
356        }
357
358        #[test]
359        fn parse_to_located_tags() {
360            let input = Language::from_vimwiki_str(":tag:");
361            let _result: Located<Tags> =
362                input.parse().expect("Failed to parse");
363        }
364
365        #[test]
366        fn parse_to_located_text() {
367            let input = Language::from_vimwiki_str("some text");
368            let _result: Located<Text> =
369                input.parse().expect("Failed to parse");
370        }
371
372        #[test]
373        fn parse_to_located_decorated_text() {
374            let input = Language::from_vimwiki_str("*some text*");
375            let _result: Located<DecoratedText> =
376                input.parse().expect("Failed to parse");
377        }
378
379        #[test]
380        fn parse_to_located_keyword() {
381            let input = Language::from_vimwiki_str("TODO");
382            let _result: Located<Keyword> =
383                input.parse().expect("Failed to parse");
384        }
385    }
386}