Merge based on file type
This commit is contained in:
parent
47a34ed7e2
commit
a9227fa5bb
4 changed files with 60 additions and 15 deletions
|
|
@ -19,11 +19,17 @@ pub mod errors;
|
||||||
/// Encode binary data for easy transport over HTTP. Inverse of
|
/// Encode binary data for easy transport over HTTP. Inverse of
|
||||||
/// `base64_to_bytes`.
|
/// `base64_to_bytes`.
|
||||||
#[wasm_bindgen(js_name = bytesToBase64)]
|
#[wasm_bindgen(js_name = bytesToBase64)]
|
||||||
pub fn bytes_to_base64(input: &[u8]) -> String { STANDARD.encode(input) }
|
pub fn bytes_to_base64(input: &[u8]) -> String {
|
||||||
|
set_panic_hook();
|
||||||
|
|
||||||
|
STANDARD.encode(input)
|
||||||
|
}
|
||||||
|
|
||||||
/// Inverse of `bytes_to_base64`.
|
/// Inverse of `bytes_to_base64`.
|
||||||
#[wasm_bindgen(js_name = base64ToBytes)]
|
#[wasm_bindgen(js_name = base64ToBytes)]
|
||||||
pub fn base64_to_bytes(input: &str) -> Result<Vec<u8>, SyncLibError> {
|
pub fn base64_to_bytes(input: &str) -> Result<Vec<u8>, SyncLibError> {
|
||||||
|
set_panic_hook();
|
||||||
|
|
||||||
STANDARD.decode(input).map_err(SyncLibError::from)
|
STANDARD.decode(input).map_err(SyncLibError::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -32,6 +38,8 @@ pub fn base64_to_bytes(input: &str) -> Result<Vec<u8>, SyncLibError> {
|
||||||
/// documents is binary.
|
/// documents is binary.
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
pub fn merge(parent: &[u8], left: &[u8], right: &[u8]) -> Vec<u8> {
|
pub fn merge(parent: &[u8], left: &[u8], right: &[u8]) -> Vec<u8> {
|
||||||
|
set_panic_hook();
|
||||||
|
|
||||||
if is_binary(parent) || is_binary(left) || is_binary(right) {
|
if is_binary(parent) || is_binary(left) || is_binary(right) {
|
||||||
right.to_vec()
|
right.to_vec()
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -47,6 +55,8 @@ pub fn merge(parent: &[u8], left: &[u8], right: &[u8]) -> Vec<u8> {
|
||||||
/// WASM wrapper around `reconcile::reconcile` for text merging.
|
/// WASM wrapper around `reconcile::reconcile` for text merging.
|
||||||
#[wasm_bindgen(js_name = mergeText)]
|
#[wasm_bindgen(js_name = mergeText)]
|
||||||
pub fn merge_text(parent: &str, left: &str, right: &str) -> String {
|
pub fn merge_text(parent: &str, left: &str, right: &str) -> String {
|
||||||
|
set_panic_hook();
|
||||||
|
|
||||||
reconcile::reconcile(parent, left, right)
|
reconcile::reconcile(parent, left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -54,6 +64,8 @@ pub fn merge_text(parent: &str, left: &str, right: &str) -> String {
|
||||||
/// content.
|
/// content.
|
||||||
#[wasm_bindgen(js_name = isBinary)]
|
#[wasm_bindgen(js_name = isBinary)]
|
||||||
pub fn is_binary(data: &[u8]) -> bool {
|
pub fn is_binary(data: &[u8]) -> bool {
|
||||||
|
set_panic_hook();
|
||||||
|
|
||||||
if data.iter().any(|&b| b == 0) {
|
if data.iter().any(|&b| b == 0) {
|
||||||
// Even though the NUL character is valid in UTF-8, it's highly suspicious in
|
// Even though the NUL character is valid in UTF-8, it's highly suspicious in
|
||||||
// human-readable text.
|
// human-readable text.
|
||||||
|
|
@ -63,10 +75,18 @@ pub fn is_binary(data: &[u8]) -> bool {
|
||||||
std::str::from_utf8(data).is_err()
|
std::str::from_utf8(data).is_err()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up panic hook for better error messages in the browser console.
|
/// We don't want to supporte merging structured data like JSON, YAML, etc.
|
||||||
#[cfg(feature = "console_error_panic_hook")]
|
#[wasm_bindgen(js_name = isFileTypeMergable)]
|
||||||
#[wasm_bindgen(js_name = setPanicHook)]
|
pub fn is_file_type_mergable(path_or_file_name: &str) -> bool {
|
||||||
pub fn set_panic_hook() {
|
set_panic_hook();
|
||||||
|
|
||||||
|
let file_extension = path_or_file_name.split('.').last().unwrap_or_default();
|
||||||
|
|
||||||
|
matches!(file_extension.to_lowercase().as_str(), "md" | "txt")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_panic_hook() {
|
||||||
// https://github.com/rustwasm/console_error_panic_hook#readme
|
// https://github.com/rustwasm/console_error_panic_hook#readme
|
||||||
|
#[cfg(feature = "console_error_panic_hook")]
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,3 @@
|
||||||
//! Test suite for the Web and headless browsers.
|
|
||||||
|
|
||||||
use insta::assert_debug_snapshot;
|
use insta::assert_debug_snapshot;
|
||||||
use sync_lib::*;
|
use sync_lib::*;
|
||||||
use wasm_bindgen_test::*;
|
use wasm_bindgen_test::*;
|
||||||
|
|
@ -44,3 +42,21 @@ fn test_is_binary() {
|
||||||
assert!(is_binary(&[0, 12]));
|
assert!(is_binary(&[0, 12]));
|
||||||
assert!(!is_binary(b"hello"));
|
assert!(!is_binary(b"hello"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test(unsupported = test)]
|
||||||
|
fn test_is_binary_empty() {
|
||||||
|
assert!(!is_binary(b""));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen_test(unsupported = test)]
|
||||||
|
fn test_is_file_type_mergable() {
|
||||||
|
assert!(is_file_type_mergable(".md"));
|
||||||
|
assert!(is_file_type_mergable("hi.md"));
|
||||||
|
assert!(is_file_type_mergable("my/path/to/my/document.md"));
|
||||||
|
assert!(is_file_type_mergable("hi.MD"));
|
||||||
|
assert!(is_file_type_mergable("my/path/to/my/DOCUMENT.MD"));
|
||||||
|
|
||||||
|
assert!(!is_file_type_mergable(".json"));
|
||||||
|
assert!(!is_file_type_mergable("HELLO.JSON"));
|
||||||
|
assert!(!is_file_type_mergable("my/config.yml"));
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use chrono::{DateTime, Utc};
|
||||||
use log::info;
|
use log::info;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sync_lib::{base64_to_bytes, merge};
|
use sync_lib::{base64_to_bytes, is_file_type_mergable, merge};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
app_state::AppState,
|
app_state::AppState,
|
||||||
|
|
@ -127,11 +127,15 @@ async fn internal_create_document(
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let merged_content = merge(
|
let merged_content = if is_file_type_mergable(&sanitized_relative_path) {
|
||||||
&[], // the empty string is the first common parent of the two documents,
|
merge(
|
||||||
&existing_version.content,
|
&[], // the empty string is the first common parent of the two documents,
|
||||||
&content,
|
&existing_version.content,
|
||||||
);
|
&content,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
content
|
||||||
|
};
|
||||||
|
|
||||||
let new_version = StoredDocumentVersion {
|
let new_version = StoredDocumentVersion {
|
||||||
vault_id,
|
vault_id,
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use chrono::{DateTime, Utc};
|
||||||
use log::info;
|
use log::info;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sync_lib::{base64_to_bytes, merge};
|
use sync_lib::{base64_to_bytes, is_file_type_mergable, merge};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
app_state::AppState,
|
app_state::AppState,
|
||||||
|
|
@ -155,7 +155,12 @@ async fn internal_update_document(
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let merged_content = merge(&parent_document.content, &latest_version.content, &content);
|
let merged_content = if is_file_type_mergable(&sanitized_relative_path) {
|
||||||
|
merge(&parent_document.content, &latest_version.content, &content)
|
||||||
|
} else {
|
||||||
|
content.clone()
|
||||||
|
};
|
||||||
|
|
||||||
let is_different_from_request_content = merged_content != content;
|
let is_different_from_request_content = merged_content != content;
|
||||||
|
|
||||||
// We can only update the relative path if we're the first one to do so
|
// We can only update the relative path if we're the first one to do so
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue