From a2e433348ae4365526304202a24fb881088467b3 Mon Sep 17 00:00:00 2001
From: Orion Henry <orion.henry@gmail.com>
Date: Fri, 28 Jan 2022 12:24:58 -0500
Subject: [PATCH] mark encode/decode/serde

---
 automerge-wasm/test/test.js                   | 22 +++++-
 automerge/src/automerge.rs                    | 19 ++---
 automerge/src/columnar.rs                     | 52 ++++++++++++--
 automerge/src/legacy/serde_impls/op.rs        | 42 +++++++++++
 automerge/src/legacy/serde_impls/op_type.rs   |  4 +-
 automerge/src/legacy/utility_impls/mod.rs     |  1 -
 .../src/legacy/utility_impls/scalar_value.rs  | 57 ---------------
 automerge/src/query.rs                        |  2 -
 automerge/src/query/mark.rs                   | 71 ------------------
 automerge/src/query/spans.rs                  |  4 +-
 automerge/src/types.rs                        |  6 ++
 automerge/src/value.rs                        | 72 +++++++++++++++++++
 12 files changed, 203 insertions(+), 149 deletions(-)
 delete mode 100644 automerge/src/legacy/utility_impls/scalar_value.rs
 delete mode 100644 automerge/src/query/mark.rs

diff --git a/automerge-wasm/test/test.js b/automerge-wasm/test/test.js
index 7d1519c1..38d68eba 100644
--- a/automerge-wasm/test/test.js
+++ b/automerge-wasm/test/test.js
@@ -3,7 +3,7 @@ const assert = require('assert')
 const util = require('util')
 const { BloomFilter } = require('./helpers/sync')
 const Automerge = require('..')
-const { MAP, LIST, TEXT, initSyncState, decodeSyncMessage, decodeSyncState, encodeSyncState }= Automerge
+const { MAP, LIST, TEXT, encodeChange, decodeChange, initSyncState, decodeSyncMessage, decodeSyncState, encodeSyncState }= Automerge
 
 // str to uint8array
 function en(str) {
@@ -460,6 +460,15 @@ describe('Automerge', () => {
       doc.insert(list, 2, "A")
       spans = doc.spans(list);
       assert.deepStrictEqual(spans, [ 'aa', [ [ 'bold', 'boolean', true ] ], 'AbA', [], 'cc' ])
+
+      // make sure save/load can handle marks
+
+      let doc2 = Automerge.load(doc.save())
+      spans = doc2.spans(list);
+      assert.deepStrictEqual(spans, [ 'aa', [ [ 'bold', 'boolean', true ] ], 'AbA', [], 'cc' ])
+
+      assert.deepStrictEqual(doc.getHeads(), doc2.getHeads())
+      assert.deepStrictEqual(doc.save(), doc2.save())
     })
 
     it.only('should handle overlapping marks', () => {
@@ -489,6 +498,17 @@ describe('Automerge', () => {
           [],
         ]
       )
+
+      // mark sure encode decode can handle marks
+
+      let all = doc.getChanges([])
+      let decoded = all.map((c) => decodeChange(c))
+      let encoded = decoded.map((c) => encodeChange(c))
+      let doc2 = Automerge.init();
+      doc2.applyChanges(encoded)
+
+      assert.deepStrictEqual(doc.spans(list) , doc2.spans(list))
+      assert.deepStrictEqual(doc.save(), doc2.save())
     })
 
   })
diff --git a/automerge/src/automerge.rs b/automerge/src/automerge.rs
index f74a21d3..afed341e 100644
--- a/automerge/src/automerge.rs
+++ b/automerge/src/automerge.rs
@@ -6,7 +6,7 @@ use crate::exid::ExId;
 use crate::op_set::OpSet;
 use crate::types::{
     ActorId, ChangeHash, Clock, ElemId, Export, Exportable, Key, ObjId, Op, OpId, OpType, Patch,
-    ScalarValue, Value, MarkData,
+    ScalarValue, Value,
 };
 use crate::{legacy, query, types, ObjType};
 use crate::{AutomergeError, Change, Prop};
