polars-df 0.23.0 → 0.24.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 (146) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -1
  3. data/Cargo.lock +72 -58
  4. data/README.md +31 -27
  5. data/ext/polars/Cargo.toml +15 -6
  6. data/ext/polars/src/batched_csv.rs +35 -39
  7. data/ext/polars/src/c_api/allocator.rs +7 -0
  8. data/ext/polars/src/c_api/mod.rs +1 -0
  9. data/ext/polars/src/catalog/unity.rs +123 -101
  10. data/ext/polars/src/conversion/any_value.rs +13 -17
  11. data/ext/polars/src/conversion/chunked_array.rs +5 -5
  12. data/ext/polars/src/conversion/datetime.rs +3 -2
  13. data/ext/polars/src/conversion/mod.rs +50 -45
  14. data/ext/polars/src/dataframe/export.rs +13 -13
  15. data/ext/polars/src/dataframe/general.rs +223 -223
  16. data/ext/polars/src/dataframe/io.rs +27 -141
  17. data/ext/polars/src/dataframe/mod.rs +13 -5
  18. data/ext/polars/src/dataframe/serde.rs +1 -1
  19. data/ext/polars/src/error.rs +44 -7
  20. data/ext/polars/src/exceptions.rs +45 -12
  21. data/ext/polars/src/expr/array.rs +12 -0
  22. data/ext/polars/src/expr/datatype.rs +2 -2
  23. data/ext/polars/src/expr/datetime.rs +4 -5
  24. data/ext/polars/src/expr/general.rs +49 -13
  25. data/ext/polars/src/expr/list.rs +4 -0
  26. data/ext/polars/src/expr/meta.rs +8 -3
  27. data/ext/polars/src/expr/mod.rs +22 -6
  28. data/ext/polars/src/expr/name.rs +19 -8
  29. data/ext/polars/src/expr/rolling.rs +50 -1
  30. data/ext/polars/src/expr/string.rs +0 -1
  31. data/ext/polars/src/expr/struct.rs +7 -2
  32. data/ext/polars/src/file.rs +136 -103
  33. data/ext/polars/src/functions/aggregation.rs +9 -8
  34. data/ext/polars/src/functions/io.rs +81 -10
  35. data/ext/polars/src/functions/lazy.rs +95 -21
  36. data/ext/polars/src/functions/mod.rs +2 -0
  37. data/ext/polars/src/functions/range.rs +19 -3
  38. data/ext/polars/src/functions/strings.rs +6 -0
  39. data/ext/polars/src/functions/utils.rs +6 -0
  40. data/ext/polars/src/interop/arrow/mod.rs +50 -1
  41. data/ext/polars/src/interop/arrow/{to_ruby.rs → to_rb.rs} +30 -0
  42. data/ext/polars/src/interop/arrow/to_rust.rs +43 -0
  43. data/ext/polars/src/interop/numo/to_numo_df.rs +1 -1
  44. data/ext/polars/src/interop/numo/to_numo_series.rs +1 -1
  45. data/ext/polars/src/lazyframe/exitable.rs +39 -0
  46. data/ext/polars/src/lazyframe/general.rs +340 -236
  47. data/ext/polars/src/lazyframe/mod.rs +46 -10
  48. data/ext/polars/src/lazyframe/optflags.rs +5 -4
  49. data/ext/polars/src/lazyframe/serde.rs +11 -3
  50. data/ext/polars/src/lazyframe/sink.rs +10 -5
  51. data/ext/polars/src/lazygroupby.rs +6 -7
  52. data/ext/polars/src/lib.rs +141 -76
  53. data/ext/polars/src/map/dataframe.rs +12 -12
  54. data/ext/polars/src/map/lazy.rs +7 -5
  55. data/ext/polars/src/map/mod.rs +15 -8
  56. data/ext/polars/src/map/series.rs +3 -3
  57. data/ext/polars/src/on_startup.rs +16 -8
  58. data/ext/polars/src/prelude.rs +1 -0
  59. data/ext/polars/src/rb_modules.rs +19 -49
  60. data/ext/polars/src/series/aggregation.rs +79 -140
  61. data/ext/polars/src/series/arithmetic.rs +16 -22
  62. data/ext/polars/src/series/comparison.rs +101 -222
  63. data/ext/polars/src/series/construction.rs +17 -18
  64. data/ext/polars/src/series/export.rs +1 -1
  65. data/ext/polars/src/series/general.rs +254 -289
  66. data/ext/polars/src/series/import.rs +17 -0
  67. data/ext/polars/src/series/map.rs +178 -160
  68. data/ext/polars/src/series/mod.rs +28 -12
  69. data/ext/polars/src/series/scatter.rs +12 -9
  70. data/ext/polars/src/sql.rs +16 -9
  71. data/ext/polars/src/testing/frame.rs +31 -0
  72. data/ext/polars/src/testing/mod.rs +5 -0
  73. data/ext/polars/src/testing/series.rs +31 -0
  74. data/ext/polars/src/timeout.rs +105 -0
  75. data/ext/polars/src/utils.rs +159 -1
  76. data/lib/polars/array_expr.rb +81 -12
  77. data/lib/polars/array_name_space.rb +74 -7
  78. data/lib/polars/batched_csv_reader.rb +21 -21
  79. data/lib/polars/binary_name_space.rb +1 -1
  80. data/lib/polars/cat_expr.rb +7 -7
  81. data/lib/polars/config.rb +1 -1
  82. data/lib/polars/convert.rb +189 -34
  83. data/lib/polars/data_frame.rb +1066 -831
  84. data/lib/polars/data_frame_plot.rb +173 -0
  85. data/lib/polars/data_type_group.rb +1 -0
  86. data/lib/polars/data_types.rb +31 -12
  87. data/lib/polars/date_time_expr.rb +51 -69
  88. data/lib/polars/date_time_name_space.rb +80 -112
  89. data/lib/polars/dynamic_group_by.rb +7 -7
  90. data/lib/polars/exceptions.rb +50 -10
  91. data/lib/polars/expr.rb +470 -517
  92. data/lib/polars/functions/aggregation/horizontal.rb +0 -1
  93. data/lib/polars/functions/aggregation/vertical.rb +2 -3
  94. data/lib/polars/functions/as_datatype.rb +290 -8
  95. data/lib/polars/functions/eager.rb +204 -10
  96. data/lib/polars/functions/escape_regex.rb +21 -0
  97. data/lib/polars/functions/lazy.rb +409 -169
  98. data/lib/polars/functions/lit.rb +17 -1
  99. data/lib/polars/functions/range/int_range.rb +74 -2
  100. data/lib/polars/functions/range/linear_space.rb +77 -0
  101. data/lib/polars/functions/range/time_range.rb +1 -1
  102. data/lib/polars/functions/repeat.rb +3 -12
  103. data/lib/polars/functions/whenthen.rb +2 -2
  104. data/lib/polars/group_by.rb +72 -20
  105. data/lib/polars/iceberg_dataset.rb +1 -6
  106. data/lib/polars/in_process_query.rb +37 -0
  107. data/lib/polars/io/cloud.rb +18 -0
  108. data/lib/polars/io/csv.rb +265 -126
  109. data/lib/polars/io/database.rb +0 -1
  110. data/lib/polars/io/delta.rb +15 -7
  111. data/lib/polars/io/ipc.rb +24 -17
  112. data/lib/polars/io/ndjson.rb +161 -24
  113. data/lib/polars/io/parquet.rb +101 -38
  114. data/lib/polars/lazy_frame.rb +849 -558
  115. data/lib/polars/lazy_group_by.rb +327 -2
  116. data/lib/polars/list_expr.rb +94 -16
  117. data/lib/polars/list_name_space.rb +88 -24
  118. data/lib/polars/meta_expr.rb +42 -1
  119. data/lib/polars/name_expr.rb +41 -4
  120. data/lib/polars/query_opt_flags.rb +198 -2
  121. data/lib/polars/rolling_group_by.rb +3 -3
  122. data/lib/polars/schema.rb +21 -3
  123. data/lib/polars/selector.rb +37 -2
  124. data/lib/polars/selectors.rb +45 -9
  125. data/lib/polars/series.rb +1156 -728
  126. data/lib/polars/series_plot.rb +72 -0
  127. data/lib/polars/slice.rb +1 -1
  128. data/lib/polars/sql_context.rb +11 -4
  129. data/lib/polars/string_expr.rb +59 -68
  130. data/lib/polars/string_name_space.rb +51 -87
  131. data/lib/polars/struct_expr.rb +36 -18
  132. data/lib/polars/testing.rb +24 -273
  133. data/lib/polars/utils/constants.rb +2 -0
  134. data/lib/polars/utils/construction/data_frame.rb +410 -0
  135. data/lib/polars/utils/construction/series.rb +364 -0
  136. data/lib/polars/utils/construction/utils.rb +9 -0
  137. data/lib/polars/utils/deprecation.rb +11 -0
  138. data/lib/polars/utils/serde.rb +8 -3
  139. data/lib/polars/utils/unstable.rb +19 -0
  140. data/lib/polars/utils/various.rb +59 -0
  141. data/lib/polars/utils.rb +46 -47
  142. data/lib/polars/version.rb +1 -1
  143. data/lib/polars.rb +47 -1
  144. metadata +25 -6
  145. data/ext/polars/src/allocator.rs +0 -13
  146. data/lib/polars/plot.rb +0 -109
