mirror of
https://github.com/enso-org/enso.git
synced 2024-11-23 08:08:34 +03:00
Move write_bytes
to be part of Vector
. (#3583)
Updates `write_bytes` API to be part of `Vector` and to conform to `write` APIs. # Important Notes Ensures doesn't touch the file if an invalid byte array.
This commit is contained in:
parent
2c780ef6ba
commit
9578dc1e43
@ -152,6 +152,7 @@
|
||||
- [Added support for custom encodings in `File_Format.Delimited` writing.][3564]
|
||||
- [Allow filtering caught error type in `Error.catch`.][3574]
|
||||
- [Implemented `Append` mode for `File_Format.Delimited`.][3573]
|
||||
- [Added `Vector.write_bytes` function and removed old `File.write_bytes`][3583]
|
||||
|
||||
[debug-shortcuts]:
|
||||
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
|
||||
@ -243,6 +244,7 @@
|
||||
[3564]: https://github.com/enso-org/enso/pull/3564
|
||||
[3574]: https://github.com/enso-org/enso/pull/3574
|
||||
[3573]: https://github.com/enso-org/enso/pull/3573
|
||||
[3583]: https://github.com/enso-org/enso/pull/3583
|
||||
|
||||
#### Enso Compiler
|
||||
|
||||
|
@ -49,6 +49,6 @@ type Body
|
||||
Examples.get_geo_data.to_file Examples.scratch_file
|
||||
to_file : File -> File
|
||||
to_file file =
|
||||
file.write_bytes self.bytes
|
||||
self.bytes.write_bytes file
|
||||
file
|
||||
|
||||
|
@ -12,6 +12,7 @@ from Standard.Base.Data.Time as Time_Module import Time
|
||||
|
||||
export Standard.Base.System.File.Option
|
||||
|
||||
polyglot java import org.enso.base.Array_Utils
|
||||
polyglot java import org.enso.base.Encoding_Utils
|
||||
polyglot java import java.io.InputStream as Java_Input_Stream
|
||||
polyglot java import java.io.OutputStream as Java_Output_Stream
|
||||
@ -289,41 +290,6 @@ type File
|
||||
bytes = self.read_bytes
|
||||
Text.from_bytes bytes encoding on_problems
|
||||
|
||||
## Appends a number of bytes at the end of this file.
|
||||
|
||||
Arguments:
|
||||
- contents: A vector of bytes to append to the file.
|
||||
|
||||
> Example
|
||||
Append the bytes of the text "hello" to a file.
|
||||
|
||||
import Standard.Examples
|
||||
|
||||
example_append_bytes = Examples.scratch_file.append_bytes "hello".utf_8
|
||||
append_bytes : Vector.Vector -> Nothing ! File_Error
|
||||
append_bytes contents =
|
||||
opts = [Option.Append, Option.Create]
|
||||
self.with_output_stream opts (_.write_bytes contents)
|
||||
|
||||
## Writes a number of bytes into this file, replacing any existing contents.
|
||||
|
||||
Arguments:
|
||||
- contents: The vector of bytes to write into the file.
|
||||
|
||||
If the file does not exist, it will be created.
|
||||
|
||||
> Example
|
||||
Write the bytes of the text "hello" to a file.
|
||||
|
||||
import Standard.Examples
|
||||
|
||||
example_write_bytes = Examples.scratch_file.write_bytes "hello".utf_8
|
||||
write_bytes : Vector.Vector -> Nothing ! File_Error
|
||||
write_bytes contents =
|
||||
opts = [Option.Write, Option.Create, Option.Truncate_Existing]
|
||||
self.with_output_stream opts (_.write_bytes contents)
|
||||
Nothing
|
||||
|
||||
## Join two path segments together.
|
||||
|
||||
Arguments:
|
||||
@ -1066,9 +1032,9 @@ get_cwd = @Builtin_Method "File.get_cwd"
|
||||
get_file : Text -> File
|
||||
get_file path = @Builtin_Method "File.get_file"
|
||||
|
||||
## Writes (or appends) the specified bytes to the specified file.
|
||||
The behavior specified in the `existing_file` parameter will be used if the
|
||||
file exists.
|
||||
## Writes (or appends) the text to the specified file using the supplied
|
||||
encoding. The behavior specified in the `existing_file` parameter will be
|
||||
used if the file exists.
|
||||
|
||||
Arguments:
|
||||
- path: The path to the target file.
|
||||
@ -1090,3 +1056,40 @@ Text.write path encoding=Encoding.utf_8 on_existing_file=Existing_File_Behavior.
|
||||
file = new path
|
||||
on_existing_file.write file stream->
|
||||
stream.write_bytes bytes
|
||||
|
||||
## Writes (or appends) the Vector of bytes into the specified file. The behavior
|
||||
specified in the `existing_file` parameter will be used if the file exists.
|
||||
|
||||
Arguments:
|
||||
- path: The path to the target file.
|
||||
- on_existing_file: Specifies how to proceed if the file already exists.
|
||||
|
||||
If the Vector contains any item which is not a `Byte`, an
|
||||
`Illegal_Argument_Error` will be raised. Enso follows the Java convention,
|
||||
that a `Byte` is between -128 and 127.
|
||||
If the path to the parent location cannot be found or the filename is
|
||||
invalid, a `File_Not_Found` is raised.
|
||||
If another error occurs, such as access denied, an `Io_Error` is raised.
|
||||
Otherwise, the file is created with the encoded text written to it.
|
||||
|
||||
> Example
|
||||
Write the UTF-8 bytes of the text "$£§€¢" to a file.
|
||||
|
||||
import Standard.Examples
|
||||
|
||||
[36, -62, -93, -62, -89, -30, -126, -84, -62, -94].write_bytes Examples.scratch_file
|
||||
> Example
|
||||
Append the UTF-8 bytes of the text "$£§€¢" to a file.
|
||||
|
||||
import Standard.Examples
|
||||
|
||||
[36, -62, -93, -62, -89, -30, -126, -84, -62, -94].write_bytes Examples.scratch_file.write_bytes Examples.scratch_file Existing_File_Behavior.Append
|
||||
Vector.Vector.write_bytes : (File|Text) -> Existing_File_Behavior -> Nothing ! Illegal_Argument_Error | File_Not_Found | Io_Error | File_Already_Exists_Error
|
||||
Vector.Vector.write_bytes path on_existing_file=Existing_File_Behavior.Backup =
|
||||
Panic.catch Unsupported_Argument_Types handler=(Error.throw (Illegal_Argument_Error "Only Vectors consisting of bytes (integers in the range from -128 to 127) are supported by the `write_bytes` method.")) <|
|
||||
## Convert to a byte array before writing - and fail early if there is any problem.
|
||||
byte_array = Array_Utils.ensureByteArray self.to_array
|
||||
|
||||
file = new path
|
||||
on_existing_file.write file stream->
|
||||
stream.write_bytes (Vector.Vector byte_array)
|
||||
|
14
std-bits/base/src/main/java/org/enso/base/Array_Utils.java
Normal file
14
std-bits/base/src/main/java/org/enso/base/Array_Utils.java
Normal file
@ -0,0 +1,14 @@
|
||||
package org.enso.base;
|
||||
|
||||
public class Array_Utils {
|
||||
/**
|
||||
* This function forces the polyglot conversion of an Enso array into a `byte[]`. This allows for
|
||||
* asserting that it is a valid `byte[]`.
|
||||
*
|
||||
* @param input the converted array.
|
||||
* @return the `input` unchanged.
|
||||
*/
|
||||
public static byte[] ensureByteArray(byte[] input) {
|
||||
return input;
|
||||
}
|
||||
}
|
@ -148,7 +148,7 @@ spec =
|
||||
Test.specify "should report errors when encountering malformed characters" <|
|
||||
utf8_file = (enso_project.data / "transient" / "utf8_invalid.csv")
|
||||
utf8_bytes = [97, 44, 98, 44, 99, 10, -60, -123, 44, -17, -65, -65, 44, -61, 40, -61, 40, 10]
|
||||
utf8_file.write_bytes utf8_bytes
|
||||
utf8_bytes.write_bytes utf8_file
|
||||
action_1 on_problems =
|
||||
utf8_file.read (Delimited "," headers=True) on_problems
|
||||
tester_1 table =
|
||||
|
@ -120,14 +120,89 @@ spec =
|
||||
contents_2.should .start_with "Cupcake ipsum dolor sit amet."
|
||||
|
||||
Test.group "write operations" <|
|
||||
data = [32, 127, -128, 0]
|
||||
data_2 = [10, 15, 20, 30]
|
||||
|
||||
transient = enso_project.data / "transient"
|
||||
Test.specify "should allow to append to files" <|
|
||||
Test.specify "should allow to writing bytes to a new file" <|
|
||||
f = transient / "new_file.dat"
|
||||
f.delete_if_exists
|
||||
f.exists.should_be_false
|
||||
data.write_bytes f
|
||||
f.exists.should_be_true
|
||||
f.read_bytes.should_equal data
|
||||
f.delete_if_exists
|
||||
|
||||
Test.specify "should backup a file when overwriting with new bytes" <|
|
||||
f = transient / "work.txt"
|
||||
f.delete_if_exists
|
||||
f_bak = transient / "work.txt.bak"
|
||||
f_bak.delete_if_exists
|
||||
data.write_bytes f
|
||||
f.exists.should_be_true
|
||||
data_2.write_bytes f
|
||||
f.read_bytes.should_equal data_2
|
||||
f_bak.exists.should_be_true
|
||||
f.delete_if_exists
|
||||
f_bak.delete_if_exists
|
||||
|
||||
Test.specify "should allow to overwriting a file with new bytes" <|
|
||||
f = transient / "work.txt"
|
||||
f.delete_if_exists
|
||||
f_bak = transient / "work.txt.bak"
|
||||
f_bak.delete_if_exists
|
||||
data.write_bytes f
|
||||
f.exists.should_be_true
|
||||
data_2.write_bytes f on_existing_file=Existing_File_Behavior.Overwrite
|
||||
f.read_bytes.should_equal data_2
|
||||
f_bak.exists.should_be_false
|
||||
f.delete_if_exists
|
||||
|
||||
Test.specify "should allow appending bytes to a new file" <|
|
||||
f = transient / "new_file.dat"
|
||||
f.delete_if_exists
|
||||
f.exists.should_be_false
|
||||
data.write_bytes f
|
||||
data_2.write_bytes f on_existing_file=Existing_File_Behavior.Append
|
||||
f.read_bytes.should_equal (data + data_2)
|
||||
f.delete_if_exists
|
||||
|
||||
Test.specify "should fail will Illegal_Argument_Error when trying to write invalid byte vector" <|
|
||||
f = transient / "work.txt"
|
||||
f.delete_if_exists
|
||||
f.exists.should_be_false
|
||||
"line 1!".write f on_existing_file=Existing_File_Behavior.Append
|
||||
[0, 1, 256].write_bytes f . should_fail_with Illegal_Argument_Error
|
||||
[0, 1, Nothing].write_bytes f . should_fail_with Illegal_Argument_Error
|
||||
|
||||
Test.specify "should not change the file when trying to write an invalid byte vector" <|
|
||||
f = transient / "work.txt"
|
||||
f.delete_if_exists
|
||||
f_bak = transient / "work.txt.bak"
|
||||
f_bak.delete_if_exists
|
||||
data.write_bytes f
|
||||
[0, 1, 256].write_bytes f . should_fail_with Illegal_Argument_Error
|
||||
f.read_bytes.should_equal data
|
||||
f_bak.exists.should_be_false
|
||||
[0, 1, 256].write_bytes f on_existing_file=Existing_File_Behavior.Overwrite . should_fail_with Illegal_Argument_Error
|
||||
f.read_bytes.should_equal data
|
||||
[0, 1, 256].write_bytes f on_existing_file=Existing_File_Behavior.Append . should_fail_with Illegal_Argument_Error
|
||||
f.read_bytes.should_equal data
|
||||
f.delete_if_exists
|
||||
|
||||
Test.specify "should allow to writing text to a new file" <|
|
||||
f = transient / "work.txt"
|
||||
f.delete_if_exists
|
||||
f.exists.should_be_false
|
||||
"line 1!".write f
|
||||
f.exists.should_be_true
|
||||
f.read_text.should_equal "line 1!"
|
||||
f.delete
|
||||
f.exists.should_be_false
|
||||
|
||||
Test.specify "should allow to appending text to a file" <|
|
||||
f = transient / "work.txt"
|
||||
f.delete_if_exists
|
||||
"line 1!".write f on_existing_file=Existing_File_Behavior.Append
|
||||
'\nline 2!'.write f on_existing_file=Existing_File_Behavior.Append
|
||||
f.read_text.should_equal 'line 1!\nline 2!'
|
||||
f.delete
|
||||
@ -140,8 +215,8 @@ spec =
|
||||
"line 1!".write f on_existing_file=Existing_File_Behavior.Overwrite . should_equal Nothing
|
||||
f.exists.should_be_true
|
||||
f.read_text.should_equal "line 1!"
|
||||
'line 2!'.write f on_existing_file=Existing_File_Behavior.Overwrite . should_equal Nothing
|
||||
f.read_text.should_equal 'line 2!'
|
||||
"line 2!".write f on_existing_file=Existing_File_Behavior.Overwrite . should_equal Nothing
|
||||
f.read_text.should_equal "line 2!"
|
||||
f.delete
|
||||
f.exists.should_be_false
|
||||
|
||||
@ -153,7 +228,7 @@ spec =
|
||||
f.exists.should_be_true
|
||||
f.read_text.should_equal "line 1!"
|
||||
"line 2!".write f on_existing_file=Existing_File_Behavior.Error . should_fail_with File_Already_Exists_Error
|
||||
f.read_text.should_equal 'line 1!'
|
||||
f.read_text.should_equal "line 1!"
|
||||
f.delete
|
||||
f.exists.should_be_false
|
||||
|
||||
@ -180,8 +255,8 @@ spec =
|
||||
n3.delete_if_exists
|
||||
|
||||
"line 2!".write f . should_equal Nothing
|
||||
f.read_text.should_equal 'line 2!'
|
||||
bak.read_text.should_equal 'line 1!'
|
||||
f.read_text.should_equal "line 2!"
|
||||
bak.read_text.should_equal "line 1!"
|
||||
if n3.exists then
|
||||
Test.fail "The temporary file should have been cleaned up."
|
||||
written_news.each n->
|
||||
|
Loading…
Reference in New Issue
Block a user