Skip to main content

post_archiver/importer/
author.rs

1use chrono::{DateTime, Utc};
2use rusqlite::params;
3
4use crate::{
5    error::Result,
6    manager::{PostArchiverConnection, PostArchiverManager, UpdateAuthor},
7    AuthorId, PlatformId,
8};
9
10impl<T> PostArchiverManager<T>
11where
12    T: PostArchiverConnection,
13{
14    /// Import an author into the archive.
15    ///
16    /// If the author already exists (by aliases), it updates their name, aliases, and updated date.
17    ///
18    /// # Errors
19    ///
20    /// Returns `Error` if there was an error accessing the database.
21    pub fn import_author(&self, author: UnsyncAuthor) -> Result<AuthorId> {
22        // find by aliases
23        let id = {
24            let mut found: Option<AuthorId> = None;
25            for alias in &author.aliases {
26                if let Some(id) = self.find_author_by_alias(&alias.source, alias.platform)? {
27                    found = Some(id);
28                    break;
29                }
30            }
31            found
32        };
33
34        let aliases = author
35            .aliases
36            .into_iter()
37            .map(|alias| (alias.source, alias.platform, alias.link))
38            .collect::<Vec<_>>();
39
40        match id {
41            Some(id) => {
42                let b = self.bind(id);
43                let mut upd = UpdateAuthor::default().name(author.name);
44                if let Some(updated) = author.updated {
45                    upd = upd.updated(updated);
46                }
47                b.update(upd)?;
48
49                b.add_aliases(aliases)?;
50
51                Ok(id)
52            }
53            None => {
54                // insert
55                let mut stmt = self.conn().prepare_cached(
56                    "INSERT INTO authors (name, updated) VALUES (?, ?) RETURNING id",
57                )?;
58                let id: AuthorId = stmt.query_row(
59                    params![author.name, author.updated.unwrap_or_else(Utc::now)],
60                    |row| row.get(0),
61                )?;
62
63                self.bind(id).add_aliases(aliases)?;
64
65                Ok(id)
66            }
67        }
68    }
69}
70
71/// Represents an author that is not yet synced with the archive.
72#[derive(Debug, Clone, PartialEq, Eq, Hash)]
73pub struct UnsyncAuthor {
74    name: String,
75    updated: Option<DateTime<Utc>>,
76    aliases: Vec<UnsyncAlias>,
77}
78
79impl UnsyncAuthor {
80    pub fn new(name: String) -> Self {
81        Self {
82            name,
83            updated: None,
84            aliases: Vec::new(),
85        }
86    }
87
88    pub fn name(self, name: String) -> Self {
89        Self { name, ..self }
90    }
91    pub fn aliases(self, aliases: Vec<UnsyncAlias>) -> Self {
92        Self { aliases, ..self }
93    }
94    pub fn updated(self, updated: Option<DateTime<Utc>>) -> Self {
95        Self { updated, ..self }
96    }
97    /// Import it into the archive.
98    ///
99    /// If the author already exists (by aliases), it updates their name, aliases, and updated date.
100    ///
101    /// # Errors
102    ///
103    /// Returns `Error` if there was an error accessing the database.
104    pub fn sync<T>(self, manager: &PostArchiverManager<T>) -> Result<AuthorId>
105    where
106        T: PostArchiverConnection,
107    {
108        manager.import_author(self)
109    }
110}
111
112/// Represents an alias for an author that is not yet synced with the archive.
113///
114/// This is used to track different platforms or identifiers for the same author.
115/// It can include links to their profiles or other relevant information.
116#[derive(Debug, Clone, PartialEq, Eq, Hash)]
117pub struct UnsyncAlias {
118    pub source: String,
119    pub platform: PlatformId,
120    pub link: Option<String>,
121}
122
123impl UnsyncAlias {
124    pub fn new(platform: PlatformId, source: String) -> Self {
125        Self {
126            link: None,
127            source,
128            platform,
129        }
130    }
131
132    pub fn source<S: Into<String>>(mut self, source: S) -> Self {
133        self.source = source.into();
134        self
135    }
136
137    pub fn platform(mut self, platform: PlatformId) -> Self {
138        self.platform = platform;
139        self
140    }
141
142    pub fn link<S: Into<String>>(mut self, link: S) -> Self {
143        self.link = Some(link.into());
144        self
145    }
146}