Line data Source code
1 : /*
2 : * SPDX-License-Identifier: GPL-3.0-only
3 : * MuseScore/Audacity CLA applies
4 : *
5 : * Copyright (C) 2026 MuseScore/Audacity and others
6 : *
7 : * This program is free software: you can redistribute it and/or modify
8 : * it under the terms of the GNU General Public License version 3 as
9 : * published by the Free Software Foundation.
10 : *
11 : * This program is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 : */
19 : #include "commanddispatcher.h"
20 :
21 : #include "async/promise.h"
22 :
23 : #include "../commandable.h"
24 : #include "../commandtypes.h"
25 :
26 : #include "log.h"
27 :
28 : using namespace muse;
29 : using namespace muse::rcommand;
30 :
31 32 : CommandDispatcher::~CommandDispatcher()
32 : {
33 544 : for (auto it = m_clients.begin(); it != m_clients.end(); ++it) {
34 512 : Commandable* client = it->second.client;
35 512 : if (client) {
36 512 : client->setDispatcher(nullptr);
37 : }
38 : }
39 :
40 32 : m_clients.clear();
41 32 : }
42 :
43 0 : async::Promise<Response> CommandDispatcher::dispatch(const Request& request)
44 : {
45 0 : if (request.command == NONE_COMMAND) {
46 0 : return async::make_promise<Response>([request](auto resolve) {
47 0 : return resolve(make_response(request, make_ret(Ret::Code::Ok)));
48 0 : });
49 : }
50 :
51 0 : auto it = m_clients.find(request.command);
52 0 : if (it == m_clients.end()) {
53 0 : LOGW() << "command not registered: " << request.command;
54 0 : return async::make_promise<Response>([request](auto resolve) {
55 0 : return resolve(make_response(request, make_ret(Ret::Code::UnknownError)));
56 0 : });
57 : }
58 :
59 0 : LOGI() << "try call command: " << request.command << ", params: " << request.params;
60 :
61 0 : Client client = it->second;
62 :
63 0 : return async::make_promise<Response>([this, request, client](auto resolve) {
64 0 : if (client.asyncCallback) {
65 0 : bool allowDispatch = true;
66 0 : m_preDispatch.send(request.command, &allowDispatch);
67 0 : if (!allowDispatch) {
68 0 : return resolve(make_response(request, make_ret(Ret::Code::Cancel)));
69 : }
70 :
71 0 : client.asyncCallback(request, [this, request, resolve](const Response& response) {
72 0 : (void)resolve(response);
73 0 : m_postDispatch.send(request.command);
74 : });
75 :
76 0 : return async::Promise<Response>::dummy_result();
77 : }
78 :
79 0 : if (client.callback) {
80 0 : bool allowDispatch = true;
81 0 : m_preDispatch.send(request.command, &allowDispatch);
82 0 : if (!allowDispatch) {
83 0 : return resolve(make_response(request, make_ret(Ret::Code::Cancel)));
84 : }
85 :
86 0 : auto res = resolve(client.callback(request));
87 0 : m_postDispatch.send(request.command);
88 0 : return res;
89 : }
90 :
91 0 : UNREACHABLE;
92 : return resolve(make_response(request, make_ret(Ret::Code::UnknownError)));
93 0 : });
94 0 : }
95 :
96 36 : Response CommandDispatcher::dispatch(const Command& command, const Params& params)
97 : {
98 36 : Request request = make_request(command, params);
99 :
100 36 : auto it = m_clients.find(command);
101 36 : if (it == m_clients.end()) {
102 0 : LOGW() << "command not registered: " << command;
103 0 : return make_response(request, make_ret(Ret::Code::UnknownError));
104 : }
105 :
106 36 : LOGI() << "try call command: " << command << " with params: " << params;
107 :
108 36 : CallBack callback = it->second.callback;
109 :
110 36 : IF_ASSERT_FAILED(callback) {
111 0 : return make_response(request, make_ret(Ret::Code::NotSupported));
112 : }
113 :
114 36 : return callback(request);
115 36 : }
116 :
117 0 : Response CommandDispatcher::dispatch(const CommandQuery& query)
118 : {
119 0 : return dispatch(query.uri(), query.params());
120 : }
121 :
122 512 : void CommandDispatcher::onRequest(Commandable* client, const Command& command, const CallBack& callback)
123 : {
124 512 : reg(client, command, { client, callback, nullptr });
125 1024 : }
126 :
127 0 : void CommandDispatcher::onRequest(Commandable* client, const Command& command, const AsyncCallBack& callback)
128 : {
129 0 : reg(client, command, { client, nullptr, callback });
130 0 : }
131 :
132 512 : void CommandDispatcher::reg(Commandable* client, const Command& command, const Client& c)
133 : {
134 512 : if (m_clients.find(command) != m_clients.end()) {
135 0 : LOGW() << "command already registered: " << command;
136 0 : return;
137 : }
138 :
139 512 : m_clients[command] = c;
140 512 : client->setDispatcher(this);
141 : }
142 :
143 0 : void CommandDispatcher::unreg(Commandable* client)
144 : {
145 0 : if (!client || !client->isDispatcher(this)) {
146 0 : return;
147 : }
148 :
149 0 : for (auto it = m_clients.begin(); it != m_clients.end();) {
150 0 : if (it->second.client == client) {
151 0 : it = m_clients.erase(it);
152 0 : continue;
153 : }
154 0 : ++it;
155 : }
156 :
157 0 : client->setDispatcher(nullptr);
158 : }
159 :
160 0 : async::Channel<Command, bool*> CommandDispatcher::preDispatch() const
161 : {
162 0 : return m_preDispatch;
163 : }
164 :
165 0 : async::Channel<Command> CommandDispatcher::postDispatch() const
166 : {
167 0 : return m_postDispatch;
168 : }
|