ALib C++ Library
Library Version: 2412 R0
Documentation generated by doxygen
Loading...
Searching...
No Matches
sample.cpp
1// #################################################################################################
2// ALib C++ Library
3// Configuration Sample
4//
5// Copyright 2024 A-Worx GmbH, Germany
6// Published under Boost Software License (a free software license, see LICENSE.txt)
7// #################################################################################################
8#include "alib/alib.hpp"
9#if !DOXYGEN // otherwise this sample would be seen in the ALib dox
10
11DOX_MARKER( [DOX_EXPR_TUT_CLI_INCLUDES])
12// Include necessary ALib CLI headers
13#include "alib/cli/commandline.hpp"
14#include "alib/compatibility/std_strings_iostream.hpp" // Support to write ALib strings and boxes to cout
15#include "alib/compatibility/std_strings_functional.hpp" // Support to write ALib strings and boxes to cout
16#include "alib/lang/basecamp/camp_inlines.hpp" // Add missing inline functions of class Camp
17#include "alib/lang/basecamp/bootstrap.hpp" // Support customized module bootstrapping
18#include "alib/cli/cliutil.hpp"
19#include "alib/enums/recordbootstrap.hpp" // Support for bootstrapping resourced enum records
20#include "alib/time/datetime.hpp" // ALib date/time types
21#include "alib/lang/system/calendar.hpp" // ALib calendar formatting
22#include <filesystem> // C++ filesystem
23
24// namespaces to use locally
25using namespace alib;
26using namespace alib::cli;
27DOX_MARKER( [DOX_EXPR_TUT_CLI_INCLUDES])
28
29DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS])
30// #################################################################################################
31// Enumerations of Commands, Parameters, Options and ExitCodes of the CLI application
32// #################################################################################################
33enum class Commands
34{
35 Now = 1, ///< Returns the current date.
36 File = 2, ///< returns the modification date of a file or directory.
37 Help = 99, ///< Prints a help text.
38};
39
40enum class Options
41{
42 Format = 0, ///< Overwrite the default format string.
43 Help = 99, ///< Show help text. (We allow this as option as well a command)
44};
45
46enum class Parameters
47{
48 Filename = 0, ///< Used with command \e file to denote the file.
49 Topic = 1, ///< Used with command \e help to optionally denote a help topic.
50};
51
52enum class ExitCodes
53{
54 OK = 0, ///< Success.
55 ErrUnknownCommand = 100, ///< Unkown command given.
56 ErrUnknownOption = 101, ///< Unkown option given.
57 ErrMissingFilename = 102, ///< Command "file" given without a filename.
58 ErrUnknownHelpTopic = 103, ///< Command or option "help" given without an unknown subtopic.
59 ErrInternalError = 255, ///< Unspecified internal error.
60 /// (this demo might be incomplete :-)
61};
62DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS])
63
64DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_ASSIGN])
65// assigning ALib enum records
70DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_ASSIGN])
71
72DOX_MARKER( [DOX_EXPR_TUT_CLI_CUSTOM_CAMP])
73// #################################################################################################
74// The custom ALib module, needed to define externalized resources.
75// #################################################################################################
76class SampleCamp : public lang::Camp
77{
78 public:
79 // Constructor. Passes version number and resource name to the module class
80 SampleCamp()
81 : Camp( "DATEMOD" )
82 {}
83
84 protected:
85 // Initialization of the module.
86 virtual void bootstrap( BootstrapPhases phase ) override
87 {
88 if( phase == BootstrapPhases::PrepareResources)
89 {
90 // Add bulk !
91 resourcePool->BootstrapBulk( ResourceCategory,
92 #define EOS ,
93
94 // ################################### Single Strings ######################################
95 "AppInfo", A_CHAR( "@HL-"
96 "Command line tool 'date'. V. {}.{} (in fact a sample application only)\n"
97 "(c) 2023-{} AWorx GmbH. Published under MIT License (Open Source).\n"
98 "For more information see: https://alib.dev\n"
99 "@HL-"),
100
101 // ######################################## Commands ######################################
102 "Commands", A_CHAR(
103 //enum ident minread Params
104 "1," "now" ",1" "," ","
105 "2," "file" ",1" ",filename" ","
106 "99," "help" ",1" ",topic" ) EOS
107
108 "Commands<", A_CHAR("datesample::Commands::"),
109
110 "THlpCmdSht_now", A_CHAR("Reports the actual date/time"),
111 "THlpCmdLng_now", A_CHAR("Reports the actual date/time. May be omitted, as this is the\n"
112 "default if no command is given.") EOS
113
114 "THlpCmdSht_file", A_CHAR("Returns the date/time of a file. "),
115 "THlpCmdLng_file", A_CHAR("Returns the last modification date/time of a file.") EOS
116
117 "THlpCmdSht_help", A_CHAR("Displays usage information. "),
118 "THlpCmdLng_help", A_CHAR("Displays usage information. Can also be given as an "
119 "option '--help'.") EOS
120
121
122 // ######################################## Options ######################################
123 "Options", A_CHAR(
124 //enum ident minread identChar in-arg-separ. args to consume ShortcutTo
125 "0," "format" ",1," "f," "=" ",1," ","
126 "99," "help" ",1," "h," "=" ",0," ) EOS
127
128 "Options<", A_CHAR("datesample::Options::"),
129
130 "TOptUsg_format", A_CHAR("--format[=]\"placholders\""),
131 "TOptHlp_format", A_CHAR("Sets the output format. The format specification is given with\n"
132 "documentation of ALib method CalendarDateTime::Format, found here:\n"
133 "https://alib.dev/classaworx_1_1lib_1_1system_1_1CalendarDateTime.html" ) ,
134 "TOptUsg_help" , A_CHAR("--help[[=]TOPIC]"),
135 "TOptHlp_help" , A_CHAR("Displays usage information.")
136 EOS
137
138 // ######################################## Parameters ######################################
139 "Parameters", A_CHAR(
140 //enum name minIdentLen identifier in-arg-sep delim args to consume isOptional
141 // (if empty -> mandatory!)
142 "0," "FILENAME" ",1," "" "," "=" "," ",-1" ",0" ","
143 "1," "TOPIC" ",1," "" "," "=" "," ",-1" ",1" ) EOS
144
145 "Parameters<", A_CHAR("datesample::Parameters::"),
146 "THlpParSht_FILENAME", A_CHAR("Mandatory parameter of command 'file."),
147 "THlpParLng_FILENAME", A_CHAR("Denotes the file that is used for retrieving the modification date.\n"
148 "This parameter is mandatory to command file and has to be appended\n"
149 "to this command, separated by '='"),
150 "THlpParSht_TOPIC" , A_CHAR("Optional parameter of command (or option) 'help'."),
151 "THlpParLng_TOPIC" , A_CHAR("Denotes a specific toopic that the help command should be verbose about.")
152 EOS
153
154 // ######################################## ExitCodes ######################################
155 "ExitCodes", A_CHAR(
156 //enum name assoc. cli exception
157 "0," "OK" ",-1" ","
158 "100," "ErrUnknownCommand" ",-1" ","
159 "101," "ErrUnknownOption" ",-1" ","
160 "102," "ErrMissingFilename" ",-1" ","
161 "103," "ErrUnknownHelpTopic" ",-1" ","
162 "255," "ErrInternalError" ",-1" ) EOS
163
164 "ExitCodes<", A_CHAR("datesample::"),
165
166 "TExit0" , A_CHAR("Success (no error).")
167 ,"TExit100" , A_CHAR("An unknown command was given. Valid commands are 'now' and 'file'")
168 ,"TExit101" , A_CHAR("An unknown option was given. The only valid option is '--format='FORMATSPEC'.")
169 ,"TExit102" , A_CHAR("Command 'file' given without a filename argument.")
170 ,"TExit103" , A_CHAR("Command or option 'help' given without an unknown subtopic.")
171 ,"TExit255" , A_CHAR("Unspecified internal error.")
172 EOS
173
174 // ################################### Help Texts ######################################
175 "HlpCLIAppName", A_CHAR("date"),
176 "HlpUsage" , A_CHAR("date [format=\"FORMATSPEC\" [now]|[file FILENAME]"),
177 "HlpHdlOpts" , A_CHAR("OPTIONS:" ),
178 "HlpHdlCmds" , A_CHAR("COMMANDS:" ),
179 "HlpHdlExtCds" , A_CHAR("EXIT CODES:" ),
180 "HlpHdlUsage" , A_CHAR("USAGE:" ),
181 "HlpHdlDscr" , A_CHAR( "DESCRIPTION:" ),
182 "HlpHdlPDscr" , A_CHAR("PARAMETER DESCRIPTION:" ),
183 "HlpHdlTopic" , A_CHAR("Help on {} {!Q<>}:\n" ),
184
185 "HlpGeneral", A_CHAR(
186 "\nABOUT date\n"
187 "@>>"
188 "This is a sample application provided with C++ library 'ALib'\n"
189 "to demonstrate the use of its sub-module \"ALib CLI\"."
190 "\n@<<\n" )
191 EOS
192
193 // end of AddBulk()
194 nullptr );
195 }
196
197 else if( phase == BootstrapPhases::PrepareConfig )
198 {
203 }
204 }
205
206 // Termination this module. (Nothing to do.)
207 virtual void shutdown( ShutdownPhases phase ) override
208 { (void) phase; }
209
210}; // class SampleCamp
211DOX_MARKER( [DOX_EXPR_TUT_CLI_CUSTOM_CAMP])
212
213DOX_MARKER( [DOX_EXPR_TUT_CLI_CUSTOM_CAMP_SINGLETON])
214// The module singleton object
215extern SampleCamp SAMPLE_CAMP;
216SampleCamp SAMPLE_CAMP;
217DOX_MARKER( [DOX_EXPR_TUT_CLI_CUSTOM_CAMP_SINGLETON])
218
219DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_ASSIGN2])
220// Specifying our custom module to hold resources of our enum records
221ALIB_RESOURCED_IN_MODULE( Commands , SAMPLE_CAMP, "Commands" )
222ALIB_RESOURCED_IN_MODULE( Parameters, SAMPLE_CAMP, "Parameters" )
223ALIB_RESOURCED_IN_MODULE( Options , SAMPLE_CAMP, "Options" )
224ALIB_RESOURCED_IN_MODULE( ExitCodes , SAMPLE_CAMP, "ExitCodes" )
225DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_ASSIGN2])
226
227DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_FWDDECL])
228// forward declaration
229ExitCodes processCLI( CommandLine& cli );
230DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_FWDDECL])
231
232DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_MAIN])
233// #################################################################################################
234// The main() function of the CLI application
235// #################################################################################################
236int main( int argc, const char **argv )
237{
238 alib::ARG_C = argc;
239 alib::ARG_VN = argv;
240
241 // 1. Add our custom module to the list of modules
243 alib::CAMPS.PushBack( &SAMPLE_CAMP );
244
245 // 2. Initialize all modules
247
248 // 3. now we start catching exceptions
249 Enum result= ExitCodes::ErrInternalError;
250 try
251 {
252 // 4. Create the central command line interface object app and perform
253 // mandatory initializations.
254 CommandLine cli;
255 {
256 // Read copyright string from resources and format to current version and year
257 Paragraphs buffer;
258 buffer.LineWidth= 70;
259 buffer.AddMarked( SAMPLE_CAMP.GetResource( "AppInfo" ),
262 CalendarDateTime(DateTime()).Year );
263 cli.AppInfo.Allocate(cli.GetAllocator(), buffer.Buffer);
264
265 // Initialize the CLI with the module to fetch the resources from.
266 cli.Init( &SAMPLE_CAMP );
267
268 // Read enum records from resources and build up corresponding object lists.
269 cli.DefineParameters<enum Parameters>();
270 cli.DefineCommands <enum Commands >();
271 cli.DefineOptions <enum Options >();
272 cli.DefineExitCodes <enum ExitCodes >();
273
274 // Read options from the command line
275 cli.ReadOptions();
276 }
277
278 // 5. check for unprocess options (not allowed with this demo. Other application might pass
279 // those to other libraries or parts of the software, which provide their own option
280 // processing.
281 if( cli.OptionArgsIgnored.Count() )
282 {
283 result= ExitCodes::ErrUnknownOption;
284 std::cerr << "Error: Unknown option given \""
285 << cli.OptionArgsIgnored.Front()
286 << "\"" << std::endl;
287 goto END;
288 }
289
290 // 6. Now, the truly custom part: Process commands and options
291 result= processCLI( cli );
292 }
293
294 // fetch exceptions and assign a corresponding exit code (error code)
295 catch( Exception& e)
296 {
297 std::cerr << e.Format() << std::endl; // print out human-readable exception information
298 result= e.Back().Type; // For this demo, just return the internal exception
299 // number as "exit code".
300 }
301 catch(std::runtime_error& e)
302 {
303 result= ExitCodes::ErrInternalError;
304 std::cerr << "A runtime error occurred: " << e.what()<< std::endl;
305 }
306
307 // 7. That's it.
308 END:
310 return int(result.Integral());
311}
312DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_MAIN])
313
314
315DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_PROCESS])
316// #################################################################################################
317// The custom function to process CLI params
318// #################################################################################################
319ExitCodes processCLI( CommandLine& cli )
320{
321 AString format; // The date output format
322 Paragraphs helpText; // A buffer for help texts
323 DateTime dt; // The timestamp to output
324
325 format << "yyyy-MM-dd HH:mm:ss";
326
327 //------- check for option 'format' -------
328 Option* option= cli.GetOption( Options::Format);
329 if( option )
330 {
331 format.Reset( option->Args.Front() );
332 }
333
334 //------- check for option 'help' -------
335 option= cli.GetOption( Options::Help);
336 if( option )
337 {
338 if( !CLIUtil::GetHelp( cli, nullptr, option, helpText ) )
339 {
340 std::cerr << "Error: Unknown help Topic \""
341 << (option->Args.Count() > 0 ? option->Args.Front() : String() )
342 << "\"" << std::endl
343 << "Usage Information follows: " << std::endl << std::endl;
344 option->Args.Clear();
345 helpText.Clear();
346 CLIUtil::GetHelp( cli, nullptr, option, helpText );
347 }
348 std::cout << helpText.Buffer << std::endl;
349 return ExitCodes::OK;
350 }
351
352 //------- No command recognized? This is allowed, assuming now -------
353 cli.ReadNextCommands();
354 if( cli.CommandsParsed.Count() == 0 )
355 {
356 // Still a command was given? This is not allowed
357 if( cli.ArgsLeft.size() > 0 )
358 {
359 std::cerr << "Error: Unknown command given \""
360 << cli.ArgStrings.at(std::size_t(*cli.ArgsLeft.begin()))
361 << "\"" << std::endl;
362 return ExitCodes::ErrUnknownCommand;
363 }
364
365 // No command, results in command "now"
367 AString printBuffer;
368 calendar.Format( format, printBuffer, lang::CurrentData::Clear );
369 std::cout << printBuffer << std::endl;
370
371 return ExitCodes::OK;
372 }
373
374 //------- Command loop -------
375 // Note: Making a loop here is optional. We do it to allow multiple commands
376 // with one invokation of the application.
377 Command* actCmd;
378 while ( (actCmd= cli.NextCommand()) != nullptr )
379 {
380 auto actCmdCode= actCmd->Declaration->Element();
381
382 if ( actCmdCode == Commands::Now )
383 {
384 dt= DateTime();
385 }
386
387 else if ( actCmdCode == Commands::File )
388 {
389 // check if filename was given as paraemter
390 if(actCmd->ParametersMandatory.Count() < 1)
391 {
392 std::cerr << "Error: no filename given with command 'file'" << std::endl;
393 std::cerr << "Usage: " << CLIUtil::GetCommandUsageFormat(cli, *actCmd->Declaration )
394 << std::endl;
395 return ExitCodes::ErrMissingFilename;
396 }
397
398 // get file (or directory) modification date
399 String4K name( actCmd->ParametersMandatory.Front()->Args.Front() );
400 std::filesystem::path path( name.Terminate() );
401 dt.Import( std::chrono::clock_cast<std::chrono::system_clock>(
402 std::filesystem::last_write_time( path ) ) ) ;
403 }
404
405 else if ( actCmdCode == Commands::Help )
406 {
407 if( !CLIUtil::GetHelp( cli, actCmd, nullptr, helpText ) )
408 {
409 std::cerr << "Error: Unknown help topic" << std::endl;
410 std::cerr << "Usage: " << CLIUtil::GetCommandUsageFormat(cli, *actCmd->Declaration )
411 << std::endl;
412 return ExitCodes::ErrUnknownHelpTopic;
413 }
414 std::cout << helpText.Buffer << std::endl;
415 continue;
416 }
417
418
419 // execute printing of commands "now" and "file"
421 AString printBuffer;
422 calendar.Format( format, printBuffer, lang::CurrentData::Clear );
423 std::cout << printBuffer << std::endl;
424 }
425 return ExitCodes::OK;
426}
427DOX_MARKER( [DOX_EXPR_TUT_CLI_ENUMS_PROCESS])
428
429#endif // !DOXYGEN
const Enum & Element() const
void Init(lang::Camp *resModule)
virtual ALIB_API Command * NextCommand()
List< MonoAllocator, Command * > CommandsParsed
A list of commands actually parsed. Filled with method ReadNextCommands.
MonoAllocator & GetAllocator() noexcept
StdVectorMono< integer > ArgsLeft
List< MonoAllocator, String, Recycling::Shared > OptionArgsIgnored
virtual ALIB_API void ReadOptions()
ALIB_API Option * GetOption(Enum element)
StdVectorMono< String > ArgStrings
virtual ALIB_API void ReadNextCommands()
ALIB_API Message & Back() const
ALIB_API AString & Format(AString &target) const
void AddMarked(boxing::TBoxes< TAllocatorArgs > &args)
ALIB_API Paragraphs & Clear()
integer LineWidth
Used as parameter lineWidth of static method invocations.
ALIB_API AString & Format(Substring format, AString &target, lang::CurrentData targetData=lang::CurrentData::Keep) const
void Allocate(TAllocator &allocator, const TString< TChar > &copy)
Definition string.hpp:2012
void Import(TTimePoint timePoint)
#define A_CHAR(STR)
#define ALIB_ENUMS_ASSIGN_RECORD(TEnum, TRecord)
Definition records.hpp:712
#define ALIB_RESOURCED_IN_MODULE(T, Camp, ResName)
@ Filename
Denotes the actual source file as the scope.
Definition alib.cpp:69
ShutdownPhases
Termination levels usable with Bootstrapping ALib Camps.
Definition alib.hpp:1418
BootstrapPhases
Initialization levels usable with Bootstrapping ALib Camps.
Definition alib.hpp:1393
const char ** ARG_VN
Definition alib.cpp:72
ALIB_API List< MonoAllocator, lang::Camp * > CAMPS
void BootstrapAddDefaultCamps()
Definition bootstrap.cpp:59
int ARG_C
Definition alib.cpp:71
ALIB_WARNINGS_RESTORE void Bootstrap(int alibVersion, int alibRevision, TCompilationFlags compilationFlags)
Definition alib.cpp:84
unsigned char REVISION
Definition alib.cpp:78
void Shutdown()
Definition alib.cpp:122
int VERSION
Definition alib.cpp:77
integer Integral() const
Definition enum.hpp:119
A command of a ALib CLI command line.
List< MonoAllocator, Parameter *, Recycling::Shared > ParametersMandatory
Mandatory parameters parsed.
CommandDecl * Declaration
The underlying declaration.
ALib Enum Record type used by class CommandDecl.
ALib Enum Record type used by class OptionDecl.
ALib Enum Record type used by class ParameterDecl.
Definition arguments.hpp:62
List< MonoAllocator, String, Recycling::Shared > Args
Arguments belonging to this option.