twee_parser/lib.rs
1//! # twee_parser
2//!
3//! The [Story] and [Passage] structs describe a Twine story.
4//! They can be constructed by the user, or parsed using the parse_* functions.
5//! A [Story] can then be modified and serialized again using the serialize_* functions.
6
7
8pub use serde_json;
9use serde_json::{Value, Map};
10
11/// An in-memory representation of a Twine story.
12#[derive(Debug, Clone)]
13pub struct Story {
14 /// The name of the story.
15 pub title: String,
16 /// The list of [Passage]s.
17 pub passages: Vec<Passage>,
18 /// The metadata.
19 /// Please refer to the [specification](https://github.com/iftechfoundation/twine-specs/blob/master/twine-2-htmloutput-spec.md#story-data)
20 /// for standard fields.
21 /// To be serializable to HTML, the values have to be strings, except tags, which are supported specifically.
22 pub meta: Map<String, Value>,
23}
24
25/// Representation of a passage in a [Story].
26#[derive(Debug, Clone)]
27pub struct Passage {
28 /// The name of the passage.
29 pub name: String,
30 /// The passage tags. Cannot contain spaces.
31 pub tags: Vec<String>,
32 /// The passage metadata.
33 pub meta: Map<String, Value>,
34 /// The text content of the passage.
35 pub content: String,
36}
37
38/// Possible parsing errors.
39#[derive(Error, Debug)]
40pub enum Error {
41 /// The xmltree library couldn't parse the data, or it doesn't have the right format.
42 #[error("Could not parse HTML: {0}")]
43 #[cfg(feature = "html")]
44 HTMLParseError(ParseError),
45 /// No <tw-storydata> tag was found.
46 #[error("No tw-storydata tag found in HTML")]
47 #[cfg(feature = "html")]
48 HTMLStoryDataNotFound,
49}
50
51/// Possible warnings during parsing.
52/// Per specification, the parser is quite generous and generates many things as warnings instead of errors.
53#[derive(Debug, Clone)]
54pub enum Warning {
55 /// The story metadata wasn't a valid JSON object.
56 StoryMetadataMalformed,
57 /// The story's title is missing.
58 StoryTitleMissing,
59 /// The passage metadata wasn't a valid inline JSON object.
60 /// The argument is the passage name.
61 PassageMetadataMalformed(String),
62 /// The passage tags weren't closed.
63 /// The argument is the passage name.
64 PassageTagsMalformed(String),
65 /// 2 passages with the same name were found.
66 /// The argument is the passage name.
67 PassageDuplicated(String),
68 /// A passage is missing it's name.
69 PassageNameMissing,
70}
71
72mod twee3;
73use thiserror::Error;
74pub use twee3::*;
75
76#[cfg(feature = "html")]
77mod html;
78#[cfg(feature = "html")]
79pub use html::*;
80#[cfg(feature = "html")]
81
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86
87 #[test]
88 fn parse_twee() {
89 let story = parse_twee3(include_str!("../test-data/Test Story.twee")).unwrap();
90 assert!(story.1.len() == 0, "{:?}", story.1);
91 }
92}