356 lines
9.8 KiB
Rust
356 lines
9.8 KiB
Rust
use automerge as am;
|
|
use std::cell::RefCell;
|
|
|
|
use crate::byte_span::AMbyteSpan;
|
|
use crate::change_hashes::AMchangeHashes;
|
|
use crate::result::{to_result, AMresult};
|
|
|
|
macro_rules! to_change {
|
|
($handle:expr) => {{
|
|
let handle = $handle.as_ref();
|
|
match handle {
|
|
Some(b) => b,
|
|
None => return AMresult::err("Invalid AMchange pointer").into(),
|
|
}
|
|
}};
|
|
}
|
|
|
|
/// \struct AMchange
|
|
/// \installed_headerfile
|
|
/// \brief A group of operations performed by an actor.
|
|
#[derive(Eq, PartialEq)]
|
|
pub struct AMchange {
|
|
body: *mut am::Change,
|
|
changehash: RefCell<Option<am::ChangeHash>>,
|
|
}
|
|
|
|
impl AMchange {
|
|
pub fn new(change: &mut am::Change) -> Self {
|
|
Self {
|
|
body: change,
|
|
changehash: Default::default(),
|
|
}
|
|
}
|
|
|
|
pub fn message(&self) -> AMbyteSpan {
|
|
if let Some(message) = unsafe { (*self.body).message() } {
|
|
return message.as_str().as_bytes().into();
|
|
}
|
|
Default::default()
|
|
}
|
|
|
|
pub fn hash(&self) -> AMbyteSpan {
|
|
let mut changehash = self.changehash.borrow_mut();
|
|
if let Some(changehash) = changehash.as_ref() {
|
|
changehash.into()
|
|
} else {
|
|
let hash = unsafe { (*self.body).hash() };
|
|
let ptr = changehash.insert(hash);
|
|
AMbyteSpan {
|
|
src: ptr.0.as_ptr(),
|
|
count: hash.as_ref().len(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AsMut<am::Change> for AMchange {
|
|
fn as_mut(&mut self) -> &mut am::Change {
|
|
unsafe { &mut *self.body }
|
|
}
|
|
}
|
|
|
|
impl AsRef<am::Change> for AMchange {
|
|
fn as_ref(&self) -> &am::Change {
|
|
unsafe { &*self.body }
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the first referenced actor identifier in a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \return A pointer to an `AMresult` struct containing a pointer to an
|
|
/// `AMactorId` struct.
|
|
/// \warning The returned `AMresult` struct must be deallocated with `AMfree()`
|
|
/// in order to prevent a memory leak.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeActorId(change: *const AMchange) -> *mut AMresult {
|
|
let change = to_change!(change);
|
|
to_result(Ok::<am::ActorId, am::AutomergeError>(
|
|
change.as_ref().actor_id().clone(),
|
|
))
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Compresses the raw bytes of a change.
|
|
///
|
|
/// \param[in,out] change A pointer to an `AMchange` struct.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeCompress(change: *mut AMchange) {
|
|
if let Some(change) = change.as_mut() {
|
|
let _ = change.as_mut().bytes();
|
|
};
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the dependencies of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A pointer to an `AMchangeHashes` struct or `NULL`.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeDeps(change: *const AMchange) -> AMchangeHashes {
|
|
match change.as_ref() {
|
|
Some(change) => AMchangeHashes::new(change.as_ref().deps()),
|
|
None => Default::default(),
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the extra bytes of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return An `AMbyteSpan` struct.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeExtraBytes(change: *const AMchange) -> AMbyteSpan {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().extra_bytes().into()
|
|
} else {
|
|
Default::default()
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Loads a sequence of bytes into a change.
|
|
///
|
|
/// \param[in] src A pointer to an array of bytes.
|
|
/// \param[in] count The number of bytes in \p src to load.
|
|
/// \return A pointer to an `AMresult` struct containing an `AMchange` struct.
|
|
/// \pre \p src `!= NULL`.
|
|
/// \pre `0 <` \p count `<= sizeof(`\p src`)`.
|
|
/// \warning The returned `AMresult` struct must be deallocated with `AMfree()`
|
|
/// in order to prevent a memory leak.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// src must be a byte array of size `>= count`
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeFromBytes(src: *const u8, count: usize) -> *mut AMresult {
|
|
let mut data = Vec::new();
|
|
data.extend_from_slice(std::slice::from_raw_parts(src, count));
|
|
to_result(am::Change::from_bytes(data))
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the hash of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A change hash as an `AMbyteSpan` struct.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeHash(change: *const AMchange) -> AMbyteSpan {
|
|
match change.as_ref() {
|
|
Some(change) => change.hash(),
|
|
None => Default::default(),
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Tests the emptiness of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A boolean.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeIsEmpty(change: *const AMchange) -> bool {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().is_empty()
|
|
} else {
|
|
true
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the maximum operation index of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A 64-bit unsigned integer.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeMaxOp(change: *const AMchange) -> u64 {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().max_op()
|
|
} else {
|
|
u64::MAX
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the message of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A UTF-8 string view as an `AMbyteSpan` struct.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeMessage(change: *const AMchange) -> AMbyteSpan {
|
|
if let Some(change) = change.as_ref() {
|
|
return change.message();
|
|
};
|
|
Default::default()
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the index of a change in the changes from an actor.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A 64-bit unsigned integer.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeSeq(change: *const AMchange) -> u64 {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().seq()
|
|
} else {
|
|
u64::MAX
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the size of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A 64-bit unsigned integer.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeSize(change: *const AMchange) -> usize {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().len()
|
|
} else {
|
|
0
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the start operation index of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A 64-bit unsigned integer.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeStartOp(change: *const AMchange) -> u64 {
|
|
if let Some(change) = change.as_ref() {
|
|
u64::from(change.as_ref().start_op())
|
|
} else {
|
|
u64::MAX
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the commit time of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return A 64-bit signed integer.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeTime(change: *const AMchange) -> i64 {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().timestamp()
|
|
} else {
|
|
i64::MAX
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Gets the raw bytes of a change.
|
|
///
|
|
/// \param[in] change A pointer to an `AMchange` struct.
|
|
/// \return An `AMbyteSpan` struct.
|
|
/// \pre \p change `!= NULL`.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// change must be a valid pointer to an AMchange
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeRawBytes(change: *const AMchange) -> AMbyteSpan {
|
|
if let Some(change) = change.as_ref() {
|
|
change.as_ref().raw_bytes().into()
|
|
} else {
|
|
Default::default()
|
|
}
|
|
}
|
|
|
|
/// \memberof AMchange
|
|
/// \brief Loads a document into a sequence of changes.
|
|
///
|
|
/// \param[in] src A pointer to an array of bytes.
|
|
/// \param[in] count The number of bytes in \p src to load.
|
|
/// \return A pointer to an `AMresult` struct containing a sequence of
|
|
/// `AMchange` structs.
|
|
/// \pre \p src `!= NULL`.
|
|
/// \pre `0 <` \p count `<= sizeof(`\p src`)`.
|
|
/// \warning The returned `AMresult` struct must be deallocated with `AMfree()`
|
|
/// in order to prevent a memory leak.
|
|
/// \internal
|
|
///
|
|
/// # Safety
|
|
/// src must be a byte array of size `>= count`
|
|
#[no_mangle]
|
|
pub unsafe extern "C" fn AMchangeLoadDocument(src: *const u8, count: usize) -> *mut AMresult {
|
|
let mut data = Vec::new();
|
|
data.extend_from_slice(std::slice::from_raw_parts(src, count));
|
|
to_result::<Result<Vec<am::Change>, _>>(
|
|
am::Automerge::load(&data)
|
|
.and_then(|d| d.get_changes(&[]).map(|c| c.into_iter().cloned().collect())),
|
|
)
|
|
}
|