ExecutionReporter.h (8990B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #pragma once 4 #include "ExecutionProgress.h" 5 #include "ChannelStreams.h" 6 #include "Resources.h" 7 #include "VTSupport.h" 8 #include <AppInstallerProgress.h> 9 #include <winget/LocIndependent.h> 10 11 #include <wil/resource.h> 12 13 #include <atomic> 14 #include <iomanip> 15 #include <istream> 16 #include <memory> 17 #include <optional> 18 #include <ostream> 19 #include <string> 20 21 22 namespace AppInstaller::CLI::Execution 23 { 24 #define WINGET_OSTREAM_FORMAT_HRESULT(hr) "0x" << Logging::SetHRFormat << hr 25 26 // One of the options available to the users when prompting for something. 27 struct BoolPromptOption 28 { 29 BoolPromptOption(Resource::StringId labelId, char hotkey, bool value) 30 : Label(labelId), Hotkey(std::string(1, hotkey)), Value(value) {} 31 32 Utility::LocIndString Hotkey; 33 Resource::LocString Label; 34 35 // Value associated with this option. 36 bool Value; 37 }; 38 39 // Reporter should be the central place to show workflow status to user. 40 struct Reporter : public IProgressSink 41 { 42 // The channel that the reporter is targeting. 43 // Based on commands/arguments, only one of these channels can be chosen. 44 enum class Channel 45 { 46 Output, 47 Completion, 48 Json, 49 Disabled, 50 }; 51 52 // The level for the Output channel. 53 enum class Level : uint32_t 54 { 55 None = 0x0, 56 Verbose = 0x1, 57 Info = 0x2, 58 Warning = 0x4, 59 Error = 0x8, 60 All = Verbose | Info | Warning | Error, 61 }; 62 63 Reporter(); 64 Reporter(std::ostream& outStream, std::istream& inStream); 65 Reporter(const Reporter&) = delete; 66 Reporter& operator=(const Reporter&) = delete; 67 68 Reporter(Reporter&&) = default; 69 Reporter& operator=(Reporter&&) = default; 70 71 // Request that a clone be constructed from the given reporter. 72 struct clone_t {}; 73 Reporter(const Reporter& other, clone_t); 74 75 ~Reporter(); 76 77 // Gets the primary device attributes if available. 78 std::optional<VirtualTerminal::PrimaryDeviceAttributes> GetPrimaryDeviceAttributes(); 79 80 // Get a stream for verbose output. 81 OutputStream Verbose() { return GetOutputStream(Level::Verbose); } 82 83 // Get a stream for informational output. 84 OutputStream Info() { return GetOutputStream(Level::Info); } 85 86 // Get a stream for warning output. 87 OutputStream Warn() { return GetOutputStream(Level::Warning); } 88 89 // Get a stream for error output. 90 OutputStream Error() { return GetOutputStream(Level::Error); } 91 92 // Get a stream for outputting completion words. 93 OutputStream Completion() { return OutputStream(*m_out, m_channel == Channel::Completion, false); } 94 95 // Get a stream for outputting completion words. 96 OutputStream Json() { return OutputStream(*m_out, m_channel == Channel::Json, false); } 97 98 // Gets a stream for output of the given level. 99 OutputStream GetOutputStream(Level level); 100 101 void EmptyLine() { GetBasicOutputStream() << std::endl; } 102 103 // Sets the channel that will be reported to. 104 // Only do this once and as soon as the channel is determined. 105 void SetChannel(Channel channel); 106 107 // Sets the visual style (mostly for progress currently) 108 void SetStyle(AppInstaller::Settings::VisualStyle style); 109 110 // Get the raw input stream. 111 std::istream& RawInputStream(); 112 113 // Check if the input stream is interactive or not. 114 bool InputStreamIsInteractive() const; 115 116 // Prompts the user, return true if they consented. 117 bool PromptForBoolResponse(Resource::LocString message, Level level = Level::Info, bool resultIfDisabled = false); 118 119 // Prompts the user, continues when Enter is pressed 120 void PromptForEnter(Level level = Level::Info); 121 122 // Prompts the user for a path. 123 std::filesystem::path PromptForPath(Resource::LocString message, Level level = Level::Info, std::filesystem::path resultIfDisabled = std::filesystem::path::path()); 124 125 // IProgressSink 126 void BeginProgress() override; 127 void OnProgress(uint64_t current, uint64_t maximum, ProgressType type) override; 128 void SetProgressMessage(std::string_view message) override; 129 void EndProgress(bool hideProgressWhenDone) override; 130 131 // Contains the objects used for async progress and the lifetime of said progress. 132 struct AsyncProgressScope 133 { 134 AsyncProgressScope(Reporter& reporter, IProgressSink* sink, bool hideProgressWhenDone); 135 ~AsyncProgressScope(); 136 137 AsyncProgressScope(const AsyncProgressScope&) = delete; 138 AsyncProgressScope& operator=(const AsyncProgressScope&) = delete; 139 140 AsyncProgressScope(AsyncProgressScope&&) = delete; 141 AsyncProgressScope& operator=(AsyncProgressScope&&) = delete; 142 143 ProgressCallback& Callback(); 144 IProgressCallback* operator->(); 145 146 bool HideProgressWhenDone() const; 147 void HideProgressWhenDone(bool value); 148 149 private: 150 std::reference_wrapper<Reporter> m_reporter; 151 ProgressCallback m_callback; 152 std::atomic_bool m_hideProgressWhenDone; 153 }; 154 155 // Runs the given callable of type: auto(IProgressCallback&) 156 template <typename F> 157 auto ExecuteWithProgress(F&& f, bool hideProgressWhenDone = false) 158 { 159 auto progressScope = BeginAsyncProgress(hideProgressWhenDone); 160 return f(progressScope->Callback()); 161 } 162 163 // Begins an asynchronous progress operation. 164 std::unique_ptr<AsyncProgressScope> BeginAsyncProgress(bool hideProgressWhenDone = false); 165 166 // Sets the in progress callback. 167 void SetProgressCallback(ProgressCallback* callback); 168 169 // Cancels the in progress task. 170 void CancelInProgressTask(bool force, CancelReason reason); 171 172 void CloseOutputStream(bool forceDisable = false); 173 174 void SetProgressSink(IProgressSink* sink) 175 { 176 m_progressSink = sink; 177 } 178 179 bool IsLevelEnabled(Level reporterLevel) 180 { 181 return WI_AreAllFlagsSet(m_enabledLevels, reporterLevel); 182 } 183 184 void SetLevelMask(Level reporterLevel, bool setEnabled = true); 185 186 // Determines if sixels are supported by the current instance. 187 bool SixelsSupported(); 188 189 // Determines if sixels are enabled; they must be both supported and enabled by user settings. 190 bool SixelsEnabled(); 191 192 private: 193 Reporter(std::shared_ptr<BaseStream> outStream, std::istream& inStream); 194 // Gets a stream for output for internal use. 195 OutputStream GetBasicOutputStream(); 196 197 // Used to show indefinite progress. Currently an indefinite spinner is the form of 198 // showing indefinite progress. 199 // running: shows indefinite progress if set to true, stops indefinite progress if set to false 200 void ShowIndefiniteProgress(bool running); 201 202 Channel m_channel = Channel::Output; 203 std::shared_ptr<BaseStream> m_out; 204 std::istream& m_in; 205 std::optional<AppInstaller::Settings::VisualStyle> m_style; 206 std::unique_ptr<IIndefiniteSpinner> m_spinner; 207 std::unique_ptr<IProgressBar> m_progressBar; 208 wil::srwlock m_progressCallbackLock; 209 std::atomic<ProgressCallback*> m_progressCallback; 210 std::atomic<IProgressSink*> m_progressSink; 211 DWORD m_outStreamFileType = FILE_TYPE_UNKNOWN; 212 DWORD m_inStreamFileType = FILE_TYPE_UNKNOWN; 213 214 // Enable all levels by default 215 Level m_enabledLevels = Level::All; 216 }; 217 218 DEFINE_ENUM_FLAG_OPERATORS(Reporter::Level); 219 220 // Indirection to enable change without tracking down every place 221 extern const VirtualTerminal::Sequence& HelpCommandEmphasis; 222 extern const VirtualTerminal::Sequence& HelpArgumentEmphasis; 223 extern const VirtualTerminal::Sequence& ManifestInfoEmphasis; 224 extern const VirtualTerminal::Sequence& SourceInfoEmphasis; 225 extern const VirtualTerminal::Sequence& NameEmphasis; 226 extern const VirtualTerminal::Sequence& IdEmphasis; 227 extern const VirtualTerminal::Sequence& UrlEmphasis; 228 extern const VirtualTerminal::Sequence& PromptEmphasis; 229 extern const VirtualTerminal::Sequence& ConvertToUpgradeFlowEmphasis; 230 extern const VirtualTerminal::Sequence& ConfigurationIntentEmphasis; 231 extern const VirtualTerminal::Sequence& ConfigurationUnitEmphasis; 232 extern const VirtualTerminal::Sequence& AuthenticationEmphasis; 233 }