66#include " AppInstallerErrors.h"
77#include " VTSupport.h"
88
9+ #include < filesystem>
10+
911namespace AppInstaller ::CLI
1012{
1113 namespace
@@ -23,12 +25,123 @@ namespace AppInstaller::CLI
2325 info << std::endl;
2426 info << error.GetDescription () << std::endl;
2527 }
28+
29+ struct ErrorGroup
30+ {
31+ std::string_view Name;
32+ WORD MinCode; // HRESULT_CODE, inclusive
33+ WORD MaxCode; // HRESULT_CODE, inclusive
34+ };
35+
36+ // Groups matching the sections in the published returnCodes.md documentation.
37+ constexpr ErrorGroup s_errorGroups[] =
38+ {
39+ { " General Errors" , 0x0001 , 0x00FF },
40+ { " Install errors." , 0x0100 , 0x01FF },
41+ { " Check for package installed status" , 0x0200 , 0x02FF },
42+ { " Configuration Errors" , 0xC000 , 0xC0FF },
43+ { " Configuration Processor Errors" , 0xC100 , 0xC1FF },
44+ };
45+
46+ constexpr std::string_view s_uncategorizedGroupName = " Uncategorized" ;
47+
48+ // Writes the markdown table for a group of errors to the given stream.
49+ void WriteMarkdownGroup (std::ostream& out, std::string_view groupName, std::vector<const Errors::HResultInformation*>& errors)
50+ {
51+ if (errors.empty ())
52+ {
53+ return ;
54+ }
55+
56+ std::sort (errors.begin (), errors.end (),
57+ [](const Errors::HResultInformation* a, const Errors::HResultInformation* b) { return HRESULT_CODE (a->Value ()) < HRESULT_CODE (b->Value ()); });
58+
59+ out << " \n ## " << groupName << " \n\n " ;
60+ out << " | Hex | Decimal | Symbol | Description |\n " ;
61+ out << " |-------------|-------------|-------------|-------------|\n " ;
62+
63+ for (const auto * error : errors)
64+ {
65+ HRESULT hr = error->Value ();
66+ char hexBuf[11 ];
67+ sprintf_s (hexBuf, " 0x%08X" , static_cast <uint32_t >(hr));
68+
69+ out << " | " << hexBuf
70+ << " | " << static_cast <int32_t >(hr)
71+ << " | " << error->Symbol ()
72+ << " | " << error->GetDescription ()
73+ << " |\n " ;
74+ }
75+ }
76+
77+ void OutputErrorMarkdown (Execution::Context& context)
78+ {
79+ auto allErrors = Errors::GetWinGetErrors ();
80+
81+ // Categorize each error into its group; the last slot holds uncategorized errors.
82+ static constexpr size_t s_errorGroupsCount = ARRAYSIZE (s_errorGroups);
83+ std::vector<std::vector<const Errors::HResultInformation*>> groupedErrors (s_errorGroupsCount + 1 );
84+
85+ for (const auto & error : allErrors)
86+ {
87+ HRESULT hr = error->Value ();
88+ WORD code = static_cast <WORD >(HRESULT_CODE (hr));
89+
90+ bool placed = false ;
91+ for (size_t i = 0 ; i < s_errorGroupsCount; ++i)
92+ {
93+ if (code >= s_errorGroups[i].MinCode && code <= s_errorGroups[i].MaxCode )
94+ {
95+ groupedErrors[i].push_back (error.get ());
96+ placed = true ;
97+ break ;
98+ }
99+ }
100+
101+ if (!placed)
102+ {
103+ groupedErrors[s_errorGroupsCount].push_back (error.get ());
104+ }
105+ }
106+
107+ std::filesystem::path outputPath{ Utility::ConvertToUTF16 (context.Args .GetArg (Execution::Args::Type::OutputFile)) };
108+ std::ofstream out{ outputPath };
109+ THROW_HR_IF (HRESULT_FROM_WIN32 (ERROR_OPEN_FAILED ), !out);
110+
111+ std::time_t now = std::time (nullptr );
112+ std::tm tm{};
113+ localtime_s (&tm, &now);
114+ char dateBuf[11 ];
115+ strftime (dateBuf, sizeof (dateBuf), " %m/%d/%Y" , &tm);
116+
117+ out <<
118+ " ---\n "
119+ " title: WinGet HRESULT Codes\n "
120+ " description: WinGet return codes and their meanings\n "
121+ " ms.date: " << dateBuf << " \n "
122+ " ms.topic: article\n "
123+ " ms.localizationpriority: low\n "
124+ " ---\n "
125+ " \n "
126+ " > [!NOTE]\n "
127+ " > This file is generated by running: winget error --output <file>\n "
128+ " \n "
129+ " # Return Codes\n " ;
130+
131+ for (size_t i = 0 ; i < ARRAYSIZE (s_errorGroups); ++i)
132+ {
133+ WriteMarkdownGroup (out, s_errorGroups[i].Name , groupedErrors[i]);
134+ }
135+
136+ WriteMarkdownGroup (out, s_uncategorizedGroupName, groupedErrors[s_errorGroupsCount]);
137+ }
26138 }
27139
28140 std::vector<Argument> ErrorCommand::GetArguments () const
29141 {
30142 return {
31- Argument{ Execution::Args::Type::ErrorInput, Resource::String::ErrorInputArgumentDescription, ArgumentType::Positional, true },
143+ Argument{ Execution::Args::Type::ErrorInput, Resource::String::ErrorInputArgumentDescription, ArgumentType::Positional },
144+ Argument{ Execution::Args::Type::OutputFile, Resource::String::ErrorOutputFileArgumentDescription, ArgumentType::Standard },
32145 };
33146 }
34147
@@ -42,8 +155,27 @@ namespace AppInstaller::CLI
42155 return { Resource::String::ErrorCommandLongDescription };
43156 }
44157
158+ void ErrorCommand::ValidateArgumentsInternal (Execution::Args& execArgs) const
159+ {
160+ if (execArgs.Contains (Execution::Args::Type::OutputFile) && execArgs.Contains (Execution::Args::Type::ErrorInput))
161+ {
162+ throw CommandException (Resource::String::ErrorOutputFileConflictsWithInput);
163+ }
164+
165+ if (!execArgs.Contains (Execution::Args::Type::OutputFile) && !execArgs.Contains (Execution::Args::Type::ErrorInput))
166+ {
167+ throw CommandException (Resource::String::ErrorRequiresInputOrOutputFile);
168+ }
169+ }
170+
45171 void ErrorCommand::ExecuteInternal (Execution::Context& context) const
46172 {
173+ if (context.Args .Contains (Execution::Args::Type::OutputFile))
174+ {
175+ OutputErrorMarkdown (context);
176+ return ;
177+ }
178+
47179 std::string input{ context.Args .GetArg (Execution::Args::Type::ErrorInput) };
48180
49181 const char * begin = input.c_str ();
0 commit comments