Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "ReadStat"
uuid = "d71aba96-b539-5138-91ee-935c3ee1374c"
version = "1.1.2-DEV"
version = "1.2.0-DEV"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand All @@ -12,9 +12,9 @@ TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[compat]
julia = "1.3"
julia = "1.10"
DataValues = "0.4.13, 0.5, 1"
ReadStat_jll = "1.1.1"
ReadStat_jll = "1.1.9"

[targets]
test = ["Test", "TestItemRunner"]
2 changes: 1 addition & 1 deletion src/C_interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
end

function readstat_get_modified_time(metadata::Ptr{Nothing})
return ccall((:readstat_get_modified_time, libreadstat), UInt, (Ptr{Nothing},), metadata)
return ccall((:readstat_get_modified_time, libreadstat), Int64, (Ptr{Nothing},), metadata)
end

function readstat_get_file_format_version(metadata::Ptr{Nothing})
Expand Down Expand Up @@ -55,23 +55,23 @@
return ccall((:readstat_value_type, libreadstat), Cint, (Value,), val)
end

function readstat_parse(filename::String, type::Val{:dta}, parser::Ptr{Nothing}, ds::ReadStatDataFrame)

Check notice on line 58 in src/C_interface.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_function_argument

An argument is included in a function signature but not used within its body.
return ccall((:readstat_parse_dta, libreadstat), Cint, (Ptr{Nothing}, Cstring, Any), parser, string(filename), ds)
end

function readstat_parse(filename::String, type::Val{:sav}, parser::Ptr{Nothing}, ds::ReadStatDataFrame)

Check notice on line 62 in src/C_interface.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_function_argument

An argument is included in a function signature but not used within its body.
return ccall((:readstat_parse_sav, libreadstat), Cint, (Ptr{Nothing}, Cstring, Any), parser, string(filename), ds)
end

function readstat_parse(filename::String, type::Val{:por}, parser::Ptr{Nothing}, ds::ReadStatDataFrame)

Check notice on line 66 in src/C_interface.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_function_argument

An argument is included in a function signature but not used within its body.
return ccall((:readstat_parse_por, libreadstat), Cint, (Ptr{Nothing}, Cstring, Any), parser, string(filename), ds)
end

function readstat_parse(filename::String, type::Val{:sas7bdat}, parser::Ptr{Nothing}, ds::ReadStatDataFrame)

Check notice on line 70 in src/C_interface.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_function_argument

An argument is included in a function signature but not used within its body.
return ccall((:readstat_parse_sas7bdat, libreadstat), Cint, (Ptr{Nothing}, Cstring, Any), parser, string(filename), ds)
end

function readstat_parse(filename::String, type::Val{:xport}, parser::Ptr{Nothing}, ds::ReadStatDataFrame)

Check notice on line 74 in src/C_interface.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_function_argument

An argument is included in a function signature but not used within its body.
return ccall((:readstat_parse_xport, libreadstat), Cint, (Ptr{Nothing}, Cstring, Any), parser, string(filename), ds)
end

Expand Down
49 changes: 14 additions & 35 deletions src/ReadStat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,17 @@
##############################################################################

const READSTAT_TYPE_STRING = Cint(0)
const READSTAT_TYPE_CHAR = Cint(1)
const READSTAT_TYPE_INT8 = Cint(1)
const READSTAT_TYPE_INT16 = Cint(2)
const READSTAT_TYPE_INT32 = Cint(3)
const READSTAT_TYPE_FLOAT = Cint(4)
const READSTAT_TYPE_DOUBLE = Cint(5)
const READSTAT_TYPE_LONG_STRING = Cint(6)
const READSTAT_TYPE_STRING_REF = Cint(6)

# Julia type for each readstat_type_t, indexed by the enum value plus one.
# STRING_REF is a reference into a string table, so it surfaces as a String
# just like STRING does.
const READSTAT_TYPES = (String, Int8, Int16, Int32, Float32, Float64, String)

const READSTAT_ERROR_OPEN = Cint(1)
const READSTAT_ERROR_READ = Cint(2)
Expand Down Expand Up @@ -85,13 +90,6 @@
##
##############################################################################

function handle_info!(obs_count::Cint, var_count::Cint, ds_ptr::Ptr{ReadStatDataFrame})
ds = unsafe_pointer_to_objref(ds_ptr)
ds.rows = obs_count
ds.columns = var_count
return Cint(0)
end

function handle_metadata!(metadata::Ptr{Nothing}, ds_ptr::Ptr{ReadStatDataFrame})
ds = unsafe_pointer_to_objref(ds_ptr)
ds.filelabel = readstat_get_file_label(metadata)
Expand All @@ -114,33 +112,18 @@
ptr == C_NULL ? "" : unsafe_string(ptr)
end

function get_type(data_type::Cint)
if data_type == READSTAT_TYPE_STRING
return String
elseif data_type == READSTAT_TYPE_CHAR
return Int8
elseif data_type == READSTAT_TYPE_INT16
return Int16
elseif data_type == READSTAT_TYPE_INT32
return Int32
elseif data_type == READSTAT_TYPE_FLOAT
return Float32
elseif data_type == READSTAT_TYPE_DOUBLE
return Float64
end
return Nothing
end
get_type(data_type::Cint) = READSTAT_TYPES[data_type + 1]
get_type(variable::Ptr{Nothing}) = get_type(readstat_variable_get_type(variable))