@@ -322,26 +322,25 @@ impl Automerge {
         value: V,
     ) -> Result<Option<ExId>, AutomergeError> {
         let obj = self.exid_to_obj(obj)?;
-        if let Some(id) = self.do_insert(obj, index, value)? {
+        let value = value.into();
+        if let Some(id) = self.do_insert(obj, index, value.into())? {
             Ok(Some(self.id_to_exid(id)))
         } else {
             Ok(None)
         }
     }
 
-    fn do_insert<V: Into<Value>>(
+    fn do_insert(
         &mut self,
         obj: ObjId,
         index: usize,
-        value: V,
+        action: OpType,
     ) -> Result<Option<OpId>, AutomergeError> {
         let id = self.next_id();
-        let value = value.into();
 
         let query = self.ops.search(obj, query::InsertNth::new(index));
 
         let key = query.key()?;
-        let action = value.into();
         let is_make = matches!(&action, OpType::Make(_));
 
         let op = Op {
@@ -399,7 +398,7 @@ impl Automerge {
         let mut results = Vec::new();
         for v in vals {
             // insert()
-            let id = self.do_insert(obj, pos, v.clone())?;
+            let id = self.do_insert(obj, pos, v.into())?;
             if let Some(id) = id {
                 results.push(self.id_to_exid(id));
             }
@@ -465,8 +464,11 @@ impl Automerge {
         value: ScalarValue,
     ) -> Result<(), AutomergeError> {
         let obj = self.exid_to_obj(obj)?;
-        let query = self.ops.search(obj, query::Mark::new(start, end));
 
+        self.do_insert(obj, start, OpType::mark(mark.into(), start_sticky, value))?;
+        self.do_insert(obj, end, OpType::Unmark(end_sticky))?;
+
+/*
         let (a, b) = query.ops()?;
         let (pos, key) = a;
         let id = self.next_id();
@@ -497,6 +499,7 @@ impl Automerge {
         };
         self.ops.insert(pos, op.clone());
         self.tx().operations.push(op);
+*/
 
         Ok(())
     }
diff --git a/automerge/src/columnar.rs b/automerge/src/columnar.rs
index 15ba749d..f64f8186 100644
--- a/automerge/src/columnar.rs
+++ b/automerge/src/columnar.rs
@@ -11,7 +11,7 @@ use std::{
     str,
 };
 
-use crate::types::{ActorId, ElemId, Key, ObjId, ObjType, Op, OpId, OpType, ScalarValue};
+use crate::types::{ActorId, ElemId, Key, ObjId, ObjType, Op, OpId, OpType, ScalarValue, MarkData };
 
 use crate::legacy as amp;
 use amp::SortedVec;
@@ -134,6 +134,15 @@ impl<'a> Iterator for OperationIterator<'a> {
             Action::MakeTable => OpType::Make(ObjType::Table),
             Action::Del => OpType::Del,
             Action::Inc => OpType::Inc(value.to_i64()?),
+            Action::Mark => {
+              // mark has 3 things in the val column
+              let name = value.to_string()?;
+              let sticky = self.value.next()?.to_bool()?;
+              let value = self.value.next()?;
+              OpType::Mark(MarkData { name, sticky, value })
+            }
+            Action::Unmark => OpType::Unmark(value.to_bool()?),
+            Action::Unused => panic!("invalid action"),
         };
         Some(amp::Op {
             action,
@@ -175,6 +184,15 @@ impl<'a> Iterator for DocOpIterator<'a> {
             Action::MakeTable => OpType::Make(ObjType::Table),
             Action::Del => OpType::Del,
             Action::Inc => OpType::Inc(value.to_i64()?),
+            Action::Mark => {
+              // mark has 3 things in the val column
+              let name = value.to_string()?;
+              let sticky = self.value.next()?.to_bool()?;
+              let value = self.value.next()?;
+              OpType::Mark(MarkData { name, sticky, value })
+            }
+            Action::Unmark => OpType::Unmark(value.to_bool()?),
+            Action::Unused => panic!("invalid action"),
         };
         Some(DocOp {
             actor,
@@ -1064,8 +1082,16 @@ impl DocOpEncoder {
                     self.val.append_null();
                     Action::Del
                 }
-                amp::OpType::Mark(_) => unimplemented!(),
-                amp::OpType::Unmark(_) => unimplemented!(),
+                amp::OpType::Mark(m) => {
+                    self.val.append_value(&m.name.clone().into(), actors);
+                    self.val.append_value(&m.sticky.into(), actors);
+                    self.val.append_value(&m.value.clone().into(), actors);
+                    Action::Mark
+                }
+                amp::OpType::Unmark(s) => {
+                    self.val.append_value(&(*s).into(), actors);
+                    Action::Unmark
+                }
                 amp::OpType::Make(kind) => {
                     self.val.append_null();
                     match kind {
@@ -1172,8 +1198,16 @@ impl ColumnEncoder {
                 self.val.append_null();
                 Action::Del
             }
-            OpType::Mark(_) => unimplemented!(),
-            OpType::Unmark(_) => unimplemented!(),
+            OpType::Mark(m) => {
+                self.val.append_value2(&m.name.clone().into(), actors);
+                self.val.append_value2(&m.sticky.into(), actors);
+                self.val.append_value2(&m.value.clone().into(), actors);
+                Action::Mark
+            }
+            OpType::Unmark(s) => {
+                self.val.append_value2(&(*s).into(), actors);
+                Action::Unmark
+            }
             OpType::Make(kind) => {
                 self.val.append_null();
                 match kind {
@@ -1279,8 +1313,11 @@ pub(crate) enum Action {
     MakeText,
     Inc,
     MakeTable,
+    Mark,
+    Unused, // final bit is used to mask `Make` actions
+    Unmark,
 }
-const ACTIONS: [Action; 7] = [
+const ACTIONS: [Action; 10] = [
     Action::MakeMap,
     Action::Set,
     Action::MakeList,
@@ -1288,6 +1325,9 @@ const ACTIONS: [Action; 7] = [
     Action::MakeText,
     Action::Inc,
     Action::MakeTable,
+    Action::Mark,
+    Action::Unused,
+    Action::Unmark,
 ];
 
 impl Decodable for Action {
diff --git a/automerge/src/legacy/serde_impls/op.rs b/automerge/src/legacy/serde_impls/op.rs
index 5f0db62d..298464b0 100644
--- a/automerge/src/legacy/serde_impls/op.rs
+++ b/automerge/src/legacy/serde_impls/op.rs
@@ -49,6 +49,12 @@ impl Serialize for Op {
         match &self.action {
             OpType::Inc(n) => op.serialize_field("value", &n)?,
             OpType::Set(value) => op.serialize_field("value", &value)?,
+            OpType::Mark(m) => {
+              op.serialize_field("name", &m.name)?;
+              op.serialize_field("sticky", &m.sticky)?;
+              op.serialize_field("value", &m.value)?;
+            }
+            OpType::Unmark(s) => op.serialize_field("sticky", &s)?,
             _ => {}
         }
         op.serialize_field("pred", &self.pred)?;
@@ -70,6 +76,8 @@ pub(crate) enum RawOpType {
     Del,
     Inc,
     Set,
+    Mark,
+    Unmark,
 }
 
 impl Serialize for RawOpType {
@@ -85,6 +93,8 @@ impl Serialize for RawOpType {
             RawOpType::Del => "del",
             RawOpType::Inc => "inc",
             RawOpType::Set => "set",
+            RawOpType::Mark => "mark",
+            RawOpType::Unmark => "unmark",
         };
         serializer.serialize_str(s)
     }
@@ -103,6 +113,8 @@ impl<'de> Deserialize<'de> for RawOpType {
             "del",
             "inc",
             "set",
+            "mark",
+            "unmark",
         ];
         // TODO: Probably more efficient to deserialize to a `&str`
         let raw_type = String::deserialize(deserializer)?;
@@ -114,6 +126,8 @@ impl<'de> Deserialize<'de> for RawOpType {
             "del" => Ok(RawOpType::Del),
             "inc" => Ok(RawOpType::Inc),
             "set" => Ok(RawOpType::Set),
+            "mark" => Ok(RawOpType::Mark),
+            "unmark" => Ok(RawOpType::Unmark),
             other => Err(Error::unknown_variant(other, VARIANTS)),
         }
     }
@@ -144,6 +158,8 @@ impl<'de> Deserialize<'de> for Op {
                 let mut insert: Option<bool> = None;
                 let mut datatype: Option<DataType> = None;
                 let mut value: Option<Option<ScalarValue>> = None;
+                let mut name: Option<String> = None;
+                let mut sticky: Option<bool> = None;
                 let mut ref_id: Option<OpId> = None;
                 while let Some(field) = map.next_key::<String>()? {
                     match field.as_ref() {
@@ -167,6 +183,8 @@ impl<'de> Deserialize<'de> for Op {
                         "insert" => read_field("insert", &mut insert, &mut map)?,
                         "datatype" => read_field("datatype", &mut datatype, &mut map)?,
                         "value" => read_field("value", &mut value, &mut map)?,
+                        "name" => read_field("name", &mut name, &mut map)?,
+                        "sticky" => read_field("sticky", &mut sticky, &mut map)?,
                         "ref" => read_field("ref", &mut ref_id, &mut map)?,
                         _ => return Err(Error::unknown_field(&field, FIELDS)),
                     }
@@ -182,6 +200,30 @@ impl<'de> Deserialize<'de> for Op {
                     RawOpType::MakeList => OpType::Make(ObjType::List),
                     RawOpType::MakeText => OpType::Make(ObjType::Text),
                     RawOpType::Del => OpType::Del,
+                    RawOpType::Mark => { 
+                        let name = name.ok_or_else(|| Error::missing_field("mark(name)"))?;
+                        let sticky = sticky.unwrap_or(false);
+                        let value = if let Some(datatype) = datatype {
+                            let raw_value = value
+                                .ok_or_else(|| Error::missing_field("value"))?
+                                .unwrap_or(ScalarValue::Null);
+                            raw_value.as_datatype(datatype).map_err(|e| {
+                                Error::invalid_value(
+                                    Unexpected::Other(e.unexpected.as_str()),
+                                    &e.expected.as_str(),
+                                )
+                            })?
+                        } else {
+                            value
+                                .ok_or_else(|| Error::missing_field("value"))?
+                                .unwrap_or(ScalarValue::Null)
+                        };
+                        OpType::mark(name, sticky, value)
+                    }
+                    RawOpType::Unmark => {
+                      let sticky = sticky.unwrap_or(true);
+                      OpType::Unmark(sticky)
+                    }
                     RawOpType::Set => {
                         let value = if let Some(datatype) = datatype {
                             let raw_value = value
diff --git a/automerge/src/legacy/serde_impls/op_type.rs b/automerge/src/legacy/serde_impls/op_type.rs
index 01041ef7..a36355e2 100644
--- a/automerge/src/legacy/serde_impls/op_type.rs
+++ b/automerge/src/legacy/serde_impls/op_type.rs
@@ -15,8 +15,8 @@ impl Serialize for OpType {
             OpType::Make(ObjType::Table) => RawOpType::MakeTable,
             OpType::Make(ObjType::List) => RawOpType::MakeList,
             OpType::Make(ObjType::Text) => RawOpType::MakeText,
-            OpType::Mark(_) => unimplemented!(),
-            OpType::Unmark(_) => unimplemented!(),
+            OpType::Mark(_) => RawOpType::Mark,
+            OpType::Unmark(_) => RawOpType::Unmark,
             OpType::Del => RawOpType::Del,
             OpType::Inc(_) => RawOpType::Inc,
             OpType::Set(_) => RawOpType::Set,
diff --git a/automerge/src/legacy/utility_impls/mod.rs b/automerge/src/legacy/utility_impls/mod.rs
index 3476b8da..99fa1750 100644
--- a/automerge/src/legacy/utility_impls/mod.rs
+++ b/automerge/src/legacy/utility_impls/mod.rs
@@ -2,4 +2,3 @@ mod element_id;
 mod key;
 mod object_id;
 mod opid;
-mod scalar_value;
diff --git a/automerge/src/legacy/utility_impls/scalar_value.rs b/automerge/src/legacy/utility_impls/scalar_value.rs
deleted file mode 100644
index ef0a3305..00000000
--- a/automerge/src/legacy/utility_impls/scalar_value.rs
+++ /dev/null
@@ -1,57 +0,0 @@
-use std::fmt;
-
-use smol_str::SmolStr;
-
-use crate::value::ScalarValue;
-
-impl From<&str> for ScalarValue {
-    fn from(s: &str) -> Self {
-        ScalarValue::Str(s.into())
-    }
-}
-
-impl From<i64> for ScalarValue {
-    fn from(n: i64) -> Self {
-        ScalarValue::Int(n)
-    }
-}
-
-impl From<u64> for ScalarValue {
-    fn from(n: u64) -> Self {
-        ScalarValue::Uint(n)
-    }
-}
-
-impl From<i32> for ScalarValue {
-    fn from(n: i32) -> Self {
-        ScalarValue::Int(n as i64)
-    }
-}
-
-impl From<bool> for ScalarValue {
-    fn from(b: bool) -> Self {
-        ScalarValue::Boolean(b)
-    }
-}
-
-impl From<char> for ScalarValue {
-    fn from(c: char) -> Self {
-        ScalarValue::Str(SmolStr::new(c.to_string()))
-    }
-}
-
-impl fmt::Display for ScalarValue {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            ScalarValue::Bytes(b) => write!(f, "\"{:?}\"", b),
-            ScalarValue::Str(s) => write!(f, "\"{}\"", s),
-            ScalarValue::Int(i) => write!(f, "{}", i),
-            ScalarValue::Uint(i) => write!(f, "{}", i),
-            ScalarValue::F64(n) => write!(f, "{:.324}", n),
-            ScalarValue::Counter(c) => write!(f, "Counter: {}", c),
-            ScalarValue::Timestamp(i) => write!(f, "Timestamp: {}", i),
-            ScalarValue::Boolean(b) => write!(f, "{}", b),
-            ScalarValue::Null => write!(f, "null"),
-        }
-    }
-}
diff --git a/automerge/src/query.rs b/automerge/src/query.rs
index 80af06b2..7bd50158 100644
--- a/automerge/src/query.rs
+++ b/automerge/src/query.rs
@@ -12,7 +12,6 @@ mod len;
 mod len_at;
 mod list_vals;
 mod list_vals_at;
-mod mark;
 mod nth;
 mod nth_at;
 mod prop;
@@ -27,7 +26,6 @@ pub(crate) use len::Len;
 pub(crate) use len_at::LenAt;
 pub(crate) use list_vals::ListVals;
 pub(crate) use list_vals_at::ListValsAt;
-pub(crate) use mark::Mark;
 pub(crate) use nth::Nth;
 pub(crate) use nth_at::NthAt;
 pub(crate) use prop::Prop;
diff --git a/automerge/src/query/mark.rs b/automerge/src/query/mark.rs
deleted file mode 100644
index 3757677b..00000000
--- a/automerge/src/query/mark.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use crate::AutomergeError;
-use crate::query::{QueryResult, TreeQuery};
-use crate::types::{ElemId, Key, Op};
-use std::fmt::Debug;
-
-#[derive(Debug, Clone, PartialEq)]
-pub(crate) struct Mark<const B: usize> {
-    start: usize,
-    end: usize,
-    pos: usize,
-    seen: usize,
-    _ops: Vec<(usize, Key)>,
-    count: usize,
-    last_seen: Option<ElemId>,
-    last_insert: Option<ElemId>,
-}
-
-impl<const B: usize> Mark<B> {
-    pub fn new(start: usize, end: usize)  -> Self {
-        Mark {
-            start,
-            end,
-            pos: 0,
-            seen: 0,
-            _ops: Vec::new(),
-            count: 0,
-            last_seen: None,
-            last_insert: None,
-        }
-    }
-
-    pub fn ops(&self) -> Result<((usize,Key),(usize,Key)),AutomergeError> {
-        if self._ops.len() == 2 {
-            Ok((self._ops[0], self._ops[1]))
-        } else if self._ops.len() == 1 {
-            Ok((self._ops[0], (self.pos + 1, self.last_insert.into())))
-        } else {
-            Err(AutomergeError::Fail)
-        }
-    }
-}
-
-impl<const B: usize> TreeQuery<B> for Mark<B> {
-    /*
-    fn query_node(&mut self, _child: &OpTreeNode<B>) -> QueryResult {
-        unimplemented!()
-    }
-    */
-
-    fn query_element(&mut self, element: &Op) -> QueryResult {
-        // find location to insert
-        // mark or set
-        if element.insert {
-            if self.seen >= self.end {
-               self._ops.push((self.pos + 1, self.last_insert.into()));
-               return QueryResult::Finish;
-            }
-            if self.seen >= self.start && self._ops.is_empty() {
-               self._ops.push((self.pos, self.last_insert.into()));
-            }
-            self.last_seen = None;
-            self.last_insert = element.elemid();
-        }
-        if self.last_seen.is_none() && element.visible() {
-            self.seen += 1;
-            self.last_seen = element.elemid()
-        }
-        self.pos += 1;
-        QueryResult::Next
-    }
-}
diff --git a/automerge/src/query/spans.rs b/automerge/src/query/spans.rs
index 461c4a70..f39f3fc1 100644
--- a/automerge/src/query/spans.rs
+++ b/automerge/src/query/spans.rs
@@ -53,9 +53,11 @@ impl<const B: usize> Spans<B> {
         if self.changed && (self.seen_at_last_mark != self.seen_at_this_mark  || self.seen_at_last_mark.is_none() && self.seen_at_this_mark.is_none()) {
             self.changed = false;
             self.seen_at_last_mark = self.seen_at_this_mark;
+            let mut marks : Vec<_> = self.marks.iter().map(|(key, val)| (key.clone(), val.clone())).collect();
+            marks.sort_by(|(k1,_),(k2,_)| k1.cmp(k2));
             self.spans.push(Span { 
                 pos: self.seen,
-                marks: self.marks.iter().map(|(key, val)| (key.clone(), val.clone())).collect()
+                marks,
             });
         }
     }
diff --git a/automerge/src/types.rs b/automerge/src/types.rs
index 6b883cfa..91a45781 100644
--- a/automerge/src/types.rs
+++ b/automerge/src/types.rs
@@ -164,6 +164,12 @@ pub enum OpType {
     Unmark(bool),
 }
 
+impl OpType {
+  pub (crate) fn mark(name: String, sticky: bool, value: ScalarValue) -> Self {
+    OpType::Mark(MarkData { name, sticky, value })
+  }
+}
+
 #[derive(PartialEq, Debug, Clone)]
 pub struct MarkData {
     pub name: String,
diff --git a/automerge/src/value.rs b/automerge/src/value.rs
index eef757fd..9ade57ef 100644
--- a/automerge/src/value.rs
+++ b/automerge/src/value.rs
@@ -375,7 +375,79 @@ impl ScalarValue {
         }
     }
 
+    pub fn to_bool(self) -> Option<bool> {
+        match self {
+            ScalarValue::Boolean(b) => Some(b),
+            _ => None,
+        }
+    }
+
+    pub fn to_string(self) -> Option<String> {
+        match self {
+            ScalarValue::Str(s) => Some(s.to_string()),
+            _ => None,
+        }
+    }
+
     pub fn counter(n: i64) -> ScalarValue {
         ScalarValue::Counter(n.into())
     }
 }
+
+impl From<&str> for ScalarValue {
+    fn from(s: &str) -> Self {
+        ScalarValue::Str(s.into())
+    }
+}
+
+impl From<String> for ScalarValue {
+    fn from(s: String) -> Self {
+        ScalarValue::Str(s.into())
+    }
+}
+
+impl From<i64> for ScalarValue {
+    fn from(n: i64) -> Self {
+        ScalarValue::Int(n)
+    }
+}
+
+impl From<u64> for ScalarValue {
+    fn from(n: u64) -> Self {
+        ScalarValue::Uint(n)
+    }
+}
+
+impl From<i32> for ScalarValue {
+    fn from(n: i32) -> Self {
+        ScalarValue::Int(n as i64)
+    }
+}
+
+impl From<bool> for ScalarValue {
+    fn from(b: bool) -> Self {
+        ScalarValue::Boolean(b)
+    }
+}
+
+impl From<char> for ScalarValue {
+    fn from(c: char) -> Self {
+        ScalarValue::Str(SmolStr::new(c.to_string()))
+    }
+}
+
+impl fmt::Display for ScalarValue {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ScalarValue::Bytes(b) => write!(f, "\"{:?}\"", b),
+            ScalarValue::Str(s) => write!(f, "\"{}\"", s),
+            ScalarValue::Int(i) => write!(f, "{}", i),
+            ScalarValue::Uint(i) => write!(f, "{}", i),
+            ScalarValue::F64(n) => write!(f, "{:.324}", n),
+            ScalarValue::Counter(c) => write!(f, "Counter: {}", c),
+            ScalarValue::Timestamp(i) => write!(f, "Timestamp: {}", i),
+            ScalarValue::Boolean(b) => write!(f, "{}", b),
+            ScalarValue::Null => write!(f, "null"),
+        }
+    }
+}