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 &lt;tw-storydata&gt; 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}