dailp/
collection.rs

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
125
126
127
128
129
130
131
132
133
134
135
136
//! Provides types for defining and structuring edited collections.
use uuid::Uuid;

use crate::AnnotatedDoc;
use {
    crate::async_graphql::{self, dataloader::DataLoader, Context, FieldResult},
    crate::slugify,
    crate::Database,
    crate::DocumentCollection,
    crate::DocumentId,
};

/// Structure to represent an edited collection. Missing certain fields and chapters in it.
/// Used for sending data to the front end
#[derive(Debug, Clone, async_graphql::SimpleObject)]
#[graphql(complex)]
pub struct EditedCollection {
    /// UUID for the collection
    pub id: Uuid,
    /// Full title of the collection
    pub title: String,
    /// Description of the collection (optional)
    pub description: Option<String>,
    /// ID of WordPress menu for navigating the collection
    pub wordpress_menu_id: Option<i64>,
    #[graphql(skip)]
    /// URL slug for the collection, like "cwkw"
    pub slug: String,
    /// Cover image URL
    pub thumbnail_url: Option<String>,
}

/// Input object for creating a new collection
#[derive(async_graphql::InputObject)]
pub struct EditedCollectionInput {
    /// Full title of the collection
    pub title: String,
    /// URL slug for the collection, like "cwkw"
    pub slug: String,
    /// ID of WordPress menu for navigating the collection
    pub wordpress_menu_id: Option<i64>,
}

/// Structure to represent a single chapter. Used to send data to the front end.
#[derive(Debug, Clone, async_graphql::SimpleObject)]
#[graphql(complex)]
pub struct CollectionChapter {
    /// UUID for the chapter
    pub id: Uuid,
    /// Full title of the chapter
    pub title: String,
    /// ID of WordPress page with text of the chapter
    pub wordpress_id: std::option::Option<i64>,
    /// Order within the parent chapter or collection
    pub index_in_parent: i64,
    /// Whether the chapter is an "Intro" or "Body" chapter
    pub section: CollectionSection,
    /// Document id
    #[graphql(skip)]
    pub document_id: Option<DocumentId>,
    #[graphql(skip)]
    /// Full path of the chapter
    pub path: Vec<String>,
}

/// Enum to represent the sections in an edited collection
#[derive(async_graphql::Enum, Clone, Copy, PartialEq, Eq, Debug, sqlx::Type)]
#[sqlx(type_name = "collection_section")]
pub enum CollectionSection {
    /// Intro chapter
    Intro,
    /// Body chapter
    Body,
    /// Credit
    Credit,
}

#[async_graphql::ComplexObject]
impl EditedCollection {
    /// URL slug for the collection, like "cwkw"
    async fn slug(&self) -> String {
        slugify(&self.slug)
    }

    async fn chapters(&self, context: &Context<'_>) -> FieldResult<Option<Vec<CollectionChapter>>> {
        Ok(context
            .data::<DataLoader<Database>>()?
            .load_one(crate::ChaptersInCollection(self.slug.clone()))
            .await?)
    }
}

#[async_graphql::ComplexObject]
impl CollectionChapter {
    /// Full path of the chapter
    async fn path(&self) -> Vec<String> {
        self.path.iter().map(slugify).collect()
    }

    async fn slug(&self) -> String {
        slugify(self.path.last().unwrap())
    }

    async fn document(&self, context: &Context<'_>) -> FieldResult<Option<AnnotatedDoc>> {
        if let Some(doc_id) = &self.document_id {
            Ok(context
                .data::<DataLoader<Database>>()?
                .load_one(*doc_id)
                .await?)
        } else {
            Ok(None)
        }
    }
    /// Breadcrumbs from the top-level archive down to where this document lives.
    async fn breadcrumbs(
        &self,
        context: &async_graphql::Context<'_>,
    ) -> FieldResult<Vec<DocumentCollection>> {
        Ok(context
            .data::<DataLoader<Database>>()?
            .loader()
            .chapter_breadcrumbs(self.path.clone())
            .await?)
    }
}

/// Input for creating an edited collection
#[derive(async_graphql::InputObject)]
pub struct CreateEditedCollectionInput {
    /// The title of the collection
    pub title: String,
    /// Description of the collection
    pub description: String,
    /// URL of the thumbnail image for the collection
    pub thumbnail_url: String,
}