feat: implement fromTOML; fix fromJSON implementation
This commit is contained in:
55
Cargo.lock
generated
55
Cargo.lock
generated
@@ -1966,6 +1966,7 @@ dependencies = [
|
|||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"xz2",
|
"xz2",
|
||||||
@@ -2280,7 +2281,7 @@ version = "3.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
|
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"toml_edit",
|
"toml_edit 0.23.10+spec-1.0.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2824,6 +2825,15 @@ dependencies = [
|
|||||||
"zmij",
|
"zmij",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_urlencoded"
|
name = "serde_urlencoded"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
@@ -3358,6 +3368,27 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime 0.6.11",
|
||||||
|
"toml_edit 0.22.27",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.7.5+spec-1.1.0"
|
version = "0.7.5+spec-1.1.0"
|
||||||
@@ -3367,6 +3398,20 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime 0.6.11",
|
||||||
|
"toml_write",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
version = "0.23.10+spec-1.0.0"
|
version = "0.23.10+spec-1.0.0"
|
||||||
@@ -3374,7 +3419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
|
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"toml_datetime",
|
"toml_datetime 0.7.5+spec-1.1.0",
|
||||||
"toml_parser",
|
"toml_parser",
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
@@ -3388,6 +3433,12 @@ dependencies = [
|
|||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_write"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tower"
|
name = "tower"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ let
|
|||||||
flake = (
|
flake = (
|
||||||
import flake-compat {
|
import flake-compat {
|
||||||
src = ./.;
|
src = ./.;
|
||||||
|
copySourceTreeToStore = false;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ bzip2 = "0.5"
|
|||||||
zip = "2.2"
|
zip = "2.2"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
toml = "0.8"
|
||||||
dirs = "5.0"
|
dirs = "5.0"
|
||||||
tempfile = "3.24"
|
tempfile = "3.24"
|
||||||
rusqlite = { version = "0.33", features = ["bundled"] }
|
rusqlite = { version = "0.33", features = ["bundled"] }
|
||||||
|
|||||||
@@ -6,55 +6,22 @@ import type { NixString, NixValue } from "../types";
|
|||||||
import { isStringWithContext, isNixPath } from "../types";
|
import { isStringWithContext, isNixPath } from "../types";
|
||||||
import { force } from "../thunk";
|
import { force } from "../thunk";
|
||||||
import { type NixStringContext, mkStringWithContext, addBuiltContext } from "../string-context";
|
import { type NixStringContext, mkStringWithContext, addBuiltContext } from "../string-context";
|
||||||
import { forceFunction } from "../type-assert";
|
import { forceFunction, forceStringNoCtx } from "../type-assert";
|
||||||
import { nixValueToJson } from "../conversion";
|
import { nixValueToJson } from "../conversion";
|
||||||
import { isAttrs, isPath, typeOf } from "./type-check";
|
import { isAttrs, isPath, typeOf } from "./type-check";
|
||||||
|
|
||||||
const convertJsonToNix = (json: unknown): NixValue => {
|
|
||||||
if (json === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (typeof json === "boolean") {
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
if (typeof json === "number") {
|
|
||||||
if (Number.isInteger(json)) {
|
|
||||||
return BigInt(json);
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
if (typeof json === "string") {
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
if (Array.isArray(json)) {
|
|
||||||
return json.map(convertJsonToNix);
|
|
||||||
}
|
|
||||||
if (typeof json === "object") {
|
|
||||||
const result: Record<string, NixValue> = {};
|
|
||||||
for (const [key, value] of Object.entries(json)) {
|
|
||||||
result[key] = convertJsonToNix(value);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
throw new TypeError(`unsupported JSON value type: ${typeof json}`);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const fromJSON = (e: NixValue): NixValue => {
|
export const fromJSON = (e: NixValue): NixValue => {
|
||||||
const str = force(e);
|
const str = force(e);
|
||||||
if (typeof str !== "string" && !isStringWithContext(str)) {
|
if (typeof str !== "string" && !isStringWithContext(str)) {
|
||||||
throw new TypeError(`builtins.fromJSON: expected a string, got ${typeOf(str)}`);
|
throw new TypeError(`builtins.fromJSON: expected a string, got ${typeOf(str)}`);
|
||||||
}
|
}
|
||||||
const jsonStr = isStringWithContext(str) ? str.value : str;
|
const jsonStr = isStringWithContext(str) ? str.value : str;
|
||||||
try {
|
return Deno.core.ops.op_from_json(jsonStr);
|
||||||
const parsed = JSON.parse(jsonStr);
|
|
||||||
return convertJsonToNix(parsed);
|
|
||||||
} catch (err) {
|
|
||||||
throw new SyntaxError(`builtins.fromJSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const fromTOML = (e: NixValue): never => {
|
export const fromTOML = (e: NixValue): NixValue => {
|
||||||
throw new Error("Not implemented: fromTOML");
|
const toml = forceStringNoCtx(e);
|
||||||
|
return Deno.core.ops.op_from_toml(toml);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const toJSON = (e: NixValue): NixString => {
|
export const toJSON = (e: NixValue): NixString => {
|
||||||
|
|||||||
@@ -41,11 +41,12 @@ export const nixValueToJson = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seen.has(v)) {
|
// FIXME: is this check necessary?
|
||||||
throw new Error("cycle detected in toJSON");
|
// if (seen.has(v)) {
|
||||||
} else {
|
// throw new Error("cycle detected in toJSON");
|
||||||
seen.add(v)
|
// } else {
|
||||||
}
|
// seen.add(v)
|
||||||
|
// }
|
||||||
|
|
||||||
if (Array.isArray(v)) {
|
if (Array.isArray(v)) {
|
||||||
return v.map((item) => nixValueToJson(item, strict, outContext, copyToStore, seen));
|
return v.map((item) => nixValueToJson(item, strict, outContext, copyToStore, seen));
|
||||||
|
|||||||
2
nix-js/runtime-ts/src/types/global.d.ts
vendored
2
nix-js/runtime-ts/src/types/global.d.ts
vendored
@@ -74,6 +74,8 @@ declare global {
|
|||||||
): string;
|
): string;
|
||||||
function op_match(regex: string, text: string): (string | null)[] | null;
|
function op_match(regex: string, text: string): (string | null)[] | null;
|
||||||
function op_split(regex: string, text: string): (string | (string | null)[])[];
|
function op_split(regex: string, text: string): (string | (string | null)[])[];
|
||||||
|
function op_from_json(json: string): any;
|
||||||
|
function op_from_toml(toml: string): any;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -183,9 +183,7 @@ fn copy_dir_recursive(src: &Path, dst: &Path) -> Result<(), std::io::Error> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_tarball_to_temp(
|
pub fn extract_tarball_to_temp(data: &[u8]) -> Result<(PathBuf, tempfile::TempDir), ArchiveError> {
|
||||||
data: &[u8],
|
|
||||||
) -> Result<(PathBuf, tempfile::TempDir), ArchiveError> {
|
|
||||||
let temp_dir = tempfile::tempdir()?;
|
let temp_dir = tempfile::tempdir()?;
|
||||||
let extracted_path = extract_archive(data, temp_dir.path())?;
|
let extracted_path = extract_archive(data, temp_dir.path())?;
|
||||||
Ok((extracted_path, temp_dir))
|
Ok((extracted_path, temp_dir))
|
||||||
|
|||||||
@@ -69,6 +69,8 @@ fn runtime_extension<Ctx: RuntimeContext>() -> Extension {
|
|||||||
op_add_filtered_path::<Ctx>(),
|
op_add_filtered_path::<Ctx>(),
|
||||||
op_match(),
|
op_match(),
|
||||||
op_split(),
|
op_split(),
|
||||||
|
op_from_json(),
|
||||||
|
op_from_toml(),
|
||||||
];
|
];
|
||||||
ops.extend(crate::fetcher::register_ops::<Ctx>());
|
ops.extend(crate::fetcher::register_ops::<Ctx>());
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::str::FromStr;
|
|||||||
use hashbrown::hash_map::{Entry, HashMap};
|
use hashbrown::hash_map::{Entry, HashMap};
|
||||||
|
|
||||||
use deno_core::OpState;
|
use deno_core::OpState;
|
||||||
|
use deno_core::v8;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rust_embed::Embed;
|
use rust_embed::Embed;
|
||||||
|
|
||||||
@@ -1039,3 +1040,118 @@ pub(super) enum SplitResult {
|
|||||||
Text(String),
|
Text(String),
|
||||||
Captures(Vec<Option<String>>),
|
Captures(Vec<Option<String>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) enum NixJsonValue {
|
||||||
|
Null,
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
Str(String),
|
||||||
|
Arr(Vec<NixJsonValue>),
|
||||||
|
Obj(Vec<(String, NixJsonValue)>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> deno_core::convert::ToV8<'a> for NixJsonValue {
|
||||||
|
type Error = deno_error::JsErrorBox;
|
||||||
|
|
||||||
|
fn to_v8<'i>(
|
||||||
|
self,
|
||||||
|
scope: &mut v8::PinScope<'a, 'i>,
|
||||||
|
) -> std::result::Result<v8::Local<'a, v8::Value>, Self::Error> {
|
||||||
|
match self {
|
||||||
|
Self::Null => Ok(v8::null(scope).into()),
|
||||||
|
Self::Bool(b) => Ok(v8::Boolean::new(scope, b).into()),
|
||||||
|
Self::Int(i) => Ok(v8::BigInt::new_from_i64(scope, i).into()),
|
||||||
|
Self::Float(f) => Ok(v8::Number::new(scope, f).into()),
|
||||||
|
Self::Str(s) => v8::String::new(scope, &s)
|
||||||
|
.map(|s| s.into())
|
||||||
|
.ok_or_else(|| deno_error::JsErrorBox::type_error("failed to create v8 string")),
|
||||||
|
Self::Arr(arr) => {
|
||||||
|
let elements = arr
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| v.to_v8(scope))
|
||||||
|
.collect::<std::result::Result<Vec<_>, _>>()?;
|
||||||
|
Ok(v8::Array::new_with_elements(scope, &elements).into())
|
||||||
|
}
|
||||||
|
Self::Obj(entries) => {
|
||||||
|
let obj = v8::Object::new(scope);
|
||||||
|
for (k, v) in entries {
|
||||||
|
let key: v8::Local<v8::Value> = v8::String::new(scope, &k)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
deno_error::JsErrorBox::type_error("failed to create v8 string")
|
||||||
|
})?
|
||||||
|
.into();
|
||||||
|
let val = v.to_v8(scope)?;
|
||||||
|
obj.set(scope, key, val);
|
||||||
|
}
|
||||||
|
Ok(obj.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn json_to_nix(value: serde_json::Value) -> NixJsonValue {
|
||||||
|
match value {
|
||||||
|
serde_json::Value::Null => NixJsonValue::Null,
|
||||||
|
serde_json::Value::Bool(b) => NixJsonValue::Bool(b),
|
||||||
|
serde_json::Value::Number(n) => {
|
||||||
|
if let Some(i) = n.as_i64() {
|
||||||
|
NixJsonValue::Int(i)
|
||||||
|
} else if let Some(f) = n.as_f64() {
|
||||||
|
NixJsonValue::Float(f)
|
||||||
|
} else {
|
||||||
|
NixJsonValue::Float(n.as_u64().unwrap_or(0) as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
serde_json::Value::String(s) => NixJsonValue::Str(s),
|
||||||
|
serde_json::Value::Array(arr) => {
|
||||||
|
NixJsonValue::Arr(arr.into_iter().map(json_to_nix).collect())
|
||||||
|
}
|
||||||
|
serde_json::Value::Object(map) => {
|
||||||
|
NixJsonValue::Obj(map.into_iter().map(|(k, v)| (k, json_to_nix(v))).collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn toml_to_nix(value: toml::Value) -> std::result::Result<NixJsonValue, NixRuntimeError> {
|
||||||
|
match value {
|
||||||
|
toml::Value::String(s) => Ok(NixJsonValue::Str(s)),
|
||||||
|
toml::Value::Integer(i) => Ok(NixJsonValue::Int(i)),
|
||||||
|
toml::Value::Float(f) => Ok(NixJsonValue::Float(f)),
|
||||||
|
toml::Value::Boolean(b) => Ok(NixJsonValue::Bool(b)),
|
||||||
|
toml::Value::Datetime(_) => Err(NixRuntimeError::from(
|
||||||
|
"while parsing TOML: Dates and times are not supported",
|
||||||
|
)),
|
||||||
|
toml::Value::Array(arr) => {
|
||||||
|
let items: std::result::Result<Vec<_>, _> = arr.into_iter().map(toml_to_nix).collect();
|
||||||
|
Ok(NixJsonValue::Arr(items?))
|
||||||
|
}
|
||||||
|
toml::Value::Table(table) => {
|
||||||
|
let entries: std::result::Result<Vec<_>, _> = table
|
||||||
|
.into_iter()
|
||||||
|
.map(|(k, v)| toml_to_nix(v).map(|v| (k, v)))
|
||||||
|
.collect();
|
||||||
|
Ok(NixJsonValue::Obj(entries?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deno_core::op2]
|
||||||
|
#[to_v8]
|
||||||
|
pub(super) fn op_from_json(
|
||||||
|
#[string] json_str: String,
|
||||||
|
) -> std::result::Result<NixJsonValue, NixRuntimeError> {
|
||||||
|
let parsed: serde_json::Value = serde_json::from_str(&json_str)
|
||||||
|
.map_err(|e| NixRuntimeError::from(format!("builtins.fromJSON: {e}")))?;
|
||||||
|
Ok(json_to_nix(parsed))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[deno_core::op2]
|
||||||
|
#[to_v8]
|
||||||
|
pub(super) fn op_from_toml(
|
||||||
|
#[string] toml_str: String,
|
||||||
|
) -> std::result::Result<NixJsonValue, NixRuntimeError> {
|
||||||
|
let parsed: toml::Value = toml::from_str(&toml_str)
|
||||||
|
.map_err(|e| NixRuntimeError::from(format!("while parsing TOML: {e}")))?;
|
||||||
|
toml_to_nix(parsed)
|
||||||
|
}
|
||||||
|
|||||||
@@ -268,12 +268,66 @@ fn escape_quote_string(s: &str) -> String {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Format a float matching C's `printf("%g", x)` with default precision 6.
|
||||||
|
fn fmt_nix_float(f: &mut Formatter<'_>, x: f64) -> FmtResult {
|
||||||
|
if !x.is_finite() {
|
||||||
|
return write!(f, "{x}");
|
||||||
|
}
|
||||||
|
if x == 0.0 {
|
||||||
|
return if x.is_sign_negative() {
|
||||||
|
write!(f, "-0")
|
||||||
|
} else {
|
||||||
|
write!(f, "0")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let precision: i32 = 6;
|
||||||
|
let exp = x.abs().log10().floor() as i32;
|
||||||
|
|
||||||
|
let formatted = if exp >= -4 && exp < precision {
|
||||||
|
let decimal_places = (precision - 1 - exp) as usize;
|
||||||
|
format!("{x:.decimal_places$}")
|
||||||
|
} else {
|
||||||
|
let sig_digits = (precision - 1) as usize;
|
||||||
|
let s = format!("{x:.sig_digits$e}");
|
||||||
|
let (mantissa, exp_part) = s
|
||||||
|
.split_once('e')
|
||||||
|
.expect("scientific notation must contain 'e'");
|
||||||
|
let (sign, digits) = if let Some(d) = exp_part.strip_prefix('-') {
|
||||||
|
("-", d)
|
||||||
|
} else if let Some(d) = exp_part.strip_prefix('+') {
|
||||||
|
("+", d)
|
||||||
|
} else {
|
||||||
|
("+", exp_part)
|
||||||
|
};
|
||||||
|
if digits.len() < 2 {
|
||||||
|
format!("{mantissa}e{sign}0{digits}")
|
||||||
|
} else {
|
||||||
|
format!("{mantissa}e{sign}{digits}")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if formatted.contains('.') {
|
||||||
|
if let Some(e_pos) = formatted.find('e') {
|
||||||
|
let trimmed = formatted[..e_pos]
|
||||||
|
.trim_end_matches('0')
|
||||||
|
.trim_end_matches('.');
|
||||||
|
write!(f, "{}{}", trimmed, &formatted[e_pos..])
|
||||||
|
} else {
|
||||||
|
let trimmed = formatted.trim_end_matches('0').trim_end_matches('.');
|
||||||
|
write!(f, "{trimmed}")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
write!(f, "{formatted}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Display for Value {
|
impl Display for Value {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||||
use Value::*;
|
use Value::*;
|
||||||
match self {
|
match self {
|
||||||
&Int(x) => write!(f, "{x}"),
|
&Int(x) => write!(f, "{x}"),
|
||||||
&Float(x) => write!(f, "{x}"),
|
&Float(x) => fmt_nix_float(f, x),
|
||||||
&Bool(x) => write!(f, "{x}"),
|
&Bool(x) => write!(f, "{x}"),
|
||||||
Null => write!(f, "null"),
|
Null => write!(f, "null"),
|
||||||
String(x) => write!(f, "{}", escape_quote_string(x)),
|
String(x) => write!(f, "{}", escape_quote_string(x)),
|
||||||
@@ -302,7 +356,7 @@ impl Display for ValueCompatDisplay<'_> {
|
|||||||
use Value::*;
|
use Value::*;
|
||||||
match self.0 {
|
match self.0 {
|
||||||
&Int(x) => write!(f, "{x}"),
|
&Int(x) => write!(f, "{x}"),
|
||||||
&Float(x) => write!(f, "{x}"),
|
&Float(x) => fmt_nix_float(f, x),
|
||||||
&Bool(x) => write!(f, "{x}"),
|
&Bool(x) => write!(f, "{x}"),
|
||||||
Null => write!(f, "null"),
|
Null => write!(f, "null"),
|
||||||
String(x) => write!(f, "{}", escape_quote_string(x)),
|
String(x) => write!(f, "{}", escape_quote_string(x)),
|
||||||
|
|||||||
@@ -152,12 +152,9 @@ eval_okay_test!(foldlStrict_lazy_elements);
|
|||||||
eval_okay_test!(foldlStrict_lazy_initial_accumulator);
|
eval_okay_test!(foldlStrict_lazy_initial_accumulator);
|
||||||
eval_okay_test!(fromjson);
|
eval_okay_test!(fromjson);
|
||||||
eval_okay_test!(fromjson_escapes);
|
eval_okay_test!(fromjson_escapes);
|
||||||
|
eval_okay_test!(fromTOML);
|
||||||
eval_okay_test!(
|
eval_okay_test!(
|
||||||
#[ignore = "not implemented: fromTOML"]
|
#[ignore = "timestamps are not supported"]
|
||||||
fromTOML
|
|
||||||
);
|
|
||||||
eval_okay_test!(
|
|
||||||
#[ignore = "not implemented: fromTOML"]
|
|
||||||
fromTOML_timestamps
|
fromTOML_timestamps
|
||||||
);
|
);
|
||||||
eval_okay_test!(functionargs);
|
eval_okay_test!(functionargs);
|
||||||
|
|||||||
Reference in New Issue
Block a user