@@ -15,7 +15,7 @@ mod serde;
15
15
  mod string;
16
16
  mod r#struct;
17
17
 
18
- use magnus::{RArray, prelude::*};
18
+ use magnus::{RArray, Ruby, prelude::*};
19
19
  use polars::lazy::dsl::Expr;
20
20
 
21
21
  use crate::RbResult;
@@ -32,10 +32,26 @@ impl From<Expr> for RbExpr {
32
32
  }
33
33
  }
34
34
 
35
- pub fn rb_exprs_to_exprs(rb_exprs: RArray) -> RbResult<Vec<Expr>> {
36
- let mut exprs = Vec::new();
37
- for item in rb_exprs.into_iter() {
38
- exprs.push(<&RbExpr>::try_convert(item)?.inner.clone());
35
+ pub(crate) trait ToExprs {
36
+ fn to_exprs(self) -> RbResult<Vec<Expr>>;
37
+ }
38
+
39
+ impl ToExprs for RArray {
40
+ fn to_exprs(self) -> RbResult<Vec<Expr>> {
41
+ let mut exprs = Vec::new();
42
+ for item in self.into_iter() {
43
+ exprs.push(<&RbExpr>::try_convert(item)?.inner.clone());
44
+ }
45
+ Ok(exprs)
46
+ }
47
+ }
48
+
49
+ pub(crate) trait ToRbExprs {
50
+ fn to_rbexprs(self, rb: &Ruby) -> RArray;
51
+ }
52
+
53
+ impl ToRbExprs for Vec<Expr> {
54
+ fn to_rbexprs(self, rb: &Ruby) -> RArray {
55
+ rb.ary_from_iter(self.iter().map(|e| RbExpr::from(e.clone())))
39
56
  }
40
- Ok(exprs)
41
57
  }
@@ -3,6 +3,7 @@ use polars::prelude::*;
3
3
  use polars_utils::format_pl_smallstr;
4
4
 
5
5
  use crate::RbExpr;
6
+ use crate::utils::RubyAttach;
6
7
 
7
8
  impl RbExpr {
8
9
  pub fn name_keep(&self) -> Self {
@@ -12,14 +13,16 @@ impl RbExpr {
12
13
  pub fn name_map(&self, lambda: Proc) -> Self {
13
14
  let lambda = Opaque::from(lambda);
14
15
  let func = PlanCallback::new(move |name: PlSmallStr| {
15
- let lambda = Ruby::get().unwrap().get_inner(lambda);
16
- let out = lambda.call::<_, String>((name.as_str(),));
17
- match out {
18
- Ok(out) => Ok(format_pl_smallstr!("{}", out)),
19
- Err(e) => Err(PolarsError::ComputeError(
20
- format!("Ruby function in 'name.map' produced an error: {e}.").into(),
21
- )),
22
- }
16
+ Ruby::attach(|rb| {
17
+ let lambda = rb.get_inner(lambda);
18
+ let out = lambda.call::<_, String>((name.as_str(),));
19
+ match out {
20
+ Ok(out) => Ok(format_pl_smallstr!("{}", out)),
21
+ Err(e) => Err(PolarsError::ComputeError(
22
+ format!("Ruby function in 'name.map' produced an error: {e}.").into(),
23
+ )),
24
+ }
25
+ })
23
26
  });
24
27
  self.inner.clone().name().map(func).into()
25
28
  }
@@ -40,6 +43,14 @@ impl RbExpr {
40
43
  self.inner.clone().name().to_uppercase().into()
41
44
  }
42
45
 
46
+ pub fn name_replace(&self, pattern: String, value: String, literal: bool) -> Self {
47
+ self.inner
48
+ .clone()
49
+ .name()
50
+ .replace(&pattern, &value, literal)
51
+ .into()
52
+ }
53
+
43
54
  pub fn name_prefix_fields(&self, prefix: String) -> Self {
44
55
  self.inner.clone().name().prefix_fields(&prefix).into()
45
56
  }
@@ -1,7 +1,7 @@
1
1
  use polars::prelude::*;
2
2
 
3
- use crate::RbExpr;
4
3
  use crate::conversion::Wrap;
4
+ use crate::{RbExpr, RbPolarsErr, RbResult};
5
5
 
6
6
  impl RbExpr {
7
7
  pub fn rolling_sum(
@@ -319,6 +319,55 @@ impl RbExpr {
319
319
  .into()
320
320
  }
321
321
 
322
+ pub fn rolling_rank(
323
+ &self,
324
+ window_size: usize,
325
+ method: Wrap<RollingRankMethod>,
326
+ seed: Option<u64>,
327
+ min_samples: Option<usize>,
328
+ center: bool,
329
+ ) -> Self {
330
+ let min_samples = min_samples.unwrap_or(window_size);
331
+ let options = RollingOptionsFixedWindow {
332
+ window_size,
333
+ min_periods: min_samples,
334
+ weights: None,
335
+ center,
336
+ fn_params: Some(RollingFnParams::Rank {
337
+ method: method.0,
338
+ seed,
339
+ }),
340
+ };
341
+
342
+ self.inner.clone().rolling_rank(options).into()
343
+ }
344
+
345
+ pub fn rolling_rank_by(
346
+ &self,
347
+ by: &RbExpr,
348
+ window_size: String,
349
+ method: Wrap<RollingRankMethod>,
350
+ seed: Option<u64>,
351
+ min_samples: usize,
352
+ closed: Wrap<ClosedWindow>,
353
+ ) -> RbResult<Self> {
354
+ let options = RollingOptionsDynamicWindow {
355
+ window_size: Duration::try_parse(&window_size).map_err(RbPolarsErr::from)?,
356
+ min_periods: min_samples,
357
+ closed_window: closed.0,
358
+ fn_params: Some(RollingFnParams::Rank {
359
+ method: method.0,
360
+ seed,
361
+ }),
362
+ };
363
+
364
+ Ok(self
365
+ .inner
366
+ .clone()
367
+ .rolling_rank_by(by.inner.clone(), options)
368
+ .into())
369
+ }
370
+
322
371
  pub fn rolling_skew(
323
372
  &self,
324
373
  window_size: usize,
@@ -30,7 +30,6 @@ impl RbExpr {
30
30
  self.inner.clone().str().to_date(options).into()
31
31
  }
32
32
 
33
- #[allow(clippy::too_many_arguments)]
34
33
  pub fn str_to_datetime(
35
34
  &self,
36
35
  format: Option<String>,
@@ -1,6 +1,7 @@
1
1
  use magnus::RArray;
2
2
 
3
- use crate::{RbExpr, RbResult, rb_exprs_to_exprs};
3
+ use crate::expr::ToExprs;
4
+ use crate::{RbExpr, RbResult};
4
5
 
5
6
  impl RbExpr {
6
7
  pub fn struct_field_by_index(&self, index: i64) -> Self {
@@ -11,6 +12,10 @@ impl RbExpr {
11
12
  self.inner.clone().struct_().field_by_name(&name).into()
12
13
  }
13
14
 
15
+ pub fn struct_multiple_fields(&self, names: Vec<String>) -> Self {
16
+ self.inner.clone().struct_().field_by_names(&names).into()
17
+ }
18
+
14
19
  pub fn struct_rename_fields(&self, names: Vec<String>) -> Self {
15
20
  self.inner.clone().struct_().rename_fields(names).into()
16
21
  }
@@ -20,7 +25,7 @@ impl RbExpr {
20
25
  }
21
26
 
22
27
  pub fn struct_with_fields(&self, fields: RArray) -> RbResult<Self> {
23
- let fields = rb_exprs_to_exprs(fields)?;
28
+ let fields = fields.to_exprs()?;
24
29
  let e = self.inner.clone().struct_().with_fields(fields);
25
30
  Ok(e.into())
26
31
  }
@@ -4,69 +4,90 @@ use std::io::{Cursor, Read, Seek, SeekFrom, Write};
4
4
  use std::path::PathBuf;
5
5
 
6
6
  use magnus::{Error, RString, Ruby, Value, prelude::*, value::Opaque};
7
- use polars::io::cloud::CloudOptions;
8
7
  use polars::io::mmap::MmapBytesReader;
9
8
  use polars::prelude::PlPath;
10
9
  use polars::prelude::file::DynWriteable;
11
10
  use polars::prelude::sync_on_close::SyncOnCloseType;
12
- use polars_utils::file::ClosableFile;
11
+ use polars_utils::create_file;
12
+ use polars_utils::file::{ClosableFile, WriteClose};
13
13
  use polars_utils::mmap::MemSlice;
14
14
 
15
- use crate::RbResult;
16
15
  use crate::error::RbPolarsErr;
17
16
  use crate::prelude::resolve_homedir;
17
+ use crate::utils::RubyAttach;
18
+ use crate::{RbErr, RbResult};
18
19
 
19
- #[derive(Clone)]
20
20
  pub struct RbFileLikeObject {
21
21
  inner: Opaque<Value>,
22
+ expects_str: bool,
23
+ has_flush: bool,
22
24
  }
23
25
 
26
+ impl WriteClose for RbFileLikeObject {}
27
+
24
28
  impl DynWriteable for RbFileLikeObject {
25
29
  fn as_dyn_write(&self) -> &(dyn io::Write + Send + 'static) {
26
30
  self as _
27
31
  }
32
+
28
33
  fn as_mut_dyn_write(&mut self) -> &mut (dyn io::Write + Send + 'static) {
29
34
  self as _
30
35
  }
36
+
31
37
  fn close(self: Box<Self>) -> io::Result<()> {
32
38
  Ok(())
33
39
  }
40
+
34
41
  fn sync_on_close(&mut self, _sync_on_close: SyncOnCloseType) -> io::Result<()> {
35
42
  Ok(())
36
43
  }
37
44
  }
38
45
 
46
+ impl Clone for RbFileLikeObject {
47
+ fn clone(&self) -> Self {
48
+ Ruby::attach(|_rb| Self {
49
+ // TODO clone
50
+ inner: self.inner,
51
+ expects_str: self.expects_str,
52
+ has_flush: self.has_flush,
53
+ })
54
+ }
55
+ }
56
+
39
57
  /// Wraps a `Value`, and implements read, seek, and write for it.
40
58
  impl RbFileLikeObject {
41
59
  /// Creates an instance of a `RbFileLikeObject` from a `Value`.
42
60
  /// To assert the object has the required methods methods,
43
61
  /// instantiate it with `RbFileLikeObject::require`
44
- pub fn new(object: Value) -> Self {
62
+ pub fn new(object: Value, expects_str: bool, has_flush: bool) -> Self {
45
63
  RbFileLikeObject {
46
64
  inner: object.into(),
65
+ expects_str,
66
+ has_flush,
47
67
  }
48
68
  }
49
69
 
50
- pub fn as_bytes(&self) -> bytes::Bytes {
51
- self.as_file_buffer().into_inner().into()
52
- }
53
-
54
- pub fn as_file_buffer(&self) -> Cursor<Vec<u8>> {
55
- let bytes = Ruby::get()
56
- .unwrap()
57
- .get_inner(self.inner)
58
- .funcall::<_, _, RString>("read", ())
59
- .expect("no read method found");
60
-
61
- let buf = unsafe { bytes.as_slice() }.to_vec();
70
+ pub(crate) fn to_memslice(&self) -> MemSlice {
71
+ Ruby::attach(|rb| {
72
+ let bytes = rb
73
+ .get_inner(self.inner)
74
+ .funcall::<_, _, RString>("read", ())
75
+ .expect("no read method found");
62
76
 
63
- Cursor::new(buf)
77
+ let b = unsafe { bytes.as_slice() }.to_vec();
78
+ MemSlice::from_vec(b)
79
+ })
64
80
  }
65
81
 
66
82
  /// Same as `RbFileLikeObject::new`, but validates that the underlying
67
83
  /// ruby object has a `read`, `write`, and `seek` methods in respect to parameters.
68
84
  /// Will return a `TypeError` if object does not have `read`, `seek`, and `write` methods.
69
- pub fn with_requirements(object: Value, read: bool, write: bool, seek: bool) -> RbResult<Self> {
85
+ pub fn ensure_requirements(
86
+ object: Value,
87
+ read: bool,
88
+ write: bool,
89
+ seek: bool,
90
+ ) -> RbResult<Self> {
70
91
  let ruby = Ruby::get_with(object);
71
92
 
72
93
  if read && !object.respond_to("read", false)? {
@@ -90,48 +111,61 @@ impl RbFileLikeObject {
90
111
  ));
91
112
  }
92
113
 
93
- Ok(RbFileLikeObject::new(object))
114
+ // TODO fix
115
+ let expects_str = false;
116
+ let has_flush = object.respond_to("write", false)?;
117
+ Ok(RbFileLikeObject::new(object, expects_str, has_flush))
94
118
  }
95
119
  }
96
120
 
97
121
  /// Extracts a string repr from, and returns an IO error to send back to rust.
98
- fn rberr_to_io_err(e: Error) -> io::Error {
99
- io::Error::other(e.to_string())
122
+ fn rberr_to_io_err(e: RbErr) -> io::Error {
123
+ Ruby::attach(|_rb| io::Error::other(e.to_string()))
100
124
  }
101
125
 
102
126
  impl Read for RbFileLikeObject {
103
127
  fn read(&mut self, mut buf: &mut [u8]) -> Result<usize, io::Error> {
104
- let bytes = Ruby::get()
105
- .unwrap()
106
- .get_inner(self.inner)
107
- .funcall::<_, _, RString>("read", (buf.len(),))
108
- .map_err(rberr_to_io_err)?;
128
+ Ruby::attach(|rb| {
129
+ let bytes = rb
130
+ .get_inner(self.inner)
131
+ .funcall::<_, _, RString>("read", (buf.len(),))
132
+ .map_err(rberr_to_io_err)?;
109
133
 
110
- buf.write_all(unsafe { bytes.as_slice() })?;
134
+ buf.write_all(unsafe { bytes.as_slice() })?;
111
135
 
112
- Ok(bytes.len())
136
+ Ok(bytes.len())
137
+ })
113
138
  }
114
139
  }
115
140
 
116
141
  impl Write for RbFileLikeObject {
117
142
  fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
118
- let ruby = Ruby::get().unwrap();
119
- let rbbytes = ruby.str_from_slice(buf);
143
+ let expects_str = self.expects_str;
120
144
 
121
- let number_bytes_written = ruby
122
- .get_inner(self.inner)
123
- .funcall::<_, _, usize>("write", (rbbytes,))
124
- .map_err(rberr_to_io_err)?;
145
+ Ruby::attach(|rb| {
146
+ if expects_str {
147
+ todo!();
148
+ }
149
+
150
+ let rbbytes = rb.str_from_slice(buf);
151
+
152
+ let number_bytes_written = rb
153
+ .get_inner(self.inner)
154
+ .funcall::<_, _, usize>("write", (rbbytes,))
155
+ .map_err(rberr_to_io_err)?;
125
156
 
126
- Ok(number_bytes_written)
157
+ Ok(number_bytes_written)
158
+ })
127
159
  }
128
160
 
129
161
  fn flush(&mut self) -> Result<(), io::Error> {
130
- Ruby::get()
131
- .unwrap()
132
- .get_inner(self.inner)
133
- .funcall::<_, _, Value>("flush", ())
134
- .map_err(rberr_to_io_err)?;
162
+ if self.has_flush {
163
+ Ruby::attach(|rb| {
164
+ rb.get_inner(self.inner)
165
+ .funcall::<_, _, Value>("flush", ())
166
+ .map_err(rberr_to_io_err)
167
+ })?;
168
+ }
135
169
 
136
170
  Ok(())
137
171
  }
@@ -139,19 +173,21 @@ impl Write for RbFileLikeObject {
139
173
 
140
174
  impl Seek for RbFileLikeObject {
141
175
  fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
142
- let (whence, offset) = match pos {
143
- SeekFrom::Start(i) => (0, i as i64),
144
- SeekFrom::Current(i) => (1, i),
145
- SeekFrom::End(i) => (2, i),
146
- };
176
+ Ruby::attach(|rb| {
177
+ let (whence, offset) = match pos {
178
+ SeekFrom::Start(i) => (0, i as i64),
179
+ SeekFrom::Current(i) => (1, i),
180
+ SeekFrom::End(i) => (2, i),
181
+ };
147
182
 
148
- let inner = Ruby::get().unwrap().get_inner(self.inner);
183
+ let inner = rb.get_inner(self.inner);
149
184
 
150
- inner
151
- .funcall::<_, _, Value>("seek", (offset, whence))
152
- .map_err(rberr_to_io_err)?;
185
+ inner
186
+ .funcall::<_, _, Value>("seek", (offset, whence))
187
+ .map_err(rberr_to_io_err)?;
153
188
 
154
- inner.funcall("tell", ()).map_err(rberr_to_io_err)
189
+ inner.funcall("tell", ()).map_err(rberr_to_io_err)
190
+ })
155
191
  }
156
192
  }
157
193
 
@@ -174,17 +210,18 @@ impl EitherRustRubyFile {
174
210
  }
175
211
  }
176
212
 
177
- pub(crate) fn into_writeable(self) -> Box<dyn DynWriteable> {
213
+ #[allow(dead_code)]
214
+ fn into_scan_source_input(self) -> RubyScanSourceInput {
178
215
  match self {
179
- Self::Rb(f) => Box::new(f),
180
- Self::Rust(f) => Box::new(f),
216
+ EitherRustRubyFile::Rb(f) => RubyScanSourceInput::Buffer(f.to_memslice()),
217
+ EitherRustRubyFile::Rust(f) => RubyScanSourceInput::File(f),
181
218
  }
182
219
  }
183
220
 
184
- pub fn into_dyn_writeable(self) -> Box<dyn Write> {
221
+ pub(crate) fn into_writeable(self) -> Box<dyn DynWriteable> {
185
222
  match self {
186
- EitherRustRubyFile::Rb(f) => Box::new(f),
187
- EitherRustRubyFile::Rust(f) => Box::new(f),
223
+ Self::Rb(f) => Box::new(f),
224
+ Self::Rust(f) => Box::new(f),
188
225
  }
189
226
  }
190
227
  }
@@ -193,52 +230,61 @@ pub enum RubyScanSourceInput {
193
230
  Buffer(MemSlice),
194
231
  Path(PlPath),
195
232
  #[allow(dead_code)]
196
- File(File),
233
+ File(ClosableFile),
197
234
  }
198
235
 
199
236
  pub(crate) fn try_get_rbfile(
237
+ _rb: &Ruby,
200
238
  rb_f: Value,
201
239
  write: bool,
202
240
  ) -> RbResult<(EitherRustRubyFile, Option<PathBuf>)> {
203
- let f = RbFileLikeObject::with_requirements(rb_f, !write, write, !write)?;
241
+ let f = RbFileLikeObject::ensure_requirements(rb_f, !write, write, !write)?;
204
242
  Ok((EitherRustRubyFile::Rb(f), None))
205
243
  }
206
244
 
207
245
  pub fn get_ruby_scan_source_input(rb_f: Value, write: bool) -> RbResult<RubyScanSourceInput> {
208
- if let Ok(s) = String::try_convert(rb_f) {
209
- let mut file_path = PlPath::new(&s);
210
- if let Some(p) = file_path.as_ref().as_local_path() {
211
- if p.starts_with("~/") {
246
+ Ruby::attach(|_rb| {
247
+ if let Ok(s) = String::try_convert(rb_f) {
248
+ let mut file_path = PlPath::new(&s);
249
+ if let Some(p) = file_path.as_ref().as_local_path()
250
+ && p.starts_with("~/")
251
+ {
212
252
  file_path = PlPath::Local(resolve_homedir(&p).into());
213
253
  }
254
+ Ok(RubyScanSourceInput::Path(file_path))
255
+ } else {
256
+ let f = RbFileLikeObject::ensure_requirements(rb_f, !write, write, !write)?;
257
+ Ok(RubyScanSourceInput::Buffer(f.to_memslice()))
214
258
  }
215
- Ok(RubyScanSourceInput::Path(file_path))
216
- } else {
217
- let f = RbFileLikeObject::with_requirements(rb_f, !write, write, !write)?;
218
- Ok(RubyScanSourceInput::Buffer(MemSlice::from_bytes(
219
- f.as_bytes(),
220
- )))
221
- }
259
+ })
260
+ }
261
+
262
+ pub fn get_either_buffer_or_path(
263
+ rb_f: Value,
264
+ write: bool,
265
+ ) -> RbResult<(EitherRustRubyFile, Option<PathBuf>)> {
266
+ Ruby::attach(|rb| {
267
+ if let Ok(rstring) = RString::try_convert(rb_f) {
268
+ let s = unsafe { rstring.as_str() }?;
269
+ let file_path = std::path::Path::new(&s);
270
+ let file_path = resolve_homedir(&file_path);
271
+ let f = if write {
272
+ create_file(&file_path).map_err(RbPolarsErr::from)?
273
+ } else {
274
+ polars_utils::open_file(&file_path).map_err(RbPolarsErr::from)?
275
+ };
276
+ Ok((EitherRustRubyFile::Rust(f.into()), Some(file_path)))
277
+ } else {
278
+ try_get_rbfile(rb, rb_f, write)
279
+ }
280
+ })
222
281
  }
223
282
 
224
283
  ///
225
284
  /// # Arguments
226
- /// * `truncate` - open or create a new file.
227
- pub fn get_either_file(rb_f: Value, truncate: bool) -> RbResult<EitherRustRubyFile> {
228
- if let Ok(rstring) = RString::try_convert(rb_f) {
229
- let s = unsafe { rstring.as_str() }?;
230
- let file_path = std::path::Path::new(&s);
231
- let file_path = resolve_homedir(&file_path);
232
- let f = if truncate {
233
- File::create(file_path).map_err(RbPolarsErr::from)?
234
- } else {
235
- polars_utils::open_file(&file_path).map_err(RbPolarsErr::from)?
236
- };
237
- Ok(EitherRustRubyFile::Rust(f.into()))
238
- } else {
239
- let f = RbFileLikeObject::with_requirements(rb_f, !truncate, truncate, !truncate)?;
240
- Ok(EitherRustRubyFile::Rb(f))
241
- }
285
+ /// * `write` - open for writing; will truncate existing file and create new file if not.
286
+ pub fn get_either_file(rb_f: Value, write: bool) -> RbResult<EitherRustRubyFile> {
287
+ Ok(get_either_buffer_or_path(rb_f, write)?.0)
242
288
  }
243
289
 
244
290
  pub fn get_file_like(f: Value, truncate: bool) -> RbResult<Box<dyn FileLike>> {
@@ -265,22 +311,9 @@ pub fn get_mmap_bytes_reader_and_path<'a>(
265
311
  ) -> RbResult<(Box<dyn MmapBytesReader + 'a>, Option<PathBuf>)> {
266
312
  match rb_f {
267
313
  RbReadBytes::Bytes(v) => Ok((Box::new(Cursor::new(unsafe { v.as_slice() })), None)),
268
- RbReadBytes::Other(v) => {
269
- let path = PathBuf::try_convert(*v)?;
270
- let f = File::open(&path).map_err(|e| {
271
- Error::new(
272
- Ruby::get().unwrap().exception_runtime_error(),
273
- e.to_string(),
274
- )
275
- })?;
276
- Ok((Box::new(f), Some(path)))
277
- }
314
+ RbReadBytes::Other(v) => match get_either_buffer_or_path(*v, false)? {
315
+ (EitherRustRubyFile::Rust(f), path) => Ok((Box::new(f), path)),
316
+ (EitherRustRubyFile::Rb(f), path) => Ok((Box::new(Cursor::new(f.to_memslice())), path)),
317
+ },
278
318
  }
279
319
  }
280
-
281
- pub fn try_get_writeable(
282
- rb_f: Value,
283
- _cloud_options: Option<&CloudOptions>,
284
- ) -> RbResult<Box<dyn Write>> {
285
- Ok(get_either_file(rb_f, true)?.into_dyn_writeable())
286
- }
@@ -1,41 +1,42 @@
1
1
  use magnus::RArray;
2
2
  use polars::lazy::dsl;
3
3
 
4
- use crate::rb_exprs_to_exprs;
5
- use crate::{RbExpr, RbPolarsErr, RbResult};
4
+ use crate::error::RbPolarsErr;
5
+ use crate::expr::ToExprs;
6
+ use crate::{RbExpr, RbResult};
6
7
 
7
8
  pub fn all_horizontal(exprs: RArray) -> RbResult<RbExpr> {
8
- let exprs = rb_exprs_to_exprs(exprs)?;
9
+ let exprs = exprs.to_exprs()?;
9
10
  let e = dsl::all_horizontal(exprs).map_err(RbPolarsErr::from)?;
10
11
  Ok(e.into())
11
12
  }
12
13
 
13
14
  pub fn any_horizontal(exprs: RArray) -> RbResult<RbExpr> {
14
- let exprs = rb_exprs_to_exprs(exprs)?;
15
+ let exprs = exprs.to_exprs()?;
15
16
  let e = dsl::any_horizontal(exprs).map_err(RbPolarsErr::from)?;
16
17
  Ok(e.into())
17
18
  }
18
19
 
19
20
  pub fn max_horizontal(exprs: RArray) -> RbResult<RbExpr> {
20
- let exprs = rb_exprs_to_exprs(exprs)?;
21
+ let exprs = exprs.to_exprs()?;
21
22
  let e = dsl::max_horizontal(exprs).map_err(RbPolarsErr::from)?;
22
23
  Ok(e.into())
23
24
  }
24
25
 
25
26
  pub fn min_horizontal(exprs: RArray) -> RbResult<RbExpr> {
26
- let exprs = rb_exprs_to_exprs(exprs)?;
27
+ let exprs = exprs.to_exprs()?;
27
28
  let e = dsl::min_horizontal(exprs).map_err(RbPolarsErr::from)?;
28
29
  Ok(e.into())
29
30
  }
30
31
 
31
32
  pub fn sum_horizontal(exprs: RArray, ignore_nulls: bool) -> RbResult<RbExpr> {
32
- let exprs = rb_exprs_to_exprs(exprs)?;
33
+ let exprs = exprs.to_exprs()?;
33
34
  let e = dsl::sum_horizontal(exprs, ignore_nulls).map_err(RbPolarsErr::from)?;
34
35
  Ok(e.into())
35
36
  }
36
37
 
37
38
  pub fn mean_horizontal(exprs: RArray, ignore_nulls: bool) -> RbResult<RbExpr> {
38
- let exprs = rb_exprs_to_exprs(exprs)?;
39
+ let exprs = exprs.to_exprs()?;
39
40
  let e = dsl::mean_horizontal(exprs, ignore_nulls).map_err(RbPolarsErr::from)?;
40
41
  Ok(e.into())
41
42
  }