1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "create_stream.h"
17 
18 #include <memory>
19 #include <tuple>
20 
21 #include "../../common/napi/n_async/n_async_work_callback.h"
22 #include "../../common/napi/n_async/n_async_work_promise.h"
23 #include "../../common/napi/n_class.h"
24 #include "../../common/napi/n_func_arg.h"
25 #include "../../common/napi/n_val.h"
26 #include "../../common/uni_error.h"
27 
28 #include "../class_stream/stream_entity.h"
29 #include "../class_stream/stream_n_exporter.h"
30 
31 namespace OHOS {
32 namespace DistributedFS {
33 namespace ModuleFileIO {
34 using namespace std;
35 
InstantiateStream(napi_env env,unique_ptr<FILE,decltype (& fclose)> fp)36 static NVal InstantiateStream(napi_env env, unique_ptr<FILE, decltype(&fclose)> fp)
37 {
38     napi_value objStream = NClass::InstantiateClass(env, StreamNExporter::className_, {});
39     if (!objStream) {
40         UniError(EIO).ThrowErr(env, "INNER BUG. Cannot instantiate stream");
41         return NVal();
42     }
43 
44     auto streamEntity = NClass::GetEntityOf<StreamEntity>(env, objStream);
45     if (!streamEntity) {
46         UniError(EIO).ThrowErr(env, "Cannot instantiate stream because of void entity");
47         return NVal();
48     }
49 
50     streamEntity->fp.swap(fp);
51     return { env, objStream };
52 }
53 
GetCreateStreamArgs(napi_env env,const NFuncArg & funcArg)54 static tuple<bool, string, string> GetCreateStreamArgs(napi_env env, const NFuncArg &funcArg)
55 {
56     auto [resGetFirstArg, path, unused] = NVal(env, funcArg[NARG_POS::FIRST]).ToUTF8StringPath();
57     if (!resGetFirstArg) {
58         UniError(EINVAL).ThrowErr(env, "Invalid path");
59         return { false, "", "" };
60     }
61 
62     auto [resGetSecondArg, mode, useless] = NVal(env, funcArg[NARG_POS::SECOND]).ToUTF8String();
63     if (!resGetSecondArg) {
64         UniError(EINVAL).ThrowErr(env, "Invalid mode");
65         return { false, "", "" };
66     }
67 
68     return { true, path.get(), mode.get() };
69 }
70 
Sync(napi_env env,napi_callback_info info)71 napi_value CreateStream::Sync(napi_env env, napi_callback_info info)
72 {
73     NFuncArg funcArg(env, info);
74     if (!funcArg.InitArgs(NARG_CNT::TWO)) {
75         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
76         return nullptr;
77     }
78 
79     auto [resGetCreateStreamArgs, argPath, argMode] = GetCreateStreamArgs(env, funcArg);
80     if (!resGetCreateStreamArgs) {
81         return nullptr;
82     }
83 
84     unique_ptr<FILE, decltype(&fclose)> fp = { fopen(argPath.c_str(), argMode.c_str()), fclose };
85     if (!fp) {
86         UniError(errno).ThrowErr(env);
87         return nullptr;
88     }
89 
90     return InstantiateStream(env, move(fp)).val_;
91 }
92 
93 struct AsyncCreateStreamArg {
94     unique_ptr<FILE, decltype(&fclose)> fp = { nullptr, fclose };
95 };
96 
Async(napi_env env,napi_callback_info info)97 napi_value CreateStream::Async(napi_env env, napi_callback_info info)
98 {
99     NFuncArg funcArg(env, info);
100     if (!funcArg.InitArgs(NARG_CNT::TWO, NARG_CNT::THREE)) {
101         UniError(EINVAL).ThrowErr(env, "Number of arguments unmatched");
102         return nullptr;
103     }
104 
105     auto [resGetCreateStreamArgs, argPath, argMode] = GetCreateStreamArgs(env, funcArg);
106     if (!resGetCreateStreamArgs) {
107         return nullptr;
108     }
109 
110     auto arg = make_shared<AsyncCreateStreamArg>();
111     auto cbExec = [arg, argPath = move(argPath), argMode = move(argMode)](napi_env env) -> UniError {
112         arg->fp = { fopen(argPath.c_str(), argMode.c_str()), fclose };
113         if (!arg->fp) {
114             return UniError(errno);
115         } else {
116             return UniError(ERRNO_NOERR);
117         }
118     };
119 
120     auto cbCompl = [arg](napi_env env, UniError err) -> NVal {
121         if (err) {
122             return { env, err.GetNapiErr(env) };
123         }
124         return InstantiateStream(env, move(arg->fp));
125     };
126 
127     const string procedureName = "FileIOCreateStream";
128     NVal thisVar(env, funcArg.GetThisVar());
129     if (funcArg.GetArgc() == NARG_CNT::TWO) {
130         return NAsyncWorkPromise(env, thisVar).Schedule(procedureName, cbExec, cbCompl).val_;
131     } else {
132         NVal cb(env, funcArg[NARG_POS::THIRD]);
133         return NAsyncWorkCallback(env, thisVar, cb).Schedule(procedureName, cbExec, cbCompl).val_;
134     }
135 }
136 } // namespace ModuleFileIO
137 } // namespace DistributedFS
138 } // namespace OHOS