-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathZipTree.jl
139 lines (115 loc) · 3.71 KB
/
ZipTree.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
using ZipFile
struct ZipPathInfo # Rename to ZipEntry ?
is_dir::Bool
path::RelPath
end
struct ZipTreeRoot
reader::ZipFile.Reader
file_info::Vector{ZipPathInfo} # same order as reader.files
end
function ZipTreeRoot(path::AbstractString)
reader = ZipFile.Reader(path)
ZipTreeRoot(reader, _zip_path_info.(reader.files))
end
Base.show(io::IO, root::ZipTreeRoot) = print(io, "ZipTreeRoot(...)")
function _zip_path_info(f)
name = f.name
is_dir = endswith(name, '/')
name = is_dir ? name[1:end-1] : name
path = DataSets.RelPath(split(name, '/'))
ZipPathInfo(is_dir, path)
end
#-------------------------------------------------------------------------------
# FIXME: Factor back together with BlobTree.jl !!
struct ZippedBlobTree <: AbstractBlobTree
root::ZipTreeRoot
path::RelPath
end
ZippedBlobTree(root::ZipTreeRoot) = ZippedBlobTree(root, RelPath())
Base.basename(tree::ZippedBlobTree) = basename(tree.path)
function Base.getindex(tree::ZippedBlobTree, path::RelPath)
newpath = joinpath(tree.path, path)
i = findfirst(tree.root.file_info) do info
info.path == newpath
end
if i == nothing
error("Path $newpath doesn't exist in $tree")
elseif tree.root.file_info[i].is_dir
ZippedBlobTree(tree.root, newpath)
else
Blob(tree.root, newpath)
end
end
function Base.getindex(tree::ZippedBlobTree, name::AbstractString)
getindex(tree, joinpath(RelPath(), name))
end
function _tree_children(tree::ZippedBlobTree)
children = String[]
for (i,info) in enumerate(tree.root.file_info)
if dirname(info.path) == tree.path
push!(children, basename(info.path))
end
end
children
end
Base.IteratorSize(tree::ZippedBlobTree) = Base.SizeUnknown()
function Base.iterate(tree::ZippedBlobTree, state=nothing)
if state == nothing
children = _tree_children(tree)
itr = iterate(children)
else
(children, cstate) = state
itr = iterate(children, cstate)
end
if itr == nothing
return nothing
else
(name, cstate) = itr
(tree[name], (children, cstate))
end
end
function Base.joinpath(tree::ZippedBlobTree, r::RelPath)
# Should this AbsPath be rooted at `tree` rather than `tree.root`?
AbsPath(tree.root, joinpath(tree.path, r))
end
function Base.joinpath(tree::ZippedBlobTree, s::AbstractString)
AbsPath(tree.root, joinpath(tree.path, s))
end
function Base.open(func::Function, f::Blob{ZipTreeRoot}; write=false, read=!write)
if write
error("Error writing file at read-only path $f")
end
i = findfirst(info->info.path == f.path, f.root.file_info)
io = f.root.reader.files[i]
_seek(io, 0)
func(io)
end
#=
w = ZipFile.Writer("/tmp/example.zip");
f = ZipFile.addfile(w, "hello.txt");
write(f, "hello world!\n");
f = ZipFile.addfile(w, "julia.txt", method=ZipFile.Deflate);
write(f, "Julia\n"^5);
close(w)
r = ZipFile.Reader("/tmp/example.zip");
for f in r.files
println("Filename: $(f.name)")
write(stdout, read(f, String));
end
close(r)
=#
#-------------------------------------------------------------------------------
# Fixes for ZipFile.jl
# TODO: Remove these once https://github.com/fhs/ZipFile.jl/pull/75 is in a release.
# It appears that ZipFile.jl just doesn't have a way to rewind to the start of
# one of the embedded files.
function _seek(io::ZipFile.ReadableFile, n::Integer)
# Only support seeking to the start
n == 0 || throw(ArguementError("Cannot efficiently seek zip stream to nonzero offset $n"))
io._datapos = -1
io._currentcrc32 = 0
io._pos = 0
io._zpos = 0
end
# Needed for use as `src` in `write(dst::IO, src::IO)`.
Base.readavailable(io::ZipFile.ReadableFile) = read(io)