@tishlang/tish 1.9.2 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/bin/tish +0 -0
  2. package/crates/js_to_tish/src/transform/expr.rs +8 -6
  3. package/crates/js_to_tish/src/transform/stmt.rs +12 -13
  4. package/crates/tish/Cargo.toml +1 -1
  5. package/crates/tish/src/cargo_native_registry.rs +4 -1
  6. package/crates/tish/src/cli_help.rs +9 -1
  7. package/crates/tish/src/main.rs +66 -11
  8. package/crates/tish/tests/integration_test.rs +145 -7
  9. package/crates/tish_ast/src/ast.rs +3 -9
  10. package/crates/tish_build_utils/src/lib.rs +74 -23
  11. package/crates/tish_builtins/src/array.rs +2 -3
  12. package/crates/tish_builtins/src/construct.rs +15 -28
  13. package/crates/tish_builtins/src/globals.rs +18 -16
  14. package/crates/tish_builtins/src/helpers.rs +1 -4
  15. package/crates/tish_builtins/src/lib.rs +1 -0
  16. package/crates/tish_builtins/src/math.rs +7 -0
  17. package/crates/tish_builtins/src/object.rs +10 -10
  18. package/crates/tish_builtins/src/string.rs +27 -3
  19. package/crates/tish_builtins/src/symbol.rs +83 -0
  20. package/crates/tish_compile/src/codegen.rs +324 -158
  21. package/crates/tish_compile/src/lib.rs +39 -7
  22. package/crates/tish_compile/src/resolve.rs +191 -6
  23. package/crates/tish_compile/src/types.rs +6 -6
  24. package/crates/tish_compile_js/src/codegen.rs +8 -5
  25. package/crates/tish_core/src/console_style.rs +9 -0
  26. package/crates/tish_core/src/json.rs +17 -7
  27. package/crates/tish_core/src/macros.rs +2 -2
  28. package/crates/tish_core/src/value.rs +213 -4
  29. package/crates/tish_cranelift/src/link.rs +1 -1
  30. package/crates/tish_cranelift_runtime/Cargo.toml +4 -0
  31. package/crates/tish_eval/src/eval.rs +135 -73
  32. package/crates/tish_eval/src/http.rs +18 -12
  33. package/crates/tish_eval/src/lib.rs +29 -0
  34. package/crates/tish_eval/src/regex.rs +1 -1
  35. package/crates/tish_eval/src/value.rs +89 -4
  36. package/crates/tish_eval/src/value_convert.rs +30 -8
  37. package/crates/tish_fmt/src/lib.rs +4 -1
  38. package/crates/tish_lexer/src/lib.rs +7 -2
  39. package/crates/tish_llvm/src/lib.rs +2 -2
  40. package/crates/tish_lsp/src/builtin_goto.rs +111 -10
  41. package/crates/tish_lsp/src/import_goto.rs +35 -22
  42. package/crates/tish_lsp/src/main.rs +118 -85
  43. package/crates/tish_native/src/build.rs +270 -24
  44. package/crates/tish_native/src/config.rs +48 -0
  45. package/crates/tish_native/src/lib.rs +139 -12
  46. package/crates/tish_parser/src/lib.rs +5 -2
  47. package/crates/tish_parser/src/parser.rs +45 -75
  48. package/crates/tish_pg/src/error.rs +1 -1
  49. package/crates/tish_pg/src/lib.rs +61 -73
  50. package/crates/tish_resolve/src/lib.rs +283 -158
  51. package/crates/tish_resolve/src/pos.rs +10 -2
  52. package/crates/tish_runtime/Cargo.toml +3 -0
  53. package/crates/tish_runtime/src/http.rs +39 -39
  54. package/crates/tish_runtime/src/http_fetch.rs +12 -12
  55. package/crates/tish_runtime/src/lib.rs +35 -44
  56. package/crates/tish_runtime/src/native_promise.rs +0 -11
  57. package/crates/tish_runtime/src/promise.rs +14 -1
  58. package/crates/tish_runtime/src/promise_io.rs +1 -4
  59. package/crates/tish_runtime/src/timers.rs +12 -7
  60. package/crates/tish_runtime/src/ws.rs +40 -27
  61. package/crates/tish_runtime/tests/fetch_readable_stream.rs +10 -8
  62. package/crates/tish_ui/src/jsx.rs +6 -4
  63. package/crates/tish_ui/src/lib.rs +5 -4
  64. package/crates/tish_ui/src/runtime/hooks.rs +123 -37
  65. package/crates/tish_ui/src/runtime/mod.rs +21 -41
  66. package/crates/tish_vm/Cargo.toml +2 -0
  67. package/crates/tish_vm/src/vm.rs +258 -153
  68. package/crates/tish_wasm/src/lib.rs +60 -7
  69. package/crates/tish_wasm_runtime/Cargo.toml +10 -1
  70. package/crates/tish_wasm_runtime/src/gpu.rs +413 -0
  71. package/crates/tish_wasm_runtime/src/lib.rs +7 -1
  72. package/crates/tishlang_cargo_bindgen/src/classify.rs +1 -3
  73. package/crates/tishlang_cargo_bindgen/src/discover.rs +10 -5
  74. package/crates/tishlang_cargo_bindgen/src/infer.rs +18 -8
  75. package/crates/tishlang_cargo_bindgen/src/lib.rs +25 -26
  76. package/crates/tishlang_cargo_bindgen/src/main.rs +41 -38
  77. package/crates/tishlang_cargo_bindgen/src/metadata.rs +4 -1
  78. package/justfile +3 -3
  79. package/package.json +1 -1
  80. package/platform/darwin-arm64/tish +0 -0
  81. package/platform/darwin-x64/tish +0 -0
  82. package/platform/linux-arm64/tish +0 -0
  83. package/platform/linux-x64/tish +0 -0
  84. package/platform/win32-x64/tish.exe +0 -0
