From 9ad8e3cc8eaec92169d334039fe0fd00807a72af Mon Sep 17 00:00:00 2001 From: Tony Lian <1040424979@qq.com> Date: Sun, 10 Mar 2019 14:27:30 -0700 Subject: [PATCH 01/22] Add writing support --- src/C_interface.jl | 28 +++++++++++++++++++++++ src/ReadStat.jl | 55 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/C_interface.jl b/src/C_interface.jl index aee677e..926ffd8 100644 --- a/src/C_interface.jl +++ b/src/C_interface.jl @@ -69,4 +69,32 @@ end function readstat_parse(filename::String, type::Val{:sas7bdat}, parser::Ptr{Nothing}, ds::ReadStatDataFrame) return ccall((:readstat_parse_sas7bdat, libreadstat), Int, (Ptr{Nothing}, Cstring, Any), parser, string(filename), ds) +end + +function readstat_begin_row(writer) + return ccall((:readstat_begin_row), Int, (Ptr{Nothing},), writer) +end + +function readstat_end_row(writer) + return ccall((:readstat_end_row), Int, (Ptr{Nothing},), writer) +end + +function readstat_begin_writing(writer, filetype::Val{:dta}, io, row_count) + return ccall((:readstat_begin_writing_dta, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) +end + +function readstat_begin_writing(writer, filetype::Val{:sav}, io, row_count) + return ccall((:readstat_begin_writing_sav, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) +end + +function readstat_begin_writing(writer, filetype::Val{:por}, io, row_count) + return ccall((:readstat_begin_writing_por, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) +end + +function readstat_begin_writing(writer, filetype::Val{:sas7bdat}, io, row_count) + return ccall((:readstat_begin_writing_sas7bdat, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) +end + +function readstat_insert_double_value(writer, variable, item) + return ccall((:readstat_insert_double_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Any), writer, variable, item) end \ No newline at end of file diff --git a/src/ReadStat.jl b/src/ReadStat.jl index a56931f..250997f 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -248,7 +248,7 @@ function Parser() ccall((:readstat_set_value_handler, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, val_fxn) ccall((:readstat_set_value_label_handler, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, label_fxn) return parser -end +end function parse_data_file!(ds::ReadStatDataFrame, parser::Ptr{Nothing}, filename::AbstractString, filetype::Val) retval = readstat_parse(filename, filetype, parser, ds) @@ -256,9 +256,62 @@ function parse_data_file!(ds::ReadStatDataFrame, parser::Ptr{Nothing}, filename: retval == 0 || error("Error parsing $filename: $retval") end + +function handle_write!() + +end + +function Writer(source; file_label="File Label") + writer = ccall((:readstat_writer_init, libreadstat), Ptr{Nothing}, ()) + write_bytes = @cfunction(handle_write!, Cint, (Cint, Cint, Ptr{ReadStatDataFrame})) + ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, ), parser, write_bytes) + ccall((:readstat_writer_set_file_label, libreadstat, Cvoid, (Ptr{Nothing}, Cstring), writer, file_label) + return writer +end + +function write_data_file(filename::AbstractString, filetype::Val, io::IO, source) + writer = Writer(source) + fields = fieldnames(eltype(source)) + variables_array = [] + + for field in fields: + variable = ccall((:readstat_add_variable, libreadstat), Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), writer, String(field), READSTAT_TYPE_DOUBLE, 0); # TODO: know width + readstat_variable_set_label(variable, String(field)) + variables_array.push!(variable) + + variables = NamedTuple{(fields...,)}((variables...,)) # generate a NamedTuple for variables + + + if Base.IteratorSize(source) == Base.HasLength(): # TODO: what about HasShape + row_count = length(q) + else: #fallback + row_count = 0 + for _ in source + row_count += 1 + end + + readstat_begin_writing(writer, filetype, io, row_count) + + for row in source + readstat_begin_row(writer) + for field in fields + readstat_insert_double_value(writer, variables[field], row[field]) # TODO: more than double + end + readstat_end_row(writer); + end + + ccall((:readstat_end_writing, libreadstat), Int, (Ptr{Nothing}), writer) + ccall((:readstat_writer_free, libreadstat), Cvoid, (Ptr{Nothing}), writer) +end + read_dta(filename::AbstractString) = read_data_file(filename, Val(:dta)) read_sav(filename::AbstractString) = read_data_file(filename, Val(:sav)) read_por(filename::AbstractString) = read_data_file(filename, Val(:por)) read_sas7bdat(filename::AbstractString) = read_data_file(filename, Val(:sas7bdat)) +write_dta(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:dta), io, source) +write_sav(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:sav), io, source) +write_por(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:por), io, source) +write_sas7bdat(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:sas7bdat), io, source) + end #module ReadStat From 1bbaef1f2ab8d7608a461dfa53714087ff542736 Mon Sep 17 00:00:00 2001 From: Tony Lian <1040424979@qq.com> Date: Tue, 19 Mar 2019 23:00:30 -0700 Subject: [PATCH 02/22] Add libreadstat into readstat_begin_row and readstat_end_row, pass pointer into readstat_begin_writing --- src/C_interface.jl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/C_interface.jl b/src/C_interface.jl index 926ffd8..c9bc705 100644 --- a/src/C_interface.jl +++ b/src/C_interface.jl @@ -72,27 +72,27 @@ function readstat_parse(filename::String, type::Val{:sas7bdat}, parser::Ptr{Noth end function readstat_begin_row(writer) - return ccall((:readstat_begin_row), Int, (Ptr{Nothing},), writer) + return ccall((:readstat_begin_row, libreadstat), Int, (Ptr{Nothing},), writer) end function readstat_end_row(writer) - return ccall((:readstat_end_row), Int, (Ptr{Nothing},), writer) + return ccall((:readstat_end_row, libreadstat), Int, (Ptr{Nothing},), writer) end function readstat_begin_writing(writer, filetype::Val{:dta}, io, row_count) - return ccall((:readstat_begin_writing_dta, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) + return ccall((:readstat_begin_writing_dta, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) end function readstat_begin_writing(writer, filetype::Val{:sav}, io, row_count) - return ccall((:readstat_begin_writing_sav, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) + return ccall((:readstat_begin_writing_sav, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) end function readstat_begin_writing(writer, filetype::Val{:por}, io, row_count) - return ccall((:readstat_begin_writing_por, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) + return ccall((:readstat_begin_writing_por, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) end function readstat_begin_writing(writer, filetype::Val{:sas7bdat}, io, row_count) - return ccall((:readstat_begin_writing_sas7bdat, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, io, Cint(row_count)) + return ccall((:readstat_begin_writing_sas7bdat, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) end function readstat_insert_double_value(writer, variable, item) From 150e1cde46e1a57b5cd0716d34e638f09b13d542 Mon Sep 17 00:00:00 2001 From: Tony Lian <1040424979@qq.com> Date: Tue, 19 Mar 2019 23:00:51 -0700 Subject: [PATCH 03/22] Add handle_write and fix several syntax errors --- src/ReadStat.jl | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 250997f..38aa5b7 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -256,16 +256,17 @@ function parse_data_file!(ds::ReadStatDataFrame, parser::Ptr{Nothing}, filename: retval == 0 || error("Error parsing $filename: $retval") end - -function handle_write!() - -end +function handle_write!(data::Ptr, len::Int, ctx::Ptr) + io = unsafe_pointer_to_objref(ctx) # restore io + actual_data = unsafe_wrap(Array{Any}, data, len) # we may want to specify the type later + write(io, actual_data) + end function Writer(source; file_label="File Label") writer = ccall((:readstat_writer_init, libreadstat), Ptr{Nothing}, ()) write_bytes = @cfunction(handle_write!, Cint, (Cint, Cint, Ptr{ReadStatDataFrame})) - ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, ), parser, write_bytes) - ccall((:readstat_writer_set_file_label, libreadstat, Cvoid, (Ptr{Nothing}, Cstring), writer, file_label) + ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, write_bytes) + ccall((:readstat_writer_set_file_label, libreadstat), Cvoid, (Ptr{Nothing}, Cstring), writer, file_label) return writer end @@ -274,21 +275,25 @@ function write_data_file(filename::AbstractString, filetype::Val, io::IO, source fields = fieldnames(eltype(source)) variables_array = [] - for field in fields: - variable = ccall((:readstat_add_variable, libreadstat), Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), writer, String(field), READSTAT_TYPE_DOUBLE, 0); # TODO: know width + for field in fields + variable = ccall((:readstat_add_variable, libreadstat), + Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), + writer, String(field), READSTAT_TYPE_DOUBLE, Cint(0)) # TODO: know width readstat_variable_set_label(variable, String(field)) variables_array.push!(variable) + end variables = NamedTuple{(fields...,)}((variables...,)) # generate a NamedTuple for variables - if Base.IteratorSize(source) == Base.HasLength(): # TODO: what about HasShape + if Base.IteratorSize(source) == Base.HasLength() # TODO: what about HasShape row_count = length(q) - else: #fallback + else #fallback row_count = 0 for _ in source row_count += 1 end + end readstat_begin_writing(writer, filetype, io, row_count) @@ -300,8 +305,8 @@ function write_data_file(filename::AbstractString, filetype::Val, io::IO, source readstat_end_row(writer); end - ccall((:readstat_end_writing, libreadstat), Int, (Ptr{Nothing}), writer) - ccall((:readstat_writer_free, libreadstat), Cvoid, (Ptr{Nothing}), writer) + ccall((:readstat_end_writing, libreadstat), Int, (Ptr{Nothing},), writer) + ccall((:readstat_writer_free, libreadstat), Cvoid, (Ptr{Nothing},), writer) end read_dta(filename::AbstractString) = read_data_file(filename, Val(:dta)) From fcfc6fe418cf3e0a9fc38214aee9d420191334c4 Mon Sep 17 00:00:00 2001 From: Tony Lian <1040424979@qq.com> Date: Sat, 23 Mar 2019 20:49:43 -0700 Subject: [PATCH 04/22] Update writing --- src/ReadStat.jl | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 38aa5b7..963e880 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -256,21 +256,29 @@ function parse_data_file!(ds::ReadStatDataFrame, parser::Ptr{Nothing}, filename: retval == 0 || error("Error parsing $filename: $retval") end -function handle_write!(data::Ptr, len::Int, ctx::Ptr) +function handle_write!(data::Ptr{UInt8}, len::Cint, ctx::Ptr) io = unsafe_pointer_to_objref(ctx) # restore io - actual_data = unsafe_wrap(Array{Any}, data, len) # we may want to specify the type later + actual_data = unsafe_wrap(Array{UInt8}, data, (len, )) # we may want to specify the type later write(io, actual_data) + return len end function Writer(source; file_label="File Label") writer = ccall((:readstat_writer_init, libreadstat), Ptr{Nothing}, ()) - write_bytes = @cfunction(handle_write!, Cint, (Cint, Cint, Ptr{ReadStatDataFrame})) - ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, write_bytes) + write_bytes = @cfunction(handle_write!, Cint, (Ptr{UInt8}, Cint, Ptr{Nothing})) + ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), writer, write_bytes) ccall((:readstat_writer_set_file_label, libreadstat), Cvoid, (Ptr{Nothing}, Cstring), writer, file_label) return writer end -function write_data_file(filename::AbstractString, filetype::Val, io::IO, source) +function write_data_file(filename::AbstractString, filetype::Val, source) + io = open(filename, "w") + write_data_file(filetype::Val, io, source) + close(io) +end + + +function write_data_file(filetype::Val, io::IO, source) writer = Writer(source) fields = fieldnames(eltype(source)) variables_array = [] @@ -279,15 +287,14 @@ function write_data_file(filename::AbstractString, filetype::Val, io::IO, source variable = ccall((:readstat_add_variable, libreadstat), Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), writer, String(field), READSTAT_TYPE_DOUBLE, Cint(0)) # TODO: know width - readstat_variable_set_label(variable, String(field)) - variables_array.push!(variable) + # readstat_variable_set_label(variable, String(field)) TODO: label for a variable + push!(variables_array, variable) end - variables = NamedTuple{(fields...,)}((variables...,)) # generate a NamedTuple for variables + variables = NamedTuple{(fields...,)}((variables_array...,)) # generate a NamedTuple for variables - if Base.IteratorSize(source) == Base.HasLength() # TODO: what about HasShape - row_count = length(q) + row_count = length(source) else #fallback row_count = 0 for _ in source From 9f5c76e65c26761cc6a352ad825552e1832787a8 Mon Sep 17 00:00:00 2001 From: Tony Lian <1040424979@qq.com> Date: Sun, 24 Mar 2019 16:06:39 -0700 Subject: [PATCH 05/22] Change functions for writing and export write functions --- src/ReadStat.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 963e880..ff4d540 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -18,7 +18,7 @@ include(depsjl_path) using DataValues: DataValueVector using Dates -export ReadStatDataFrame, read_dta, read_sav, read_por, read_sas7bdat +export ReadStatDataFrame, read_dta, read_sav, read_por, read_sas7bdat, write_dta, write_sav, write_por, write_sas7bdat ############################################################################## ## @@ -321,9 +321,9 @@ read_sav(filename::AbstractString) = read_data_file(filename, Val(:sav)) read_por(filename::AbstractString) = read_data_file(filename, Val(:por)) read_sas7bdat(filename::AbstractString) = read_data_file(filename, Val(:sas7bdat)) -write_dta(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:dta), io, source) -write_sav(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:sav), io, source) -write_por(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:por), io, source) -write_sas7bdat(filename::AbstractString, io::IO, source) = write_data_file(filename, Val(:sas7bdat), io, source) +write_dta(filename::AbstractString, source) = write_data_file(filename, Val(:dta), source) +write_sav(filename::AbstractString, source) = write_data_file(filename, Val(:sav), source) +write_por(filename::AbstractString, source) = write_data_file(filename, Val(:por), source) +write_sas7bdat(filename::AbstractString, source) = write_data_file(filename, Val(:sas7bdat), source) end #module ReadStat From dd9da3f08262ed2f314721d1c1f0239d03e602eb Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 09:30:40 +0200 Subject: [PATCH 06/22] remove merge artifact --- src/ReadStat.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 6f559a6..6337ee0 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -276,13 +276,10 @@ function Parser() ccall((:readstat_set_value_label_handler, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, label_fxn) return parser end -<<<<<<< HEAD -======= function error_message(retval::Integer) unsafe_string(ccall((:readstat_error_message, libreadstat), Ptr{Cchar}, (Cint,), retval)) end ->>>>>>> master function error_message(retval::Integer) unsafe_string(ccall((:readstat_error_message, libreadstat), Ptr{Cchar}, (Cint,), retval)) From e952bdcfabdb77e5038acde1d331e6c9f36597f9 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 09:42:25 +0200 Subject: [PATCH 07/22] add write_xport --- src/ReadStat.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 6337ee0..158f5bd 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -361,5 +361,6 @@ write_dta(filename::AbstractString, source) = write_data_file(filename, Val(:dta write_sav(filename::AbstractString, source) = write_data_file(filename, Val(:sav), source) write_por(filename::AbstractString, source) = write_data_file(filename, Val(:por), source) write_sas7bdat(filename::AbstractString, source) = write_data_file(filename, Val(:sas7bdat), source) +write_xport(filename::AbstractString, source) = write_data_file(filename, Val(:xport), source) end #module ReadStat From adca45e7decadf010bf64c7f04ef965fe9604ef0 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 10:52:55 +0200 Subject: [PATCH 08/22] ignore write_tests --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 369ae03..9df679a 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ deps/build.log *.jl.mem Manifest.toml .vscode +test/write_tests/ \ No newline at end of file From bc9fd1f4b4d4076d8d9718c7785884ecbcfd3631 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 10:53:09 +0200 Subject: [PATCH 09/22] add Tables as dependency --- Project.toml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Project.toml b/Project.toml index 761d41d..6ac772b 100644 --- a/Project.toml +++ b/Project.toml @@ -3,17 +3,18 @@ uuid = "d71aba96-b539-5138-91ee-935c3ee1374c" version = "1.1.2-DEV" [deps] -Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" DataValues = "e7dc6d0d-1eca-5fa6-8ad6-5aecde8b7ea5" +Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" ReadStat_jll = "a4dc8951-f1cc-5499-9034-9ec1c3e64557" - -[extras] -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" +Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" [compat] -julia = "1.3" DataValues = "0.4.13" ReadStat_jll = "1.1.1" +julia = "1.3" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] test = ["Test"] From b952f76792d666e9d80642d5a041d5afdc427cf1 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 10:53:19 +0200 Subject: [PATCH 10/22] add xport write function --- src/C_interface.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/C_interface.jl b/src/C_interface.jl index 084a1a2..4b53d5a 100644 --- a/src/C_interface.jl +++ b/src/C_interface.jl @@ -104,6 +104,10 @@ function readstat_begin_writing(writer, filetype::Val{:sas7bdat}, io, row_count) return ccall((:readstat_begin_writing_sas7bdat, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) end +function readstat_begin_writing(writer, filetype::Val{:xport}, io, row_count) + return ccall((:readstat_begin_writing_xport, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) +end + function readstat_insert_double_value(writer, variable, item) return ccall((:readstat_insert_double_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Any), writer, variable, item) end \ No newline at end of file From 9913a386429507296876235b5c03bf2572fc8614 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 10:55:00 +0200 Subject: [PATCH 11/22] make writing doubles work again --- src/ReadStat.jl | 35 ++++++++++++----------------- test/runtests.jl | 57 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 37 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 158f5bd..fa38531 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -11,8 +11,9 @@ using ReadStat_jll using DataValues: DataValueVector import DataValues using Dates +import Tables -export ReadStatDataFrame, read_dta, read_sav, read_por, read_sas7bdat, read_xport, write_dta, write_sav, write_por, write_sas7bdat +export ReadStatDataFrame, read_dta, read_sav, read_por, read_sas7bdat, read_xport, write_dta, write_sav, write_por, write_sas7bdat, write_xport ############################################################################## ## @@ -315,34 +316,24 @@ end function write_data_file(filetype::Val, io::IO, source) writer = Writer(source) - fields = fieldnames(eltype(source)) + + rows = Tables.rows(source) + schema = Tables.schema(rows) variables_array = [] - for field in fields - variable = ccall((:readstat_add_variable, libreadstat), + variables_array = map(Tables.columnnames(source)) do column_name + return ccall((:readstat_add_variable, libreadstat), Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), - writer, String(field), READSTAT_TYPE_DOUBLE, Cint(0)) # TODO: know width + writer, String(column_name), READSTAT_TYPE_DOUBLE, Cint(0)) # TODO: know width # readstat_variable_set_label(variable, String(field)) TODO: label for a variable - push!(variables_array, variable) end - variables = NamedTuple{(fields...,)}((variables_array...,)) # generate a NamedTuple for variables - - if Base.IteratorSize(source) == Base.HasLength() # TODO: what about HasShape - row_count = length(source) - else #fallback - row_count = 0 - for _ in source - row_count += 1 - end - end + readstat_begin_writing(writer, filetype, io, length(rows)) - readstat_begin_writing(writer, filetype, io, row_count) - - for row in source + for row in rows readstat_begin_row(writer) - for field in fields - readstat_insert_double_value(writer, variables[field], row[field]) # TODO: more than double + Tables.eachcolumn(schema, row) do val, i, name + insert_value!(writer, variables_array[i], val) end readstat_end_row(writer); end @@ -351,6 +342,8 @@ function write_data_file(filetype::Val, io::IO, source) ccall((:readstat_writer_free, libreadstat), Cvoid, (Ptr{Nothing},), writer) end +insert_value!(writer, variable, value::Float64) = readstat_insert_double_value(writer, variable, value) + read_dta(filename::AbstractString) = read_data_file(filename, Val(:dta)) read_sav(filename::AbstractString) = read_data_file(filename, Val(:sav)) read_por(filename::AbstractString) = read_data_file(filename, Val(:por)) diff --git a/test/runtests.jl b/test/runtests.jl index dbc10b0..d37f754 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,22 +2,47 @@ using ReadStat using DataValues using Test -@testset "ReadStat: $ext files" for (reader, ext) in - ((read_dta, "dta"), - (read_sav, "sav"), - (read_sas7bdat, "sas7bdat"), - (read_xport, "xpt")) +testdir = joinpath(@__DIR__, "write_tests") +if isdir(testdir) + rm(testdir, recursive = true) +end +mkdir(testdir) + +@testset "ReadStat: $ext files" for (reader, writer, ext) in + ((read_dta, write_dta, "dta"), + (read_sav, write_sav, "sav"), + (read_sas7bdat, write_sas7bdat, "sas7bdat"), + (read_xport, write_xport, "xpt")) + + @testset "Reading" begin + dtafile = joinpath(@__DIR__, "types.$ext") + rsdf = reader(dtafile) + data = rsdf.data - dtafile = joinpath(dirname(@__FILE__), "types.$ext") - rsdf = reader(dtafile) - data = rsdf.data + @test length(data) == 6 + @test rsdf.headers == [:vfloat, :vdouble, :vlong, :vint, :vbyte, :vstring] + @test data[1] == DataValueArray{Float32}([3.14, 7., NA]) + @test data[2] == DataValueArray{Float64}([3.14, 7., NA]) + @test data[3] == DataValueArray{Int32}([2, 7, NA]) + @test data[4] == DataValueArray{Int16}([2, 7, NA]) + @test data[5] == DataValueArray{Int8}([2, 7., NA]) + @test data[6] == DataValueArray{String}(["2", "7", ""]) + end - @test length(data) == 6 - @test rsdf.headers == [:vfloat, :vdouble, :vlong, :vint, :vbyte, :vstring] - @test data[1] == DataValueArray{Float32}([3.14, 7., NA]) - @test data[2] == DataValueArray{Float64}([3.14, 7., NA]) - @test data[3] == DataValueArray{Int32}([2, 7, NA]) - @test data[4] == DataValueArray{Int16}([2, 7, NA]) - @test data[5] == DataValueArray{Int8}([2, 7., NA]) - @test data[6] == DataValueArray{String}(["2", "7", ""]) + @testset "Writing" begin + data = ( + vdouble = [3.14, 7.], + # vfloat = [3.14f0, 7.f0, missing], + # vlong = [2, 7, missing], + # vint = [Int16(2), Int16(7), missing], + # vbyte = [Int8(2), Int8(7), missing], + # vstring = ["2", "7", ""], + ) + filepath = joinpath(testdir, "testwrite.$ext") + writer(filepath, data) + rsdf = reader(filepath) + data_read = rsdf.data + @test length(data_read) == length(data) + @test rsdf.headers == [:vdouble] + end end From d40c96bceb71c54a145facb8a86b1b38164b289a Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 11:00:49 +0200 Subject: [PATCH 12/22] refactor double type --- src/C_interface.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/C_interface.jl b/src/C_interface.jl index 4b53d5a..7896f8b 100644 --- a/src/C_interface.jl +++ b/src/C_interface.jl @@ -108,6 +108,6 @@ function readstat_begin_writing(writer, filetype::Val{:xport}, io, row_count) return ccall((:readstat_begin_writing_xport, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, pointer_from_objref(io), Cint(row_count)) end -function readstat_insert_double_value(writer, variable, item) - return ccall((:readstat_insert_double_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Any), writer, variable, item) +function readstat_insert_double_value(writer, variable, value) + return ccall((:readstat_insert_double_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cdouble), writer, variable, value) end \ No newline at end of file From b77a19d095259539a80eccb1ef61dd38074c4b90 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 11:05:08 +0200 Subject: [PATCH 13/22] add more insertion functions --- src/C_interface.jl | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/C_interface.jl b/src/C_interface.jl index 7896f8b..f7fc357 100644 --- a/src/C_interface.jl +++ b/src/C_interface.jl @@ -110,4 +110,28 @@ end function readstat_insert_double_value(writer, variable, value) return ccall((:readstat_insert_double_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cdouble), writer, variable, value) -end \ No newline at end of file +end + +function readstat_insert_float_value(writer, variable, value) + return ccall((:readstat_insert_float_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cfloat), writer, variable, value) +end + +function readstat_insert_int32_value(writer, variable, value) + return ccall((:readstat_insert_int32_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cint), writer, variable, value) +end + +function readstat_insert_int16_value(writer, variable, value) + return ccall((:readstat_insert_int16_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cshort), writer, variable, value) +end + +function readstat_insert_int8_value(writer, variable, value) + return ccall((:readstat_insert_int8_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cchar), writer, variable, value) +end + +function readstat_insert_string_value(writer, variable, value) + return ccall((:readstat_insert_string_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Ptr{Cstring}), writer, variable, value) +end + +function readstat_insert_missing_value(writer, variable) + return ccall((:readstat_insert_missing_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), writer, variable) +end From d122d22f1555ab9ddb8846728b74dcc333cc75aa Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 12:47:16 +0200 Subject: [PATCH 14/22] add variable insertion and string length logic --- src/C_interface.jl | 6 +++++- src/ReadStat.jl | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/C_interface.jl b/src/C_interface.jl index f7fc357..c486ff9 100644 --- a/src/C_interface.jl +++ b/src/C_interface.jl @@ -129,9 +129,13 @@ function readstat_insert_int8_value(writer, variable, value) end function readstat_insert_string_value(writer, variable, value) - return ccall((:readstat_insert_string_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Ptr{Cstring}), writer, variable, value) + return ccall((:readstat_insert_string_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}, Cstring), writer, variable, value) end function readstat_insert_missing_value(writer, variable) return ccall((:readstat_insert_missing_value, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), writer, variable) end + +function readstat_add_variable(writer, name, type, width) + return ccall((:readstat_add_variable, libreadstat), Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), writer, name, type, width) +end \ No newline at end of file diff --git a/src/ReadStat.jl b/src/ReadStat.jl index fa38531..afaa925 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -321,12 +321,13 @@ function write_data_file(filetype::Val, io::IO, source) schema = Tables.schema(rows) variables_array = [] - variables_array = map(Tables.columnnames(source)) do column_name - return ccall((:readstat_add_variable, libreadstat), - Ptr{Nothing}, (Ptr{Nothing}, Cstring, Cint, Cint), - writer, String(column_name), READSTAT_TYPE_DOUBLE, Cint(0)) # TODO: know width + variables_array = map(schema.names, schema.types) do column_name, column_type + readstat_type, storage_width = readstat_column_type_and_width(source, column_name, nonmissingtype(column_type)) + return add_variable!(writer, column_name, readstat_type, storage_width) # readstat_variable_set_label(variable, String(field)) TODO: label for a variable end + + readstat_begin_writing(writer, filetype, io, length(rows)) @@ -342,7 +343,33 @@ function write_data_file(filetype::Val, io::IO, source) ccall((:readstat_writer_free, libreadstat), Cvoid, (Ptr{Nothing},), writer) end +readstat_column_type_and_width(_, _, other_type) = error("Cannot handle column with element type $other_type. Is this type supported by ReadStat?") +readstat_column_type_and_width(_, _, ::Type{Float64}) = READSTAT_TYPE_DOUBLE, 0 +readstat_column_type_and_width(_, _, ::Type{Float32}) = READSTAT_TYPE_FLOAT, 0 +readstat_column_type_and_width(_, _, ::Type{Int32}) = READSTAT_TYPE_INT32, 0 +readstat_column_type_and_width(_, _, ::Type{Int16}) = READSTAT_TYPE_INT16, 0 +readstat_column_type_and_width(_, _, ::Type{Int8}) = READSTAT_TYPE_CHAR, 0 +function readstat_column_type_and_width(source, colname, ::Type{String}) + col = Tables.getcolumn(source, colname) + maxlen = maximum(col) do str + str === missing ? 0 : ncodeunits(str) + end + if maxlen >= 2045 # maximum length of normal strings + return READSTAT_TYPE_LONG_STRING, 0 + else + return READSTAT_TYPE_STRING, maxlen + end +end + +add_variable!(writer, name, type, width = 0) = readstat_add_variable(writer, name, type, width) + insert_value!(writer, variable, value::Float64) = readstat_insert_double_value(writer, variable, value) +insert_value!(writer, variable, value::Float32) = readstat_insert_float_value(writer, variable, value) +insert_value!(writer, variable, ::Missing) = readstat_insert_missing_value(writer, variable) +insert_value!(writer, variable, value::Int8) = readstat_insert_int8_value(writer, variable, value) +insert_value!(writer, variable, value::Int16) = readstat_insert_int16_value(writer, variable, value) +insert_value!(writer, variable, value::Int32) = readstat_insert_int32_value(writer, variable, value) +insert_value!(writer, variable, value::AbstractString) = readstat_insert_string_value(writer, variable, value) read_dta(filename::AbstractString) = read_data_file(filename, Val(:dta)) read_sav(filename::AbstractString) = read_data_file(filename, Val(:sav)) From 39c475199f3206257bc599f29a23e7a7b2c5bd02 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 12:47:25 +0200 Subject: [PATCH 15/22] more tests --- test/runtests.jl | 78 ++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index d37f754..75ae76e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -8,41 +8,53 @@ if isdir(testdir) end mkdir(testdir) -@testset "ReadStat: $ext files" for (reader, writer, ext) in - ((read_dta, write_dta, "dta"), - (read_sav, write_sav, "sav"), - (read_sas7bdat, write_sas7bdat, "sas7bdat"), - (read_xport, write_xport, "xpt")) +@testset "ReadStat" begin + @testset "$ext files" for (reader, writer, ext) in + ((read_dta, write_dta, "dta"), + (read_sav, write_sav, "sav"), + (read_sas7bdat, write_sas7bdat, "sas7bdat"), + (read_xport, write_xport, "xpt")) - @testset "Reading" begin - dtafile = joinpath(@__DIR__, "types.$ext") - rsdf = reader(dtafile) - data = rsdf.data + @testset "Reading" begin + dtafile = joinpath(@__DIR__, "types.$ext") + rsdf = reader(dtafile) + data = rsdf.data - @test length(data) == 6 - @test rsdf.headers == [:vfloat, :vdouble, :vlong, :vint, :vbyte, :vstring] - @test data[1] == DataValueArray{Float32}([3.14, 7., NA]) - @test data[2] == DataValueArray{Float64}([3.14, 7., NA]) - @test data[3] == DataValueArray{Int32}([2, 7, NA]) - @test data[4] == DataValueArray{Int16}([2, 7, NA]) - @test data[5] == DataValueArray{Int8}([2, 7., NA]) - @test data[6] == DataValueArray{String}(["2", "7", ""]) - end + @test length(data) == 6 + @test rsdf.headers == [:vfloat, :vdouble, :vlong, :vint, :vbyte, :vstring] + @test data[1] == DataValueArray{Float32}([3.14, 7., NA]) + @test data[2] == DataValueArray{Float64}([3.14, 7., NA]) + @test data[3] == DataValueArray{Int32}([2, 7, NA]) + @test data[4] == DataValueArray{Int16}([2, 7, NA]) + @test data[5] == DataValueArray{Int8}([2, 7., NA]) + @test data[6] == DataValueArray{String}(["2", "7", ""]) + + @test rsdf.types == [Float32, Float64, Int32, Int16, Int8, String] + end + + @testset "Writing" begin + data = ( + vdouble = [3.14, 7., missing], + vfloat = [3.14f0, 7.f0, missing], + vint32 = [Int32(2), Int32(7), missing], + vint16 = [Int16(2), Int16(7), missing], + vint8 = [Int8(2), Int8(7), missing], + vstring = ["2", "7", ""], + ) + filepath = joinpath(testdir, "testwrite.$ext") + writer(filepath, data) + rsdf = reader(filepath) + data_read = rsdf.data + @test length(data_read) == length(data) + @test rsdf.headers == collect(keys(data)) + @test rsdf.types == [Float64, Float32, Int32, Int16, Int8, String] - @testset "Writing" begin - data = ( - vdouble = [3.14, 7.], - # vfloat = [3.14f0, 7.f0, missing], - # vlong = [2, 7, missing], - # vint = [Int16(2), Int16(7), missing], - # vbyte = [Int8(2), Int8(7), missing], - # vstring = ["2", "7", ""], - ) - filepath = joinpath(testdir, "testwrite.$ext") - writer(filepath, data) - rsdf = reader(filepath) - data_read = rsdf.data - @test length(data_read) == length(data) - @test rsdf.headers == [:vdouble] + same_value(a::DataValue, b) = a.hasvalue ? get(a) === b : b === missing + same_value(a::DataValue{String}, b::String) = a.hasvalue && get(a) == b + + @test all(zip(data_read, values(data))) do (col_read, col) + all(Base.splat(same_value), zip(col_read, col)) + end + end end end From dc8998b790428567410abbd6985ef730637c7b40 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 12:51:53 +0200 Subject: [PATCH 16/22] remove duplicated line --- src/ReadStat.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index afaa925..f4b50e8 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -282,10 +282,6 @@ function error_message(retval::Integer) unsafe_string(ccall((:readstat_error_message, libreadstat), Ptr{Cchar}, (Cint,), retval)) end -function error_message(retval::Integer) - unsafe_string(ccall((:readstat_error_message, libreadstat), Ptr{Cchar}, (Cint,), retval)) -end - function parse_data_file!(ds::ReadStatDataFrame, parser::Ptr{Nothing}, filename::AbstractString, filetype::Val) retval = readstat_parse(filename, filetype, parser, ds) readstat_parser_free(parser) From 85e781c1728e9f8aadfee1813bf573b2a13d6ce1 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 13:16:54 +0200 Subject: [PATCH 17/22] adjust tests to reflect type limitations in output formats --- test/runtests.jl | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/test/runtests.jl b/test/runtests.jl index 75ae76e..52fe636 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -28,8 +28,6 @@ mkdir(testdir) @test data[4] == DataValueArray{Int16}([2, 7, NA]) @test data[5] == DataValueArray{Int8}([2, 7., NA]) @test data[6] == DataValueArray{String}(["2", "7", ""]) - - @test rsdf.types == [Float32, Float64, Int32, Int16, Int8, String] end @testset "Writing" begin @@ -39,7 +37,7 @@ mkdir(testdir) vint32 = [Int32(2), Int32(7), missing], vint16 = [Int16(2), Int16(7), missing], vint8 = [Int8(2), Int8(7), missing], - vstring = ["2", "7", ""], + vstring = ["2", "7", missing], ) filepath = joinpath(testdir, "testwrite.$ext") writer(filepath, data) @@ -47,10 +45,11 @@ mkdir(testdir) data_read = rsdf.data @test length(data_read) == length(data) @test rsdf.headers == collect(keys(data)) - @test rsdf.types == [Float64, Float32, Int32, Int16, Int8, String] - same_value(a::DataValue, b) = a.hasvalue ? get(a) === b : b === missing - same_value(a::DataValue{String}, b::String) = a.hasvalue && get(a) == b + same_value(a::DataValue, b) = a.hasvalue && get(a) == b + same_value(a::DataValue, b::Missing) = !a.hasvalue + # missing String appears to be read back in as the empty string "" + same_value(a::DataValue{String}, b::Missing) = a.hasvalue && get(a) == "" @test all(zip(data_read, values(data))) do (col_read, col) all(Base.splat(same_value), zip(col_read, col)) From 810aab61e23a852314e764f9a5d318eac58f38b6 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 13:17:21 +0200 Subject: [PATCH 18/22] add comment --- test/runtests.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/runtests.jl b/test/runtests.jl index 52fe636..70f1a0e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -46,7 +46,7 @@ mkdir(testdir) @test length(data_read) == length(data) @test rsdf.headers == collect(keys(data)) - same_value(a::DataValue, b) = a.hasvalue && get(a) == b + same_value(a::DataValue, b) = a.hasvalue && get(a) == b # SAS and SPSS only support Float64 and String, so we can't test === same_value(a::DataValue, b::Missing) = !a.hasvalue # missing String appears to be read back in as the empty string "" same_value(a::DataValue{String}, b::Missing) = a.hasvalue && get(a) == "" From 40e93f6916a34a2e73b1af56815f70f934e892cf Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 13:25:13 +0200 Subject: [PATCH 19/22] throw for uninferred table schema --- src/ReadStat.jl | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index f4b50e8..c26a333 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -315,6 +315,9 @@ function write_data_file(filetype::Val, io::IO, source) rows = Tables.rows(source) schema = Tables.schema(rows) + if schema === nothing + error("Could not determine table schema for data source.") + end variables_array = [] variables_array = map(schema.names, schema.types) do column_name, column_type From 117465eb38e689ab8777b4f4ad1445c850a96d66 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 13:25:20 +0200 Subject: [PATCH 20/22] add broken long string test --- test/runtests.jl | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/runtests.jl b/test/runtests.jl index 70f1a0e..5bdd7a0 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -55,5 +55,14 @@ mkdir(testdir) all(Base.splat(same_value), zip(col_read, col)) end end + + @testset "Long string" begin + data = (x = ["a" ^ 2046, missing],) + filepath = joinpath(testdir, "testwrite_longstring.$ext") + writer(filepath, data) + rsdf = reader(filepath) + data_read = rsdf.data + @test_broken get(data_read[1][1]) == "a" ^ 2046 + end end end From 26dfb360773294cc7a981eba003644eb6db11ed9 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 13:31:51 +0200 Subject: [PATCH 21/22] pass file label via kwargs --- src/ReadStat.jl | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index c26a333..598aac1 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -295,7 +295,7 @@ function handle_write!(data::Ptr{UInt8}, len::Cint, ctx::Ptr) return len end -function Writer(source; file_label="File Label") +function Writer(; file_label) writer = ccall((:readstat_writer_init, libreadstat), Ptr{Nothing}, ()) write_bytes = @cfunction(handle_write!, Cint, (Ptr{UInt8}, Cint, Ptr{Nothing})) ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), writer, write_bytes) @@ -303,15 +303,15 @@ function Writer(source; file_label="File Label") return writer end -function write_data_file(filename::AbstractString, filetype::Val, source) +function write_data_file(filename::AbstractString, filetype::Val, source; kwargs...) io = open(filename, "w") - write_data_file(filetype::Val, io, source) + write_data_file(filetype::Val, io, source; kwargs...) close(io) end -function write_data_file(filetype::Val, io::IO, source) - writer = Writer(source) +function write_data_file(filetype::Val, io::IO, source; file_label = "") + writer = Writer(; file_label = file_label) rows = Tables.rows(source) schema = Tables.schema(rows) @@ -326,8 +326,6 @@ function write_data_file(filetype::Val, io::IO, source) # readstat_variable_set_label(variable, String(field)) TODO: label for a variable end - - readstat_begin_writing(writer, filetype, io, length(rows)) for row in rows @@ -376,10 +374,10 @@ read_por(filename::AbstractString) = read_data_file(filename, Val(:por)) read_sas7bdat(filename::AbstractString) = read_data_file(filename, Val(:sas7bdat)) read_xport(filename::AbstractString) = read_data_file(filename, Val(:xport)) -write_dta(filename::AbstractString, source) = write_data_file(filename, Val(:dta), source) -write_sav(filename::AbstractString, source) = write_data_file(filename, Val(:sav), source) -write_por(filename::AbstractString, source) = write_data_file(filename, Val(:por), source) -write_sas7bdat(filename::AbstractString, source) = write_data_file(filename, Val(:sas7bdat), source) -write_xport(filename::AbstractString, source) = write_data_file(filename, Val(:xport), source) +write_dta(filename::AbstractString, source; kwargs...) = write_data_file(filename, Val(:dta), source; kwargs...) +write_sav(filename::AbstractString, source; kwargs...) = write_data_file(filename, Val(:sav), source; kwargs...) +write_por(filename::AbstractString, source; kwargs...) = write_data_file(filename, Val(:por), source; kwargs...) +write_sas7bdat(filename::AbstractString, source; kwargs...) = write_data_file(filename, Val(:sas7bdat), source; kwargs...) +write_xport(filename::AbstractString, source; kwargs...) = write_data_file(filename, Val(:xport), source; kwargs...) end #module ReadStat From b2c48c4c65be6f5ffbf9b480781d2759ae022559 Mon Sep 17 00:00:00 2001 From: Julius Krumbiegel Date: Mon, 1 Aug 2022 13:35:25 +0200 Subject: [PATCH 22/22] add filelabel test --- src/ReadStat.jl | 8 ++++---- test/runtests.jl | 8 ++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ReadStat.jl b/src/ReadStat.jl index 598aac1..5de462a 100644 --- a/src/ReadStat.jl +++ b/src/ReadStat.jl @@ -295,11 +295,11 @@ function handle_write!(data::Ptr{UInt8}, len::Cint, ctx::Ptr) return len end -function Writer(; file_label) +function Writer(; filelabel) writer = ccall((:readstat_writer_init, libreadstat), Ptr{Nothing}, ()) write_bytes = @cfunction(handle_write!, Cint, (Ptr{UInt8}, Cint, Ptr{Nothing})) ccall((:readstat_set_data_writer, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), writer, write_bytes) - ccall((:readstat_writer_set_file_label, libreadstat), Cvoid, (Ptr{Nothing}, Cstring), writer, file_label) + ccall((:readstat_writer_set_file_label, libreadstat), Cvoid, (Ptr{Nothing}, Cstring), writer, filelabel) return writer end @@ -310,8 +310,8 @@ function write_data_file(filename::AbstractString, filetype::Val, source; kwargs end -function write_data_file(filetype::Val, io::IO, source; file_label = "") - writer = Writer(; file_label = file_label) +function write_data_file(filetype::Val, io::IO, source; filelabel = "") + writer = Writer(; filelabel = filelabel) rows = Tables.rows(source) schema = Tables.schema(rows) diff --git a/test/runtests.jl b/test/runtests.jl index 5bdd7a0..09344fa 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -64,5 +64,13 @@ mkdir(testdir) data_read = rsdf.data @test_broken get(data_read[1][1]) == "a" ^ 2046 end + + @testset "File metadata" begin + data = (a = Int32[1, 2, 3],) + filepath = joinpath(testdir, "testwrite_file_metadata.$ext") + writer(filepath, data; filelabel = "Test label") + rsdf = reader(filepath) + @test rsdf.filelabel == "Test label" + end end end