get_storagewidth(variable::Ptr{Nothing}) = readstat_variable_get_storage_width(variable)

get_measure(variable::Ptr{Nothing}) = readstat_variable_get_measure(variable)

get_alignment(variable::Ptr{Nothing}) = readstat_variable_get_measure(variable)
get_alignment(variable::Ptr{Nothing}) = readstat_variable_get_alignment(variable)

function handle_variable!(var_index::Cint, variable::Ptr{Nothing},
val_label::Cstring, ds_ptr::Ptr{ReadStatDataFrame})
col = var_index + 1

Check notice on line 126 in src/ReadStat.jl

View workflow job for this annotation

GitHub Actions / julia-ci / lint

unused_binding

Variable has been assigned but not used.
ds = unsafe_pointer_to_objref(ds_ptr)::ReadStatDataFrame
missing_count = readstat_variable_get_missing_ranges_count(variable)

Expand All @@ -165,10 +148,7 @@
return Cint(0)
end

function get_type(val::Value)
data_type = readstat_value_type(val)
return [String, Int8, Int16, Int32, Float32, Float64, String][data_type + 1]
end
get_type(val::Value) = get_type(readstat_value_type(val))

Base.convert(::Type{Int8}, val::Value) = ccall((:readstat_int8_value, libreadstat), Int8, (Value,), val)
Base.convert(::Type{Int16}, val::Value) = ccall((:readstat_int16_value, libreadstat), Int16, (Value,), val)
Expand Down Expand Up @@ -265,15 +245,14 @@

function Parser()
parser = ccall((:readstat_parser_init, libreadstat), Ptr{Nothing}, ())
info_fxn = @cfunction(handle_info!, Cint, (Cint, Cint, Ptr{ReadStatDataFrame}))
meta_fxn = @cfunction(handle_metadata!, Cint, (Ptr{Nothing}, Ptr{ReadStatDataFrame}))
var_fxn = @cfunction(handle_variable!, Cint, (Cint, Ptr{Nothing}, Cstring, Ptr{ReadStatDataFrame}))
val_fxn = @cfunction(handle_value!, Cint, (Cint, Ptr{Nothing}, ReadStatValue, Ptr{ReadStatDataFrame}))
label_fxn = @cfunction(handle_value_label!, Cint, (Cstring, Value, Cstring, Ptr{ReadStatDataFrame}))
ccall((:readstat_set_metadata_handler, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, meta_fxn)
ccall((:readstat_set_variable_handler, libreadstat), Int, (Ptr{Nothing}, Ptr{Nothing}), parser, var_fxn)
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)
ccall((:readstat_set_metadata_handler, libreadstat), Cint, (Ptr{Nothing}, Ptr{Nothing}), parser, meta_fxn)
ccall((:readstat_set_variable_handler, libreadstat), Cint, (Ptr{Nothing}, Ptr{Nothing}), parser, var_fxn)
ccall((:readstat_set_value_handler, libreadstat), Cint, (Ptr{Nothing}, Ptr{Nothing}), parser, val_fxn)
ccall((:readstat_set_value_label_handler, libreadstat), Cint, (Ptr{Nothing}, Ptr{Nothing}), parser, label_fxn)
return parser
end

Expand Down
22 changes: 17 additions & 5 deletions test/test_readstat.jl
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
@testitem "ReadStat" begin
using DataValues

@testset "ReadStat: $ext files" for (reader, ext) in
((read_dta, "dta"),
(read_sav, "sav"),
(read_sas7bdat, "sas7bdat"),
(read_xport, "xpt"))
# Expected alignments are readstat_alignment_t values:
# 0 = UNKNOWN, 1 = LEFT, 2 = CENTER, 3 = RIGHT.
@testset "ReadStat: $ext files" for (reader, ext, alignments) in
((read_dta, "dta", Int32[3, 3, 3, 3, 3, 3]),
(read_sav, "sav", Int32[0, 0, 0, 0, 0, 0]),
(read_sas7bdat, "sas7bdat", Int32[0, 0, 0, 0, 0, 0]),
(read_xport, "xpt", Int32[3, 3, 3, 3, 3, 1]))

dtafile = joinpath(@__DIR__, "types.$ext")
rsdf = reader(dtafile)
Expand All @@ -19,5 +21,15 @@
@test data[4] == DataValueArray{Int16}([2, 7, NA])
@test data[5] == DataValueArray{Int8}([2, 7., NA])
@test data[6] == DataValueArray{String}(["2", "7", ""])

# Alignments must come from readstat_variable_get_alignment, not from
# the measure accessor sitting next to it in the C API. Every fixture
# reports measure UNKNOWN, so reading the wrong one yields all zeros.
@test rsdf.alignments == alignments
@test rsdf.measures == Int32[0, 0, 0, 0, 0, 0]

# Every readstat_type_t the readers emit must map to a concrete Julia
# type; a gap in that mapping used to surface as Nothing.
@test all(!=(Nothing), rsdf.types)
end
end
Loading