dubp_documents/traits/
text.rs

1//  Copyright (C) 2020  Éloïs SANCHEZ.
2//
3// This program is free software: you can redistribute it and/or modify
4// it under the terms of the GNU Affero General Public License as
5// published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7//
8// This program is distributed in the hope that it will be useful,
9// but WITHOUT ANY WARRANTY; without even the implied warranty of
10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11// GNU Affero General Public License for more details.
12//
13// You should have received a copy of the GNU Affero General Public License
14// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
16//! Define the Text Document Traits.
17
18use crate::*;
19
20#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
21/// Contains a document in full or compact format
22pub enum TextDocumentFormat<D: TextDocument> {
23    /// Complete format (Allows to check the validity of the signature)
24    Complete(D),
25    /// Format present in the blocks (does not always allow to verify the signature)
26    Compact(D::CompactTextDocument_),
27}
28
29impl<D: TextDocument> TextDocumentFormat<D> {
30    /// To compact document
31    pub fn to_compact_document(&self) -> Cow<D::CompactTextDocument_> {
32        match *self {
33            TextDocumentFormat::Complete(ref doc) => doc.to_compact_document(),
34            TextDocumentFormat::Compact(ref compact_doc) => Cow::Borrowed(compact_doc),
35        }
36    }
37}
38
39/// Trait for a compact text document.
40pub trait CompactTextDocument: Sized + Clone + PartialEq {
41    /// Generate document compact text.
42    /// the compact format is the one used in the blocks.
43    ///
44    /// - Don't contains leading signatures
45    /// - Contains line breaks on all line.
46    fn as_compact_text(&self) -> String;
47}
48
49impl<D: TextDocument> CompactTextDocument for TextDocumentFormat<D> {
50    fn as_compact_text(&self) -> String {
51        match *self {
52            TextDocumentFormat::Complete(ref doc) => doc.generate_compact_text(),
53            TextDocumentFormat::Compact(ref doc) => doc.as_compact_text(),
54        }
55    }
56}
57
58/// Trait for a text document.
59pub trait TextDocument: Document {
60    /// Type of associated compact document.
61    type CompactTextDocument_: CompactTextDocument;
62
63    /// Return document as text without leading signatures.
64    fn as_text(&self) -> &str;
65
66    /// Return document as text with leading signatures.
67    fn as_text_with_signatures(&self) -> String {
68        let mut text = self.as_text().to_string();
69
70        for sig in self.signatures() {
71            text.push_str(&sig.to_base64());
72            text.push('\n');
73        }
74        text.pop(); // remove the last line break
75
76        text
77    }
78
79    /// Generate compact document.
80    /// the compact format is the one used in the blocks.
81    /// - Don't contains leading signatures
82    fn to_compact_document(&self) -> Cow<Self::CompactTextDocument_>;
83
84    /// Generate document compact text.
85    /// the compact format is the one used in the blocks.
86    ///
87    /// - Don't contains leading signatures
88    /// - Contains line breaks on all line.
89    fn generate_compact_text(&self) -> String {
90        self.to_compact_document().as_compact_text()
91    }
92}
93
94pub type StringAndSmallVec1<T> = (String, SmallVec<[T; 1]>);
95
96/// Trait for a V10 document builder.
97pub trait TextDocumentBuilder {
98    /// Type of the builded document.
99    type Document: Document;
100    /// Type of the signator signing the documents.
101    type Signator: Signator<PublicKey = <Self::Document as Document>::PublicKey>;
102
103    /// Generate document text.
104    ///
105    /// - Don't contains leading signatures
106    /// - Contains line breaks on all line.
107    fn generate_text(&self) -> String;
108
109    /// Generate final document with signatures, and also return them in an array.
110    ///
111    /// Returns :
112    ///
113    /// - Text without signatures
114    /// - Signatures
115    fn build_signed_text(
116        &self,
117        signators: Vec<Self::Signator>,
118    ) -> StringAndSmallVec1<<<Self::Document as Document>::PublicKey as PublicKey>::Signature> {
119        let text = self.generate_text();
120
121        let signatures: SmallVec<_> = {
122            let text_bytes = text.as_bytes();
123            signators
124                .iter()
125                .map(|signator| signator.sign(text_bytes))
126                .collect()
127        };
128
129        (text, signatures)
130    }
131
132    /// Build a document with provided text and signatures.
133    fn build_with_text_and_sigs(
134        self,
135        text: String,
136        signatures: SmallVec<
137            [<<Self::Document as Document>::PublicKey as PublicKey>::Signature; 1],
138        >,
139    ) -> Self::Document;
140}
141
142impl<T> DocumentBuilder for T
143where
144    T: TextDocumentBuilder,
145{
146    type Document = <Self as TextDocumentBuilder>::Document;
147    type Signator = <Self as TextDocumentBuilder>::Signator;
148
149    fn build_and_sign(self, signators: Vec<Self::Signator>) -> Self::Document {
150        let (text, signatures) = self.build_signed_text(signators);
151        self.build_with_text_and_sigs(text, signatures)
152    }
153
154    fn build_with_signature(
155        self,
156        signatures: SmallVec<
157            [<<Self::Document as Document>::PublicKey as PublicKey>::Signature; 1],
158        >,
159    ) -> Self::Document {
160        let text = self.generate_text();
161        self.build_with_text_and_sigs(text, signatures)
162    }
163}