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
// Copyright © 2022 The Radicle Link Contributors

use nonempty::NonEmpty;
use radicle_crypto::PublicKey;

use crate::Embed;
use crate::Evaluate;
use crate::Store;

use super::*;

/// The metadata required for creating a new [`CollaborativeObject`].
pub struct Create {
    /// The CRDT history to initialize this object with.
    pub contents: NonEmpty<Vec<u8>>,
    /// The typename for this object.
    pub type_name: TypeName,
    /// The message to add when creating this object.
    pub message: String,
    /// Embedded content.
    pub embeds: Vec<Embed>,
    /// COB version.
    pub version: Version,
}

impl Create {
    fn template(self) -> change::Template<git_ext::Oid> {
        change::Template {
            type_name: self.type_name,
            tips: Vec::new(),
            message: self.message,
            embeds: self.embeds,
            contents: self.contents,
        }
    }
}

/// Create a new [`CollaborativeObject`].
///
/// The `storage` is the backing storage for storing
/// [`crate::Entry`]s at content-addressable locations. Please see
/// [`Store`] for further information.
///
/// The `signer` is expected to be a cryptographic signing key. This
/// ensures that the objects origin is cryptographically verifiable.
///
/// The `resource` is the parent of this object, for example a
/// software project. Its content-address is stored in the object's
/// history.
///
/// The `identifier` is a unqiue id that is passed through to the
/// [`crate::object::Storage`].
///
/// The `args` are the metadata for this [`CollaborativeObject`]. See
/// [`Create`] for further information.
pub fn create<T, S, G>(
    storage: &S,
    signer: &G,
    resource: Option<Oid>,
    related: Vec<Oid>,
    identifier: &PublicKey,
    args: Create,
) -> Result<CollaborativeObject<T>, error::Create>
where
    T: Evaluate<S>,
    S: Store,
    G: crypto::Signer,
{
    let type_name = args.type_name.clone();
    let version = args.version;
    let init_change = storage
        .store(resource, related, signer, args.template())
        .map_err(error::Create::from)?;
    let object_id = init_change.id().into();
    let object = T::init(&init_change, storage).map_err(error::Create::evaluate)?;

    storage
        .update(identifier, &type_name, &object_id, &object_id)
        .map_err(|err| error::Create::Refs { err: Box::new(err) })?;

    let history = History::new_from_root(init_change);

    Ok(CollaborativeObject {
        manifest: Manifest::new(type_name, version),
        history,
        object,
        id: object_id,
    })
}