@@ -209,11 +209,11 @@ impl<'a> Parser<'a> {
209
209
  let opened_with_brace = matches!(self.peek_kind(), Some(TokenKind::LBrace));
210
210
  if opened_with_brace {
211
211
  self.advance(); // {
212
- // After `{`, the lexer often emits `Indent` for the first indented line of the body.
213
- // `parse_statement` treats a leading `Indent` as starting a *nested* indent-block, so
214
- // without consuming this token we get `Block { Block { let ... } ; ... }` and the first
215
- // `let`/`const` is scoped too narrowly (JS ReferenceError). This indent is layout for
216
- // *this* brace block, not an inner block.
212
+ // After `{`, the lexer often emits `Indent` for the first indented line of the body.
213
+ // `parse_statement` treats a leading `Indent` as starting a *nested* indent-block, so
214
+ // without consuming this token we get `Block { Block { let ... } ; ... }` and the first
215
+ // `let`/`const` is scoped too narrowly (JS ReferenceError). This indent is layout for
216
+ // *this* brace block, not an inner block.
217
217
  if matches!(self.peek_kind(), Some(TokenKind::Indent)) {
218
218
  self.advance();
219
219
  }
@@ -291,10 +291,7 @@ impl<'a> Parser<'a> {
291
291
  start: name_tok.span.start,
292
292
  end: name_tok.span.end,
293
293
  };
294
- let name = name_tok
295
- .literal
296
- .clone()
297
- .ok_or("Expected identifier")?;
294
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
298
295
 
299
296
  // Optional type annotation: `: Type`
300
297
  let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
@@ -348,10 +345,7 @@ impl<'a> Parser<'a> {
348
345
  start: name_tok.span.start,
349
346
  end: name_tok.span.end,
350
347
  };
351
- let name = name_tok
352
- .literal
353
- .clone()
354
- .ok_or("Expected identifier")?;
348
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
355
349
  elements.push(Some(DestructElement::Rest(name, name_span)));
356
350
  break;
357
351
  }
@@ -368,10 +362,7 @@ impl<'a> Parser<'a> {
368
362
  start: name_tok.span.start,
369
363
  end: name_tok.span.end,
370
364
  };
371
- let name = name_tok
372
- .literal
373
- .clone()
374
- .ok_or("Expected identifier")?;
365
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
375
366
  DestructElement::Ident(name, name_span)
376
367
  }
377
368
  _ => return Err("Expected identifier or pattern in destructuring".to_string()),
@@ -399,10 +390,7 @@ impl<'a> Parser<'a> {
399
390
  start: key_tok.span.start,
400
391
  end: key_tok.span.end,
401
392
  };
