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
/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

//! This module contains everything needed to contruct a new [`Entry`] (via [`TransformedEntry`]) and [`Message`] (via [`TransformedMessage`]) after parsing, optionally using previous [`Entry's`](`Entry`) data if requested

use crate::{
	entry::{Entry, EntryId},
	sink::message::{Media, Message},
};

use url::Url;

/// An [`Entry`] mirror that can be converted to [`Entry`] but whose fields can be chosen to inherit old entry's values on [`None`]
/// Refer to [`Entry`] for more docs on itself and each field
#[allow(missing_docs)]
#[derive(Default, Debug)]
pub struct TransformedEntry {
	pub id: TransformResult<EntryId>,
	pub reply_to: TransformResult<EntryId>,
	pub raw_contents: TransformResult<String>,
	pub msg: TransformedMessage,
}

/// A [`Message`] mirror that can be converted to [`Message`] but whose fields can be chosen to inherit old message's values on [`None`]
/// Refer to [`Message`] for more docs on itself and each field
#[allow(missing_docs)]
#[derive(Default, Debug)]
pub struct TransformedMessage {
	pub title: TransformResult<String>,
	pub body: TransformResult<String>,
	pub link: TransformResult<Url>,
	pub media: TransformResult<Vec<Media>>,
}

/// An [`Option`] wrapper that can specify what to replace the value with if it's [`None`]
#[derive(Debug)]
pub enum TransformResult<T> {
	/// Use previous value if None, and a new one if Some
	Old(Option<T>),
	/// Use empty value if None, and a new one if Some
	New(Option<T>),
}

impl TransformedEntry {
	/// Transform [`TransformedEntry`] into a new [`Entry`], using `old_entry`'s fields as fallback if needed
	#[must_use]
	pub fn into_entry(self, old_entry: &Entry) -> Entry {
		Entry {
			id: self.id.get(|| old_entry.id.clone()),
			reply_to: self.reply_to.get(|| old_entry.reply_to.clone()),
			raw_contents: self.raw_contents.get(|| old_entry.raw_contents.clone()),
			msg: self.msg.into_message(&old_entry.msg),
		}
	}
}

impl TransformedMessage {
	/// Transform [`TransformedMessage`] into a new [`Message`], using `old_msg`'s fields as fallback if needed
	#[must_use]
	pub fn into_message(self, old_msg: &Message) -> Message {
		Message {
			title: self.title.get(|| old_msg.title.clone()),
			body: self.body.get(|| old_msg.body.clone()),
			link: self.link.get(|| old_msg.link.clone()),
			media: self.media.get(|| old_msg.media.clone()),
		}
	}
}

impl<T> TransformResult<T> {
	/// Combine new value with the old value using new value's merge stradegy
	pub fn get<F>(self, old_val: F) -> Option<T>
	where
		F: FnOnce() -> Option<T>,
	{
		match self {
			Self::Old(val) => val.or_else(old_val),
			Self::New(val) => val,
		}
	}
}

impl<T> Default for TransformResult<T> {
	fn default() -> Self {
		TransformResult::Old(None)
	}
}