@@ -3,7 +3,7 @@ import * as unzipper from "unzipper";
33import { Uri , ProgressOptions , ProgressLocation , commands , window } from "vscode" ;
44import * as fs from "fs-extra" ;
55import * as path from "path" ;
6- import { DatabaseManager } from "./databases" ;
6+ import { DatabaseManager , DatabaseItem } from "./databases" ;
77import { ProgressCallback , showAndLogErrorMessage , withProgress } from "./helpers" ;
88
99export default async function promptFetchDatabase ( dbm : DatabaseManager , storagePath : string ) {
@@ -28,13 +28,13 @@ export default async function promptFetchDatabase(dbm: DatabaseManager, storageP
2828 }
2929}
3030
31- async function databaseFetcher (
31+ export async function databaseFetcher (
3232 databaseUrl : string ,
3333 databasesManager : DatabaseManager ,
3434 storagePath : string ,
35- progressCallback : ProgressCallback
36- ) : Promise < void > {
37- progressCallback ( {
35+ progressCallback ? : ProgressCallback
36+ ) : Promise < DatabaseItem > {
37+ progressCallback ?. ( {
3838 maxStep : 3 ,
3939 message : 'Downloading database' ,
4040 step : 1
@@ -45,36 +45,27 @@ async function databaseFetcher(
4545 await fs . ensureDir ( storagePath ) ;
4646 const unzipPath = await getStorageFolder ( storagePath , databaseUrl ) ;
4747
48- const response = await fetch . default ( databaseUrl ) ;
49- const unzipStream = unzipper . Extract ( {
50- path : unzipPath
51- } ) ;
52- progressCallback ( {
53- maxStep : 3 ,
54- message : 'Unzipping database' ,
55- step : 2
56- } ) ;
57- await new Promise ( ( resolve , reject ) => {
58- response . body . on ( 'error' , reject ) ;
59- unzipStream . on ( 'error' , reject ) ;
60- unzipStream . on ( 'close' , resolve ) ;
61- response . body . pipe ( unzipStream ) ;
62- } ) ;
63- progressCallback ( {
48+ if ( isFile ( databaseUrl ) ) {
49+ await readAndUnzip ( databaseUrl , unzipPath ) ;
50+ } else {
51+ await fetchAndUnzip ( databaseUrl , unzipPath , progressCallback ) ;
52+ }
53+
54+ progressCallback ?.( {
6455 maxStep : 3 ,
6556 message : 'Opening database' ,
6657 step : 3
6758 } ) ;
6859
69- // if there is a single directory inside, then assume that's what we want to import
70- const dirs = await fs . readdir ( unzipPath ) ;
71- const dbPath = dirs ?. length === 1 && ( await fs . stat ( path . join ( unzipPath , dirs [ 0 ] ) ) ) . isDirectory
72- ? path . join ( unzipPath , dirs [ 0 ] )
73- : unzipPath ;
74-
75- // might need to upgrade before importing...
76- const item = await databasesManager . openDatabase ( Uri . parse ( dbPath ) ) ;
77- databasesManager . setCurrentDatabaseItem ( item ) ;
60+ const dbPath = await findDirWithFile ( unzipPath , '.dbinfo' ) ;
61+ if ( dbPath ) {
62+ // might need to upgrade before importing...
63+ const item = await databasesManager . openDatabase ( Uri . parse ( dbPath ) ) ;
64+ databasesManager . setCurrentDatabaseItem ( item ) ;
65+ return item ;
66+ } else {
67+ throw new Error ( 'Database not found in archive.' ) ;
68+ }
7869}
7970
8071async function getStorageFolder ( storagePath : string , urlStr : string ) {
@@ -110,3 +101,68 @@ function validateUrl(databaseUrl: string) {
110101 throw new Error ( 'Must use https for downloading a database.' ) ;
111102 }
112103}
104+
105+ async function readAndUnzip ( databaseUrl : string , unzipPath : string ) {
106+ const unzipStream = unzipper . Extract ( {
107+ path : unzipPath ,
108+ verbose : true
109+ } ) ;
110+
111+ await new Promise ( ( resolve , reject ) => {
112+ // we already know this is a file scheme
113+ const databaseFile = Uri . parse ( databaseUrl ) . path ;
114+ const stream = fs . createReadStream ( databaseFile ) ;
115+ stream . on ( 'error' , reject ) ;
116+ unzipStream . on ( 'error' , reject ) ;
117+ unzipStream . on ( 'close' , resolve ) ;
118+ stream . pipe ( unzipStream ) ;
119+ } ) ;
120+ }
121+
122+ async function fetchAndUnzip ( databaseUrl : string , unzipPath : string , progressCallback ?: ProgressCallback ) {
123+ const response = await fetch . default ( databaseUrl ) ;
124+ const unzipStream = unzipper . Extract ( {
125+ path : unzipPath
126+ } ) ;
127+ progressCallback ?.( {
128+ maxStep : 3 ,
129+ message : 'Unzipping database' ,
130+ step : 2
131+ } ) ;
132+ await new Promise ( ( resolve , reject ) => {
133+ response . body . on ( 'error' , reject ) ;
134+ unzipStream . on ( 'error' , reject ) ;
135+ unzipStream . on ( 'close' , resolve ) ;
136+ response . body . pipe ( unzipStream ) ;
137+ } ) ;
138+ }
139+
140+ function isFile ( databaseUrl : string ) {
141+ return Uri . parse ( databaseUrl ) . scheme === 'file' ;
142+ }
143+
144+ /**
145+ * Recursively looks for a file in a directory. If the file exists, then returns the directory containing the file.
146+ *
147+ * @param dir The directory to search
148+ * @param toFind The file to recursively look for in this directory
149+ *
150+ * @returns the directory containing the file, or undefined if not found.
151+ */
152+ async function findDirWithFile ( dir : string , toFind : string ) : Promise < string | undefined > {
153+ if ( ! ( await fs . stat ( dir ) ) . isDirectory ( ) ) {
154+ return ;
155+ }
156+ const files = await fs . readdir ( dir ) ;
157+ if ( files . includes ( toFind ) ) {
158+ return dir ;
159+ }
160+ for ( const file of files ) {
161+ const newPath = path . join ( dir , file ) ;
162+ const result = await findDirWithFile ( newPath , toFind ) ;
163+ if ( result ) {
164+ return result ;
165+ }
166+ }
167+ return ;
168+ }
0 commit comments