402
- let key = key_tok
403
- .literal
404
- .clone()
405
- .ok_or("Expected identifier")?;
393
+ let key = key_tok.literal.clone().ok_or("Expected identifier")?;
406
394
 
407
395
  let value = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
408
396
  self.advance();
@@ -418,10 +406,7 @@ impl<'a> Parser<'a> {
418
406
  start: name_tok.span.start,
419
407
  end: name_tok.span.end,
420
408
  };
421
- let name = name_tok
422
- .literal
423
- .clone()
424
- .ok_or("Expected identifier")?;
409
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
425
410
  DestructElement::Ident(name, name_span)
426
411
  }
427
412
  _ => return Err("Expected identifier or pattern after ':'".to_string()),
@@ -474,10 +459,7 @@ impl<'a> Parser<'a> {
474
459
  start: param_tok.span.start,
475
460
  end: param_tok.span.end,
476
461
  };
477
- let param_name = param_tok
478
- .literal
479
- .clone()
480
- .ok_or("Expected param name")?;
462
+ let param_name = param_tok.literal.clone().ok_or("Expected param name")?;
481
463
  let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
482
464
  self.advance();
483
465
  Some(self.parse_type_annotation()?)
@@ -549,11 +531,27 @@ impl<'a> Parser<'a> {
549
531
  self.advance(); // {
550
532
  let mut props = Vec::new();
551
533
  while !matches!(self.peek_kind(), Some(TokenKind::RBrace)) {
552
- let key = self
553
- .expect(TokenKind::Ident)?
554
- .literal
555
- .clone()
556
- .ok_or("Expected property name")?;
534
+ // `for` is a keyword but a common method name (`Symbol.for`); allow it here.
535
+ let key: Arc<str> = match self.peek_kind() {
536
+ Some(TokenKind::Ident) => {
537
+ let tok = self.expect(TokenKind::Ident)?;
538
+ Arc::from(
539
+ tok.literal
540
+ .as_deref()
541
+ .ok_or("Expected property name")?,
542
+ )
543
+ }
544
+ Some(TokenKind::For) => {
545
+ self.advance();
546
+ Arc::from("for")
547
+ }
548
+ _ => {
549
+ return Err(format!(
550
+ "Expected Ident or `for` as object type property name, got {:?}",
551
+ self.peek_kind()
552
+ ));
553
+ }
554
+ };
557
555
  self.expect(TokenKind::Colon)?;
558
556
  let typ = self.parse_type_annotation()?;
559
557
  props.push((key, typ));
@@ -601,10 +599,7 @@ impl<'a> Parser<'a> {
601
599
  start: name_tok.span.start,
602
600
  end: name_tok.span.end,
603
601
  };
604
- let name = name_tok
605
- .literal
606
- .clone()
607
- .ok_or("Expected function name")?;
602
+ let name = name_tok.literal.clone().ok_or("Expected function name")?;
608
603
  self.expect(TokenKind::LParen)?;
609
604
  let mut params = Vec::with_capacity(4);
610
605
  let mut rest_param = None;
@@ -616,10 +611,7 @@ impl<'a> Parser<'a> {
616
611
  start: rest_tok.span.start,
617
612
  end: rest_tok.span.end,
618
613
  };
619
- let param_name = rest_tok
620
- .literal
621
- .clone()
622
- .ok_or("Expected rest param name")?;
614
+ let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
623
615
  // Optional type annotation for rest param
624
616
  let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
625
617
  self.advance();
@@ -702,10 +694,7 @@ impl<'a> Parser<'a> {
702
694
  start: name_tok.span.start,
703
695
  end: name_tok.span.end,
704
696
  };
705
- let name = name_tok
706
- .literal
707
- .clone()
708
- .ok_or("Expected type alias name")?;
697
+ let name = name_tok.literal.clone().ok_or("Expected type alias name")?;
709
698
  self.expect(TokenKind::Assign)?;
710
699
  let ty = self.parse_type_annotation()?;
711
700
  Ok(Statement::TypeAlias {
@@ -747,10 +736,7 @@ impl<'a> Parser<'a> {
747
736
  start: name_tok.span.start,
748
737
  end: name_tok.span.end,
749
738
  };
750
- let name = name_tok
751
- .literal
752
- .clone()
753
- .ok_or("Expected identifier")?;
739
+ let name = name_tok.literal.clone().ok_or("Expected identifier")?;
754
740
  let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
755
741
  self.advance();
756
742
  Some(self.parse_type_annotation()?)
@@ -780,10 +766,7 @@ impl<'a> Parser<'a> {
780
766
  start: name_tok.span.start,
781
767
  end: name_tok.span.end,
782
768
  };
783
- let name = name_tok
784
- .literal
785
- .clone()
786
- .ok_or("Expected function name")?;
769
+ let name = name_tok.literal.clone().ok_or("Expected function name")?;
787
770
  self.expect(TokenKind::LParen)?;
788
771
  let mut params = Vec::with_capacity(4);
789
772
  let mut rest_param = None;
@@ -795,10 +778,7 @@ impl<'a> Parser<'a> {
795
778
  start: rest_tok.span.start,
796
779
  end: rest_tok.span.end,
797
780
  };
798
- let param_name = rest_tok
799
- .literal
800
- .clone()
801
- .ok_or("Expected rest param name")?;
781
+ let param_name = rest_tok.literal.clone().ok_or("Expected rest param name")?;
802
782
  let type_ann = if matches!(self.peek_kind(), Some(TokenKind::Colon)) {
803
783
  self.advance();
804
784
  Some(self.parse_type_annotation()?)
@@ -893,10 +873,7 @@ impl<'a> Parser<'a> {
893
873
  start: for_name_tok.span.start,
894
874
  end: for_name_tok.span.end,
895
875
  };
896
- let name = for_name_tok
897
- .literal
898
- .clone()
899
- .ok_or("Expected identifier")?;
876
+ let name = for_name_tok.literal.clone().ok_or("Expected identifier")?;
900
877
  if matches!(self.peek_kind(), Some(TokenKind::Of)) {
901
878
  self.advance();
902
879
  let iterable = self.parse_expr()?;
@@ -1186,10 +1163,7 @@ impl<'a> Parser<'a> {
1186
1163
  start: def_tok.span.start,
1187
1164
  end: def_tok.span.end,
1188
1165
  };
1189
- let name = def_tok
1190
- .literal
1191
- .clone()
1192
- .ok_or("Expected identifier")?;
1166
+ let name = def_tok.literal.clone().ok_or("Expected identifier")?;
1193
1167
  vec![ImportSpecifier::Default { name, name_span }]
1194
1168
  } else {
1195
1169
  return Err("Expected { }, * as name, or default import".to_string());
@@ -1271,7 +1245,9 @@ impl<'a> Parser<'a> {
1271
1245
  // Member assignment: obj.prop = val
1272
1246
  if let Expr::Member {
1273
1247
  object,
1274
- prop: MemberProp::Name { name: prop_name, .. },
1248
+ prop: MemberProp::Name {
1249
+ name: prop_name, ..
1250
+ },
1275
1251
  ..
1276
1252
  } = &left
1277
1253
  {
@@ -1510,10 +1486,7 @@ impl<'a> Parser<'a> {
1510
1486
  let optional = kind == TokenKind::OptionalChain;
1511
1487
  self.advance();
1512
1488
  let prop_tok = self.expect_ident_or_type_member_name()?;
1513
- let prop = prop_tok
1514
- .literal
1515
- .clone()
1516
- .ok_or("Expected property name")?;
1489
+ let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
1517
1490
  let prop_span = Span {
1518
1491
  start: prop_tok.span.start,
1519
1492
  end: prop_tok.span.end,
@@ -1630,10 +1603,7 @@ impl<'a> Parser<'a> {
1630
1603
  let optional = kind == TokenKind::OptionalChain;
1631
1604
  self.advance();
1632
1605
  let prop_tok = self.expect_ident_or_type_member_name()?;
1633
- let prop = prop_tok
1634
- .literal
1635
- .clone()
1636
- .ok_or("Expected property name")?;
1606
+ let prop = prop_tok.literal.clone().ok_or("Expected property name")?;
1637
1607
  let prop_span = Span {
1638
1608
  start: prop_tok.span.start,
1639
1609
  end: prop_tok.span.end,
@@ -1,5 +1,5 @@
1
- use std::fmt::Write as _;
2
1
  use std::error::Error as StdError;
2
+ use std::fmt::Write as _;
3
3
 
4
4
  use deadpool::managed::PoolError;
5
5
  use thiserror::Error;
@@ -90,15 +90,9 @@ fn params_to_sql(values: &[JsonValue]) -> Result<Vec<Box<dyn ToSql + Sync + Send
90
90
  } else if let Some(u) = n.as_u64() {
91
91
  Box::new(u as i64)
92
92
  } else if let Some(f) = n.as_f64() {
93
- if f.fract() == 0.0
94
- && f >= i32::MIN as f64
95
- && f <= i32::MAX as f64
96
- {
93
+ if f.fract() == 0.0 && f >= i32::MIN as f64 && f <= i32::MAX as f64 {
97
94
  Box::new(f as i32)
98
- } else if f.fract() == 0.0
99
- && f >= i64::MIN as f64
100
- && f <= i64::MAX as f64
101
- {
95
+ } else if f.fract() == 0.0 && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
102
96
  Box::new(f as i64)
103
97
  } else {
104
98
  Box::new(f)
@@ -110,7 +104,9 @@ fn params_to_sql(values: &[JsonValue]) -> Result<Vec<Box<dyn ToSql + Sync + Send
110
104
  }
111
105
  }
112
106
  JsonValue::String(s) => Box::new(s.clone()),
113
- JsonValue::Array(_) | JsonValue::Object(_) => Box::new(tokio_postgres::types::Json(v.clone())),
107
+ JsonValue::Array(_) | JsonValue::Object(_) => {
108
+ Box::new(tokio_postgres::types::Json(v.clone()))
109
+ }
114
110
  };
115
111
  out.push(b);
116
112
  }
@@ -222,7 +218,7 @@ fn row_to_value_direct(row: &Row) -> tishlang_runtime::Value {
222
218
  };
223
219
  om.insert(key, v);
224
220
  }
225
- RtValue::Object(VmRef::new(om))
221
+ RtValue::object(om)
226
222
  }
227
223
 
228
224
  fn row_to_object(row: &Row) -> Result<JsonValue> {
@@ -378,8 +374,8 @@ pub struct PerWorkerClient {
378
374
  impl PerWorkerClient {
379
375
  /// Open a single direct connection (no pool).
380
376
  pub async fn connect(connection_string: &str) -> Result<Self> {
381
- let cfg = parse_connection_string(connection_string)
382
- .map_err(TishPgError::BadConnectionString)?;
377
+ let cfg =
378
+ parse_connection_string(connection_string).map_err(TishPgError::BadConnectionString)?;
383
379
  let (client, connection) = cfg.connect(NoTls).await?;
384
380
  let driver = tokio::spawn(async move {
385
381
  if let Err(e) = connection.await {
@@ -530,13 +526,11 @@ mod tish_sync {
530
526
  use super::*;
531
527
  use once_cell::sync::Lazy;
532
528
  use slab::Slab;
533
- use std::sync::Mutex;
534
529
  use tishlang_runtime::Value as TishValue;
535
530
  use tokio::runtime::Runtime as TokioRuntime;
536
531
 
537
- static RT: Lazy<TokioRuntime> = Lazy::new(|| {
538
- TokioRuntime::new().expect("tish_pg: failed to build tokio runtime")
539
- });
532
+ static RT: Lazy<TokioRuntime> =
533
+ Lazy::new(|| TokioRuntime::new().expect("tish_pg: failed to build tokio runtime"));
540
534
  // `RwLock` (not `Mutex`) on the registries: hot path is read-only —
541
535
  // every query does `get_client(id)` + `get_statement(id)`. Inserts
542
536
  // happen once per `connect`/`prepare` at startup. With multiple HTTP
@@ -544,8 +538,7 @@ mod tish_sync {
544
538
  // serialised every query through one global lock; `RwLock` lets all
545
539
  // concurrent reads run lock-free against each other.
546
540
  use std::sync::RwLock;
547
- static CLIENTS: Lazy<RwLock<Slab<PerWorkerClient>>> =
548
- Lazy::new(|| RwLock::new(Slab::new()));
541
+ static CLIENTS: Lazy<RwLock<Slab<PerWorkerClient>>> = Lazy::new(|| RwLock::new(Slab::new()));
549
542
  static STATEMENTS: Lazy<RwLock<Slab<(usize, Statement)>>> =
550
543
  Lazy::new(|| RwLock::new(Slab::new()));
551
544
 
@@ -609,12 +602,10 @@ mod tish_sync {
609
602
  .map(JsonValue::Number)
610
603
  .unwrap_or(JsonValue::Null),
611
604
  TishValue::String(s) => JsonValue::String(s.to_string()),
612
- TishValue::Array(a) => {
613
- JsonValue::Array(a.borrow().iter().map(tish_to_json).collect())
614
- }
605
+ TishValue::Array(a) => JsonValue::Array(a.borrow().iter().map(tish_to_json).collect()),
615
606
  TishValue::Object(o) => {
616
607
  let mut m = serde_json::Map::new();
617
- for (k, v) in o.borrow().iter() {
608
+ for (k, v) in o.borrow().strings.iter() {
618
609
  m.insert(k.to_string(), tish_to_json(v));
619
610
  }
620
611
  JsonValue::Object(m)
@@ -623,35 +614,35 @@ mod tish_sync {
623
614
  }
624
615
  }
625
616
 
626
- fn json_to_tish(v: JsonValue) -> TishValue {
627
- use std::cell::RefCell;
628
- use std::rc::Rc;
629
- use std::sync::Arc;
630
- use tishlang_runtime::ObjectMap;
631
- match v {
632
- JsonValue::Null => TishValue::Null,
633
- JsonValue::Bool(b) => TishValue::Bool(b),
634
- JsonValue::Number(n) => TishValue::Number(n.as_f64().unwrap_or(0.0)),
635
- JsonValue::String(s) => TishValue::String(s.into()),
636
- JsonValue::Array(a) => {
637
- let mut out = Vec::with_capacity(a.len());
638
- for item in a {
639
- out.push(json_to_tish(item));
640
- }
641
- TishValue::Array(VmRef::new(out))
642
- }
643
- JsonValue::Object(m) => {
644
- // Pre-allocate ObjectMap capacity so HashMap doesn't rehash
645
- // on every insert. Common TFB rows are 2 columns (id,
646
- // randomnumber or id, message).
647
- let mut om = ObjectMap::with_capacity(m.len());
648
- for (k, v) in m {
649
- om.insert(Arc::from(k), json_to_tish(v));
650
- }
651
- TishValue::Object(VmRef::new(om))
652
- }
653
- }
654
- }
617
+ // fn json_to_tish(v: JsonValue) -> TishValue {
618
+ // use std::cell::RefCell;
619
+ // use std::rc::Rc;
620
+ // use std::sync::Arc;
621
+ // use tishlang_runtime::ObjectMap;
622
+ // match v {
623
+ // JsonValue::Null => TishValue::Null,
624
+ // JsonValue::Bool(b) => TishValue::Bool(b),
625
+ // JsonValue::Number(n) => TishValue::Number(n.as_f64().unwrap_or(0.0)),
626
+ // JsonValue::String(s) => TishValue::String(s.into()),
627
+ // JsonValue::Array(a) => {
628
+ // let mut out = Vec::with_capacity(a.len());
629
+ // for item in a {
630
+ // out.push(json_to_tish(item));
631
+ // }
632
+ // TishValue::Array(VmRef::new(out))
633
+ // }
634
+ // JsonValue::Object(m) => {
635
+ // // Pre-allocate ObjectMap capacity so HashMap doesn't rehash
636
+ // // on every insert. Common TFB rows are 2 columns (id,
637
+ // // randomnumber or id, message).
638
+ // let mut om = ObjectMap::with_capacity(m.len());
639
+ // for (k, v) in m {
640
+ // om.insert(Arc::from(k), json_to_tish(v));
641
+ // }
642
+ // TishValue::object(om)
643
+ // }
644
+ // }
645
+ // }
655
646
 
656
647
  fn tish_err(msg: impl Into<String>) -> TishValue {
657
648
  use std::sync::Arc;
@@ -659,16 +650,14 @@ mod tish_sync {
659
650
  let mut om = ObjectMap::with_capacity(2);
660
651
  om.insert(Arc::from("error"), TishValue::String(msg.into().into()));
661
652
  om.insert(Arc::from("ok"), TishValue::Bool(false));
662
- TishValue::Object(VmRef::new(om))
653
+ TishValue::object(om)
663
654
  }
664
655
 
665
- fn rows_to_value(res: QueryResult) -> TishValue {
666
- use std::cell::RefCell;
667
- use std::rc::Rc;
668
- TishValue::Array(VmRef::new(
669
- res.rows.into_iter().map(json_to_tish).collect(),
670
- ))
671
- }
656
+ // fn rows_to_value(res: QueryResult) -> TishValue {
657
+ // use std::cell::RefCell;
658
+ // use std::rc::Rc;
659
+ // TishValue::Array(VmRef::new(res.rows.into_iter().map(json_to_tish).collect()))
660
+ // }
672
661
 
673
662
  /// `perWorkerClient(connection_string) -> client_handle` (blocking).
674
663
  pub fn per_worker_client(args: &[TishValue]) -> TishValue {
@@ -682,7 +671,10 @@ mod tish_sync {
682
671
  let id = g.insert(c);
683
672
  TishValue::Number(id as f64)
684
673
  }
685
- Err(e) => tish_err(format!("perWorkerClient: {}", crate::format_tish_pg_error(&e))),
674
+ Err(e) => tish_err(format!(
675
+ "perWorkerClient: {}",
676
+ crate::format_tish_pg_error(&e)
677
+ )),
686
678
  }
687
679
  }
688
680
 
@@ -694,9 +686,8 @@ mod tish_sync {
694
686
  let cs = match args.first() {
695
687
  Some(TishValue::String(s)) => s.to_string(),
696
688
  Some(TishValue::Object(obj)) => {
697
- use std::sync::Arc;
698
689
  let b = obj.borrow();
699
- match b.get(&Arc::from("connectionString")) {
690
+ match b.strings.get("connectionString") {
700
691
  Some(TishValue::String(s)) => s.to_string(),
701
692
  _ => return tish_err("connect: options.connectionString missing"),
702
693
  }
@@ -754,11 +745,7 @@ mod tish_sync {
754
745
  return tish_err("queryPrepared: expected (client, stmt, params)");
755
746
  };
756
747
  let params = match args.get(2) {
757
- Some(TishValue::Array(a)) => a
758
- .borrow()
759
- .iter()
760
- .map(tish_to_json)
761
- .collect::<Vec<_>>(),
748
+ Some(TishValue::Array(a)) => a.borrow().iter().map(tish_to_json).collect::<Vec<_>>(),
762
749
  Some(TishValue::Null) | None => Vec::new(),
763
750
  Some(v) => vec![tish_to_json(v)],
764
751
  };
@@ -773,7 +760,10 @@ mod tish_sync {
773
760
  // produces. Fewer allocations + interned column-name `Arc`s.
774
761
  match block_on(client.query_prepared_to_values(&stmt, &params)) {
775
762
  Ok(rows) => TishValue::Array(VmRef::new(rows)),
776
- Err(e) => tish_err(format!("queryPrepared: {}", crate::format_tish_pg_error(&e))),
763
+ Err(e) => tish_err(format!(
764
+ "queryPrepared: {}",
765
+ crate::format_tish_pg_error(&e)
766
+ )),
777
767
  }
778
768
  }
779
769
 
@@ -895,10 +885,8 @@ mod tish_sync {
895
885
  let rows = raw_client
896
886
  .query("SELECT name FROM _tish_pg_migrations", &[])
897
887
  .await?;
898
- let already: std::collections::HashSet<String> = rows
899
- .iter()
900
- .map(|r| r.get::<_, String>(0))
901
- .collect();
888
+ let already: std::collections::HashSet<String> =
889
+ rows.iter().map(|r| r.get::<_, String>(0)).collect();
902
890
 
903
891
  let mut applied_now: Vec<String> = Vec::new();
904
892
  for (name, path) in &files {
@@ -942,7 +930,7 @@ mod tish_sync {
942
930
  let mut om = ObjectMap::with_capacity(2);
943
931
  om.insert(Arc::from("ok"), TishValue::Bool(true));
944
932
  om.insert(Arc::from("applied"), TishValue::Array(VmRef::new(applied)));
945
- TishValue::Object(VmRef::new(om))
933
+ TishValue::object(om)
946
934
  }
947
935
  }
948
936