aboutsummaryrefslogtreecommitdiffstats
path: root/lib/src/project.rs
blob: e4cdda7cbd64422e8028154ec3e377b3a49c4e67 (plain)
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
use ogmarkup::generator::Output;
use ogmarkup::typography::{Typography, ENGLISH, FRENCH};
use serde_derive::{Deserialize, Serialize};

use crate::error::{Error, Raise};

#[derive(Debug, Serialize, Deserialize)]
pub enum Language {
    Fr,
    En,
}

impl Language {
    pub fn typography(&self) -> &dyn Typography {
        match self {
            Language::Fr => &FRENCH,
            Language::En => &ENGLISH,
        }
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Cover {
    pub extension : String,
    pub content : Vec<u8>,
}

pub trait Loader {
    type CovId;
    type DocId;
    type ProjId;

    fn load_cover(&self, id : &Self::CovId) -> Result<Cover, Error>;

    fn load_document(&self, id : &Self::DocId) -> Result<String, Error>;

    fn load_project(&self, id : &Self::ProjId) -> Result<Project<Self::CovId, Self::DocId>, Error>;
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Chapter<I> {
    pub title : Option<String>,
    pub content : Vec<I>,
}

impl<I> Chapter<I> {
    fn load_and_render<T, L, O>(&self, loader : &L, typo : &T) -> Result<Chapter<O>, Error>
    where
        T : Typography + ?Sized,
        L : Loader<DocId = I>,
        O : Output,
    {
        let title = &self.title;
        let content = &self.content;

        let doc = content
            .iter()
            .map(|ref x| {
                let input = loader.load_document(x)?;
                ogmarkup::compile(&input, typo)
                    .or_raise("Cannot parse an ogmarkup document for some reason")
            })
            .collect::<Result<Vec<O>, Error>>()?;

        Ok(Chapter {
            title : title.clone(),
            content : doc,
        })
    }
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Project<C, I> {
    pub author : String,
    pub title : String,
    pub description : Option<String>,
    pub chapters : Vec<Chapter<I>>,
    pub cover : Option<C>,
    pub numbering : Option<bool>,
    pub language : Language,
}

impl<O> Project<Cover, O> {
    pub fn load_and_render<'input, L>(
        id : &L::ProjId,
        loader : &L,
    ) -> Result<Project<Cover, O>, Error>
    where
        L : Loader,
        O : Output,
    {
        let project = loader.load_project(id)?;

        let lang = project.language;
        let typo = lang.typography();
        let numbering = project.numbering;
        let descr = project.description;
        let author = project.author;
        let title = project.title;
        let cover = project
            .cover
            .map(|x| loader.load_cover(&x).or_raise("cannot load the cover"))
            .map_or(Ok(None), |r| r.map(Some))?;

        project
            .chapters
            .into_iter()
            .map(|chapter| chapter.load_and_render(loader, typo))
            .collect::<Result<Vec<Chapter<O>>, Error>>()
            .map(|x| Project {
                author : author,
                title : title,
                description : descr,
                chapters : x,
                cover : cover,
                numbering : numbering,
                language : lang,
            })
    }
}