Renamed Value to JsonValue to make things a little more clear

This commit is contained in:
Dave Hrycyszyn
2024-05-30 15:45:38 +01:00
parent 0733e12539
commit 3120ceee5d
7 changed files with 108 additions and 102 deletions

View File

@@ -2,7 +2,7 @@
extern crate test;
use bft_json_crdt::{
json_crdt::Value, keypair::make_author, list_crdt::ListCrdt, op::Op, op::ROOT_ID,
json_crdt::JsonValue, keypair::make_author, list_crdt::ListCrdt, op::Op, op::ROOT_ID,
};
use rand::seq::SliceRandom;
use test::Bencher;
@@ -35,7 +35,7 @@ fn bench_insert_many_agents_conflicts(b: &mut Bencher) {
const N: u8 = 50;
let mut rng = rand::thread_rng();
let mut crdts: Vec<ListCrdt<i64>> = Vec::with_capacity(N as usize);
let mut logs: Vec<Op<Value>> = Vec::new();
let mut logs: Vec<Op<JsonValue>> = Vec::new();
for i in 0..N {
let list = ListCrdt::new(make_author(i), vec![]);
crdts.push(list);

View File

@@ -90,8 +90,8 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream {
let expanded = quote! {
impl #impl_generics #crate_name::json_crdt::CrdtNodeFromValue for #ident #ty_generics #where_clause {
fn node_from(value: #crate_name::json_crdt::Value, id: #crate_name::keypair::AuthorId, path: Vec<#crate_name::op::PathSegment>) -> Result<Self, String> {
if let #crate_name::json_crdt::Value::Object(mut obj) = value {
fn node_from(value: #crate_name::json_crdt::JsonValue, id: #crate_name::keypair::AuthorId, path: Vec<#crate_name::op::PathSegment>) -> Result<Self, String> {
if let #crate_name::json_crdt::JsonValue::Object(mut obj) = value {
Ok(#ident {
path: path.clone(),
id,
@@ -119,7 +119,7 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream {
}
impl #impl_generics #crate_name::json_crdt::CrdtNode for #ident #ty_generics #where_clause {
fn apply(&mut self, op: #crate_name::op::Op<#crate_name::json_crdt::Value>) -> #crate_name::json_crdt::OpState {
fn apply(&mut self, op: #crate_name::op::Op<#crate_name::json_crdt::JsonValue>) -> #crate_name::json_crdt::OpState {
let path = op.path.clone();
let author = op.id.clone();
if !#crate_name::op::ensure_subpath(&self.path, &op.path) {
@@ -143,10 +143,10 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream {
}
}
fn view(&self) -> #crate_name::json_crdt::Value {
fn view(&self) -> #crate_name::json_crdt::JsonValue {
let mut view_map = std::collections::HashMap::new();
#(view_map.insert(#ident_strings.to_string(), self.#ident_literals.view().into());)*
#crate_name::json_crdt::Value::Object(view_map)
#crate_name::json_crdt::JsonValue::Object(view_map)
}
fn new(id: #crate_name::keypair::AuthorId, path: Vec<#crate_name::op::PathSegment>) -> Self {

View File

@@ -23,9 +23,9 @@ pub trait CrdtNode: CrdtNodeFromValue + Hashable + Clone {
/// Create a new CRDT of this type
fn new(id: AuthorId, path: Vec<PathSegment>) -> Self;
/// Apply an operation to this CRDT, forwarding if necessary
fn apply(&mut self, op: Op<Value>) -> OpState;
fn apply(&mut self, op: Op<JsonValue>) -> OpState;
/// Get a JSON representation of the value in this node
fn view(&self) -> Value;
fn view(&self) -> JsonValue;
}
/// Enum representing possible outcomes of applying an operation to a CRDT
@@ -60,14 +60,14 @@ pub enum OpState {
}
/// The following types can be used as a 'terminal' type in CRDTs
pub trait MarkPrimitive: Into<Value> + Default {}
pub trait MarkPrimitive: Into<JsonValue> + Default {}
impl MarkPrimitive for bool {}
impl MarkPrimitive for i32 {}
impl MarkPrimitive for i64 {}
impl MarkPrimitive for f64 {}
impl MarkPrimitive for char {}
impl MarkPrimitive for String {}
impl MarkPrimitive for Value {}
impl MarkPrimitive for JsonValue {}
/// Implement CrdtNode for non-CRDTs
/// This is a stub implementation so most functions don't do anything/log an error
@@ -75,11 +75,11 @@ impl<T> CrdtNode for T
where
T: CrdtNodeFromValue + MarkPrimitive + Hashable + Clone,
{
fn apply(&mut self, _op: Op<Value>) -> OpState {
fn apply(&mut self, _op: Op<JsonValue>) -> OpState {
OpState::ErrApplyOnPrimitive
}
fn view(&self) -> Value {
fn view(&self) -> JsonValue {
self.to_owned().into()
}
@@ -113,7 +113,7 @@ pub struct SignedOp {
author: AuthorId,
/// Signed hash using priv key of author. Effectively [`OpID`] Use this as the ID to figure out what has been delivered already
pub signed_digest: SignedDigest,
pub inner: Op<Value>,
pub inner: Op<JsonValue>,
/// List of causal dependencies
pub depends_on: Vec<SignedDigest>,
}
@@ -245,26 +245,26 @@ impl<T: CrdtNode + DebugView> BaseCrdt<T> {
/// An enum representing a JSON value
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
pub enum JsonValue {
Null,
Bool(bool),
Number(f64),
String(String),
Array(Vec<Value>),
Object(HashMap<String, Value>),
Array(Vec<JsonValue>),
Object(HashMap<String, JsonValue>),
}
impl Display for Value {
impl Display for JsonValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Value::Null => "null".to_string(),
Value::Bool(b) => b.to_string(),
Value::Number(n) => n.to_string(),
Value::String(s) => format!("\"{s}\""),
Value::Array(arr) => {
JsonValue::Null => "null".to_string(),
JsonValue::Bool(b) => b.to_string(),
JsonValue::Number(n) => n.to_string(),
JsonValue::String(s) => format!("\"{s}\""),
JsonValue::Array(arr) => {
if arr.len() > 1 {
format!(
"[\n{}\n]",
@@ -283,7 +283,7 @@ impl Display for Value {
)
}
}
Value::Object(obj) => format!(
JsonValue::Object(obj) => format!(
"{{ {} }}",
obj.iter()
.map(|(k, v)| format!(" \"{k}\": {v}"))
@@ -295,7 +295,7 @@ impl Display for Value {
}
}
impl Default for Value {
impl Default for JsonValue {
fn default() -> Self {
Self::Null
}
@@ -303,17 +303,19 @@ impl Default for Value {
/// Allow easy conversion to and from serde's JSON format. This allows us to use the [`json!`]
/// macro
impl From<Value> for serde_json::Value {
fn from(value: Value) -> Self {
impl From<JsonValue> for serde_json::Value {
fn from(value: JsonValue) -> Self {
match value {
Value::Null => serde_json::Value::Null,
Value::Bool(x) => serde_json::Value::Bool(x),
Value::Number(x) => serde_json::Value::Number(serde_json::Number::from_f64(x).unwrap()),
Value::String(x) => serde_json::Value::String(x),
Value::Array(x) => {
JsonValue::Null => serde_json::Value::Null,
JsonValue::Bool(x) => serde_json::Value::Bool(x),
JsonValue::Number(x) => {
serde_json::Value::Number(serde_json::Number::from_f64(x).unwrap())
}
JsonValue::String(x) => serde_json::Value::String(x),
JsonValue::Array(x) => {
serde_json::Value::Array(x.iter().map(|a| a.clone().into()).collect())
}
Value::Object(x) => serde_json::Value::Object(
JsonValue::Object(x) => serde_json::Value::Object(
x.iter()
.map(|(k, v)| (k.clone(), v.clone().into()))
.collect(),
@@ -322,17 +324,17 @@ impl From<Value> for serde_json::Value {
}
}
impl From<serde_json::Value> for Value {
impl From<serde_json::Value> for JsonValue {
fn from(value: serde_json::Value) -> Self {
match value {
serde_json::Value::Null => Value::Null,
serde_json::Value::Bool(x) => Value::Bool(x),
serde_json::Value::Number(x) => Value::Number(x.as_f64().unwrap()),
serde_json::Value::String(x) => Value::String(x),
serde_json::Value::Null => JsonValue::Null,
serde_json::Value::Bool(x) => JsonValue::Bool(x),
serde_json::Value::Number(x) => JsonValue::Number(x.as_f64().unwrap()),
serde_json::Value::String(x) => JsonValue::String(x),
serde_json::Value::Array(x) => {
Value::Array(x.iter().map(|a| a.clone().into()).collect())
JsonValue::Array(x.iter().map(|a| a.clone().into()).collect())
}
serde_json::Value::Object(x) => Value::Object(
serde_json::Value::Object(x) => JsonValue::Object(
x.iter()
.map(|(k, v)| (k.clone(), v.clone().into()))
.collect(),
@@ -341,73 +343,73 @@ impl From<serde_json::Value> for Value {
}
}
impl Value {
impl JsonValue {
pub fn into_json(self) -> serde_json::Value {
self.into()
}
}
/// Conversions from primitive types to [`Value`]
impl From<bool> for Value {
impl From<bool> for JsonValue {
fn from(val: bool) -> Self {
Value::Bool(val)
JsonValue::Bool(val)
}
}
impl From<i64> for Value {
impl From<i64> for JsonValue {
fn from(val: i64) -> Self {
Value::Number(val as f64)
JsonValue::Number(val as f64)
}
}
impl From<i32> for Value {
impl From<i32> for JsonValue {
fn from(val: i32) -> Self {
Value::Number(val as f64)
JsonValue::Number(val as f64)
}
}
impl From<f64> for Value {
impl From<f64> for JsonValue {
fn from(val: f64) -> Self {
Value::Number(val)
JsonValue::Number(val)
}
}
impl From<String> for Value {
impl From<String> for JsonValue {
fn from(val: String) -> Self {
Value::String(val)
JsonValue::String(val)
}
}
impl From<char> for Value {
impl From<char> for JsonValue {
fn from(val: char) -> Self {
Value::String(val.into())
JsonValue::String(val.into())
}
}
impl<T> From<Option<T>> for Value
impl<T> From<Option<T>> for JsonValue
where
T: CrdtNode,
{
fn from(val: Option<T>) -> Self {
match val {
Some(x) => x.view(),
None => Value::Null,
None => JsonValue::Null,
}
}
}
impl<T> From<Vec<T>> for Value
impl<T> From<Vec<T>> for JsonValue
where
T: CrdtNode,
{
fn from(value: Vec<T>) -> Self {
Value::Array(value.iter().map(|x| x.view()).collect())
JsonValue::Array(value.iter().map(|x| x.view()).collect())
}
}
/// Fallibly create a CRDT Node from a JSON Value
pub trait CrdtNodeFromValue: Sized {
fn node_from(value: Value, id: AuthorId, path: Vec<PathSegment>) -> Result<Self, String>;
fn node_from(value: JsonValue, id: AuthorId, path: Vec<PathSegment>) -> Result<Self, String>;
}
/// Fallibly cast a JSON Value into a CRDT Node
@@ -416,7 +418,7 @@ pub trait IntoCrdtNode<T>: Sized {
}
/// [`CrdtNodeFromValue`] implies [`IntoCRDTNode<T>`]
impl<T> IntoCrdtNode<T> for Value
impl<T> IntoCrdtNode<T> for JsonValue
where
T: CrdtNodeFromValue,
{
@@ -426,16 +428,16 @@ where
}
/// Trivial conversion from Value to Value as CrdtNodeFromValue
impl CrdtNodeFromValue for Value {
fn node_from(value: Value, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
impl CrdtNodeFromValue for JsonValue {
fn node_from(value: JsonValue, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
Ok(value)
}
}
/// Conversions from primitives to CRDTs
impl CrdtNodeFromValue for bool {
fn node_from(value: Value, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let Value::Bool(x) = value {
fn node_from(value: JsonValue, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let JsonValue::Bool(x) = value {
Ok(x)
} else {
Err(format!("failed to convert {value:?} -> bool"))
@@ -444,8 +446,8 @@ impl CrdtNodeFromValue for bool {
}
impl CrdtNodeFromValue for f64 {
fn node_from(value: Value, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let Value::Number(x) = value {
fn node_from(value: JsonValue, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let JsonValue::Number(x) = value {
Ok(x)
} else {
Err(format!("failed to convert {value:?} -> f64"))
@@ -454,8 +456,8 @@ impl CrdtNodeFromValue for f64 {
}
impl CrdtNodeFromValue for i64 {
fn node_from(value: Value, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let Value::Number(x) = value {
fn node_from(value: JsonValue, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let JsonValue::Number(x) = value {
Ok(x as i64)
} else {
Err(format!("failed to convert {value:?} -> f64"))
@@ -464,8 +466,8 @@ impl CrdtNodeFromValue for i64 {
}
impl CrdtNodeFromValue for String {
fn node_from(value: Value, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let Value::String(x) = value {
fn node_from(value: JsonValue, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let JsonValue::String(x) = value {
Ok(x)
} else {
Err(format!("failed to convert {value:?} -> String"))
@@ -474,8 +476,8 @@ impl CrdtNodeFromValue for String {
}
impl CrdtNodeFromValue for char {
fn node_from(value: Value, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let Value::String(x) = value.clone() {
fn node_from(value: JsonValue, _id: AuthorId, _path: Vec<PathSegment>) -> Result<Self, String> {
if let JsonValue::String(x) = value.clone() {
x.chars().next().ok_or(format!(
"failed to convert {value:?} -> char: found a zero-length string"
))
@@ -489,7 +491,7 @@ impl<T> CrdtNodeFromValue for LwwRegisterCrdt<T>
where
T: CrdtNode,
{
fn node_from(value: Value, id: AuthorId, path: Vec<PathSegment>) -> Result<Self, String> {
fn node_from(value: JsonValue, id: AuthorId, path: Vec<PathSegment>) -> Result<Self, String> {
let mut crdt = LwwRegisterCrdt::new(id, path);
crdt.set(value);
Ok(crdt)
@@ -500,8 +502,8 @@ impl<T> CrdtNodeFromValue for ListCrdt<T>
where
T: CrdtNode,
{
fn node_from(value: Value, id: AuthorId, path: Vec<PathSegment>) -> Result<Self, String> {
if let Value::Array(arr) = value {
fn node_from(value: JsonValue, id: AuthorId, path: Vec<PathSegment>) -> Result<Self, String> {
if let JsonValue::Array(arr) = value {
let mut crdt = ListCrdt::new(id, path);
let result: Result<(), String> =
arr.into_iter().enumerate().try_for_each(|(i, val)| {
@@ -521,7 +523,7 @@ mod test {
use serde_json::json;
use crate::{
json_crdt::{add_crdt_fields, BaseCrdt, CrdtNode, IntoCrdtNode, OpState, Value},
json_crdt::{add_crdt_fields, BaseCrdt, CrdtNode, IntoCrdtNode, JsonValue, OpState},
keypair::make_keypair,
list_crdt::ListCrdt,
lww_crdt::LwwRegisterCrdt,
@@ -697,7 +699,7 @@ mod test {
.set(3000.0)
.sign_with_dependencies(&kp1, vec![&_add_money]);
let sword: Value = json!({
let sword: JsonValue = json!({
"name": "Sword",
"soulbound": true,
})
@@ -748,8 +750,8 @@ mod test {
let mut base2 = BaseCrdt::<Game>::new(&kp2);
// init a 2d grid
let row0: Value = json!([true, false]).into();
let row1: Value = json!([false, true]).into();
let row0: JsonValue = json!([true, false]).into();
let row1: JsonValue = json!([false, true]).into();
let construct1 = base1.doc.grid.insert_idx(0, row0).sign(&kp1);
let construct2 = base1.doc.grid.insert_idx(1, row1).sign(&kp1);
@@ -800,13 +802,13 @@ mod test {
#[add_crdt_fields]
#[derive(Clone, CrdtNode)]
struct Test {
reg: LwwRegisterCrdt<Value>,
reg: LwwRegisterCrdt<JsonValue>,
}
let kp1 = make_keypair();
let mut base1 = BaseCrdt::<Test>::new(&kp1);
let base_val: Value = json!({
let base_val: JsonValue = json!({
"a": true,
"b": "asdf",
"c": {
@@ -856,11 +858,11 @@ mod test {
assert_eq!(crdt.doc.reg.view(), json!(true).into());
// set nested
let mut list_view: Value = crdt.doc.strct.view().into();
let mut list_view: JsonValue = crdt.doc.strct.view().into();
assert_eq!(list_view, json!([]).into());
// only keeps actual numbers
let list: Value = json!({"list": [0, 123, -0.45, "char", []]}).into();
let list: JsonValue = json!({"list": [0, 123, -0.45, "char", []]}).into();
crdt.doc.strct.insert_idx(0, list);
list_view = crdt.doc.strct.view().into();
assert_eq!(list_view, json!([{ "list": [0, 123, -0.45]}]).into());

View File

@@ -1,6 +1,6 @@
use crate::{
debug::debug_path_mismatch,
json_crdt::{CrdtNode, OpState, Value},
json_crdt::{CrdtNode, JsonValue, OpState},
keypair::AuthorId,
op::*,
};
@@ -48,7 +48,7 @@ where
}
/// Locally insert some content causally after the given operation
pub fn insert<U: Into<Value>>(&mut self, after: OpId, content: U) -> Op<Value> {
pub fn insert<U: Into<JsonValue>>(&mut self, after: OpId, content: U) -> Op<JsonValue> {
let mut op = Op::new(
after,
self.our_id,
@@ -67,7 +67,11 @@ where
}
/// Shorthand function to insert at index locally. Indexing ignores deleted items
pub fn insert_idx<U: Into<Value> + Clone>(&mut self, idx: usize, content: U) -> Op<Value> {
pub fn insert_idx<U: Into<JsonValue> + Clone>(
&mut self,
idx: usize,
content: U,
) -> Op<JsonValue> {
let mut i = 0;
for op in &self.ops {
if !op.is_deleted {
@@ -97,7 +101,7 @@ where
/// Mark a node as deleted. If the node doesn't exist, it will be stuck
/// waiting for that node to be created.
pub fn delete(&mut self, id: OpId) -> Op<Value> {
pub fn delete(&mut self, id: OpId) -> Op<JsonValue> {
let op = Op::new(
id,
self.our_id,
@@ -117,7 +121,7 @@ where
/// Apply an operation (both local and remote) to this local list CRDT.
/// Forwards it to a nested CRDT if necessary.
pub fn apply(&mut self, op: Op<Value>) -> OpState {
pub fn apply(&mut self, op: Op<JsonValue>) -> OpState {
if !op.is_valid_hash() {
return OpState::ErrHashMismatch;
}
@@ -308,11 +312,11 @@ impl<T> CrdtNode for ListCrdt<T>
where
T: CrdtNode,
{
fn apply(&mut self, op: Op<Value>) -> OpState {
fn apply(&mut self, op: Op<JsonValue>) -> OpState {
self.apply(op.into())
}
fn view(&self) -> Value {
fn view(&self) -> JsonValue {
self.view().into()
}

View File

@@ -1,5 +1,5 @@
use crate::debug::DebugView;
use crate::json_crdt::{CrdtNode, OpState, Value};
use crate::json_crdt::{CrdtNode, OpState, JsonValue};
use crate::op::{join_path, print_path, Op, PathSegment, SequenceNumber};
use std::cmp::{max, Ordering};
use std::fmt::Debug;
@@ -38,7 +38,7 @@ where
}
/// Sets the current value of the register
pub fn set<U: Into<Value>>(&mut self, content: U) -> Op<Value> {
pub fn set<U: Into<JsonValue>>(&mut self, content: U) -> Op<JsonValue> {
let mut op = Op::new(
self.value.id,
self.our_id,
@@ -57,7 +57,7 @@ where
}
/// Apply an operation (both local and remote) to this local register CRDT.
pub fn apply(&mut self, op: Op<Value>) -> OpState {
pub fn apply(&mut self, op: Op<JsonValue>) -> OpState {
if !op.is_valid_hash() {
return OpState::ErrHashMismatch;
}
@@ -100,11 +100,11 @@ impl<T> CrdtNode for LwwRegisterCrdt<T>
where
T: CrdtNode,
{
fn apply(&mut self, op: Op<Value>) -> OpState {
fn apply(&mut self, op: Op<JsonValue>) -> OpState {
self.apply(op.into())
}
fn view(&self) -> Value {
fn view(&self) -> JsonValue {
self.view().into()
}

View File

@@ -1,5 +1,5 @@
use crate::debug::{debug_path_mismatch, debug_type_mismatch};
use crate::json_crdt::{CrdtNode, CrdtNodeFromValue, IntoCrdtNode, SignedOp, Value};
use crate::json_crdt::{CrdtNode, CrdtNodeFromValue, IntoCrdtNode, JsonValue, SignedOp};
use crate::keypair::{sha256, AuthorId};
use fastcrypto::ed25519::Ed25519KeyPair;
use serde::{Deserialize, Serialize};
@@ -112,7 +112,7 @@ where
}
/// Conversion from Op<Value> -> Op<T> given that T is a CRDT that can be created from a JSON value
impl Op<Value> {
impl Op<JsonValue> {
pub fn into<T: CrdtNodeFromValue + CrdtNode>(self) -> Op<T> {
let content = if let Some(inner_content) = self.content {
match inner_content.into_node(self.id, self.path.clone()) {

View File

@@ -1,7 +1,7 @@
use bft_json_crdt::{
keypair::make_author,
list_crdt::ListCrdt,
op::{Op, OpId, ROOT_ID}, json_crdt::{CrdtNode, Value},
op::{Op, OpId, ROOT_ID}, json_crdt::{CrdtNode, JsonValue},
};
use rand::{rngs::ThreadRng, seq::SliceRandom, Rng};
@@ -14,9 +14,9 @@ const TEST_N: usize = 100;
#[test]
fn test_list_fuzz_commutative() {
let mut rng = rand::thread_rng();
let mut op_log = Vec::<Op<Value>>::new();
let mut op_log1 = Vec::<Op<Value>>::new();
let mut op_log2 = Vec::<Op<Value>>::new();
let mut op_log = Vec::<Op<JsonValue>>::new();
let mut op_log1 = Vec::<Op<JsonValue>>::new();
let mut op_log2 = Vec::<Op<JsonValue>>::new();
let mut l1 = ListCrdt::<char>::new(make_author(1), vec![]);
let mut l2 = ListCrdt::<char>::new(make_author(2), vec![]);
let mut chk = ListCrdt::<char>::new(make_author(3), vec![]);
@@ -62,8 +62,8 @@ fn test_list_fuzz_commutative() {
assert_eq!(l2_doc, chk_doc);
// now, allow cross mixing between both
let mut op_log1 = Vec::<Op<Value>>::new();
let mut op_log2 = Vec::<Op<Value>>::new();
let mut op_log1 = Vec::<Op<JsonValue>>::new();
let mut op_log2 = Vec::<Op<JsonValue>>::new();
for _ in 0..TEST_N {
let letter1: char = rng.gen_range(b'a'..=b'z') as char;
let letter2: char = rng.gen_range(b'a'..=b'z') as char;