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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use Error;
use dot::{Summary, SiteId};

macro_rules! crdt_impl2 {
    ($self_ident:ident,
     $state:ty,
     $state_static:ty,
     $state_ident:ident,
     $inner:ty,
     $op:ty,
     $local_op:ty,
     $local_value:ty,
    ) => {

        /// Returns the site id.
        pub fn site_id(&self) -> SiteId {
            self.site_id
        }

        #[doc(hidden)]
        pub fn summary(&self) -> &Summary {
            &self.summary
        }

        #[doc(hidden)]
        pub fn cached_ops(&self) -> &[$op] {
            &self.cached_ops
        }

        /// Returns a borrowed CRDT state.
        pub fn state(&self) -> $state {
            $state_ident {
                inner: Cow::Borrowed(&self.inner),
                summary: Cow::Borrowed(&self.summary),
            }
        }

        /// Returns an owned CRDT state of cloned values.
        pub fn clone_state(&self) -> $state_static {
            $state_ident {
                inner: Cow::Owned(self.inner.clone()),
                summary: Cow::Owned(self.summary.clone()),
            }
        }

        /// Consumes the CRDT and returns its state
        pub fn into_state(self) -> $state_static {
            $state_ident {
                inner: Cow::Owned(self.inner),
                summary: Cow::Owned(self.summary),
            }
        }

        /// Constructs a new CRDT from a state and optional site id.
        /// If the site id is present, it must be nonzero.
        pub fn from_state(state: $state, site_id: Option<SiteId>) -> Result<Self, Error> {
            let site_id = match site_id {
                None => 0,
                Some(0) => return Err(Error::InvalidSiteId),
                Some(s) => s,
            };

            Ok($self_ident{
                site_id,
                inner: state.inner.into_owned(),
                summary: state.summary.into_owned(),
                cached_ops: vec![],
            })
        }

        /// Returns the CRDT value's equivalent local value.
        pub fn local_value(&self) -> $local_value {
            self.inner.local_value()
        }

        /// Executes an op and returns the equivalent local op.
        /// This function assumes that the op always inserts values
        /// from the correct site. For untrusted ops, used `validate_and_execute_op`.
        pub fn execute_op(&mut self, op: $op) -> $local_op {
            for dot in op.inserted_dots() {
                self.summary.insert(dot);
            }
            self.inner.execute_op(op)
        }

        /// Validates that an op only inserts elements from a given site id,
        /// then executes the op and returns the equivalent local op.
        pub fn validate_and_execute_op(&mut self, op: $op, site_id: SiteId) -> Result<$local_op, Error> {
            op.validate(site_id)?;
            Ok(self.execute_op(op))
        }

        /// Merges a remote CRDT state into the CRDT. The remote
        /// CRDT state must have a site id.
        pub fn merge(&mut self, other: $state) -> Result<(), Error> {
            other.inner.validate_no_unassigned_sites()?;
            other.summary.validate_no_unassigned_sites()?;
            self.inner.merge(other.inner.into_owned(), &self.summary, &other.summary);
            self.summary.merge(&other.summary);
            Ok(())
        }

        /// Assigns a site id to the CRDT and returns any cached ops.
        /// If the CRDT already has a site id, it returns an error.
        pub fn add_site_id(&mut self, site_id: SiteId) -> Result<Vec<$op>, Error> {
            if self.site_id != 0 {
                return Err(Error::AlreadyHasSiteId);
            }

            self.site_id = site_id;
            self.inner.add_site_id(site_id);
            self.summary.add_site_id(site_id);
            Ok(::std::mem::replace(&mut self.cached_ops, vec![])
                .into_iter()
                .map(|mut op| { op.add_site_id(site_id); op})
                .collect())
        }

        fn after_op(&mut self, op: $op) -> Result<$op, Error> {
            if self.site_id == 0 {
                self.cached_ops.push(op);
                Err(Error::AwaitingSiteId)
            } else {
                Ok(op)
            }
        }
    }
}

pub(crate) trait NestedInner: Sized {
    fn nested_add_site_id(&mut self, site_id: SiteId);

    fn nested_validate_all(&self, site_id: SiteId) -> Result<(), Error>;

    fn nested_validate_no_unassigned_sites(&self) -> Result<(), Error>;

    fn nested_can_merge(&self, other: &Self) -> bool;

    fn nested_merge(&mut self, other: Self, summary: &Summary, other_summary: &Summary) -> Result<(), Error> {
        if self.nested_can_merge(&other) {
            self.nested_force_merge(other, summary, other_summary);
            Ok(())
        } else {
            Err(Error::CannotMerge)
        }
    }

    fn nested_force_merge(&mut self, other: Self, summary: &Summary, other_summary: &Summary);
}

pub(crate) trait NestedOp {
    fn nested_add_site_id(&mut self, site_id: SiteId);

    fn nested_validate(&self, site_id: SiteId) -> Result<(), Error>;
}