aboutsummaryrefslogtreecommitdiffstats
path: root/src/document/store.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-02 11:07:46 +0100
committertv <tv@krebsco.de>2026-11-02 11:07:46 +0100
commita7b8b4d6543c4c780334df1c823d2e2f10d9e1bf (patch)
tree12cf1ad634ed128c40bfd9b6c9b9283dc81b3908 /src/document/store.rs
parent694661d7477e4bd1e8d7ec96dcc37742d1b5ca3f (diff)
api: drop $float as JSON number is OK
Diffstat (limited to 'src/document/store.rs')
-rw-r--r--src/document/store.rs53
1 files changed, 37 insertions, 16 deletions
diff --git a/src/document/store.rs b/src/document/store.rs
index 9075032..1a235b2 100644
--- a/src/document/store.rs
+++ b/src/document/store.rs
@@ -676,14 +676,27 @@ pub(crate) fn write_val_path(
Ok(writing.retyped)
}
+/// Which values a write writes as the type of what they are written over (`stuck`).
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub(crate) enum Sticky {
+ /// None: the value says its type (a type's own media type, a cast).
+ No,
+ /// A number without a fraction over a float: typed JSON says every other type
+ /// itself, but not a float's, as JSON numbers don't keep `2.0` apart from `2` in
+ /// every language.
+ Floats,
+ /// Plain JSON, which has fewer types than Automerge: where it is the JSON form of a
+ /// value of the type there (a string that reads as a timestamp, as base64, a number
+ /// without a fraction over a float, an unsigned integer or a counter, a string over
+ /// a text).
+ All,
+}
+
/// How a write treats the types of what it writes over.
pub(crate) struct Writing {
- /// Types are sticky: the value comes as plain JSON, which has fewer types than
- /// Automerge, and where it is the JSON form of a value of the type there (a string
- /// that reads as a timestamp, as base64, a number without a fraction over a float,
- /// an unsigned integer or a counter, a string over a text), it is written as that
- /// type (`stuck`). Typed JSON and a type's own media type say the type themselves.
- pub(crate) sticky: bool,
+ /// Types are sticky: what the value is written as where it is the JSON form of a
+ /// value of the type there.
+ pub(crate) sticky: Sticky,
/// How sticky timestamps are written in JSON (`typed::TimestampFormat::json`).
pub(crate) timestamps: TimestampFormat,
/// Whether the write may change the type of what it writes over; if not, one that
@@ -695,10 +708,10 @@ pub(crate) struct Writing {
impl Writing {
pub(crate) fn sticky(timestamps: TimestampFormat) -> Self {
- Self::new(true, timestamps, true)
+ Self::new(Sticky::All, timestamps, true)
}
- pub(crate) fn new(sticky: bool, timestamps: TimestampFormat, retype: bool) -> Self {
+ pub(crate) fn new(sticky: Sticky, timestamps: TimestampFormat, retype: bool) -> Self {
Self {
sticky,
timestamps,
@@ -760,11 +773,23 @@ fn kind_of(held: &Value<'_>) -> Kind {
}
/// `value` written as the type of `held`, where it is a JSON form of a value of it
-/// (`Writing::sticky`); None where it isn't (it is written as it is).
-fn stuck(held: &Value<'_>, value: &Val, timestamps: TimestampFormat) -> Option<Val> {
+/// as `sticky` says (`Writing::sticky`); None where it isn't (it is written as it is).
+fn stuck(
+ held: &Value<'_>,
+ value: &Val,
+ sticky: Sticky,
+ timestamps: TimestampFormat,
+) -> Option<Val> {
let Val::Scalar(scalar) = value else {
return None;
};
+ let over_float = matches!(held, Value::Scalar(held) if matches!(held.as_ref(), ScalarValue::F64(_)))
+ && matches!(scalar, ScalarValue::Int(_) | ScalarValue::Uint(_));
+ match sticky {
+ Sticky::No => return None,
+ Sticky::Floats if !over_float => return None,
+ Sticky::Floats | Sticky::All => {}
+ }
let held = match held {
Value::Object(ObjType::Text) => {
return match scalar {
@@ -843,11 +868,7 @@ fn update_of<'v>(
let Some((held, _)) = holds.first() else {
return Ok((Update::Anew, Cow::Borrowed(value)));
};
- let value = match writing
- .sticky
- .then(|| stuck(held, value, writing.timestamps))
- .flatten()
- {
+ let value = match stuck(held, value, writing.sticky, writing.timestamps) {
Some(stuck) => Cow::Owned(stuck),
None => Cow::Borrowed(value),
};
@@ -1083,7 +1104,7 @@ pub(crate) fn cast_path(
.as_ref()
.ok_or_else(|| not_castable(format!("{} is cast to nothing", held.kind.name())))?;
let cast = typed::cast(held.kind, value, to, timestamps).map_err(not_castable)?;
- write_val_path(doc, path, &cast, Writing::new(false, timestamps, true))
+ write_val_path(doc, path, &cast, Writing::new(Sticky::No, timestamps, true))
}
/// Inserts `value` into a list: `path` ends in the index it goes to (the items from