API
 
Loading...
Searching...
No Matches
stdFileName.hpp
Go to the documentation of this file.
1/** \file stdFileName.hpp
2 * \brief The stdFileName class for managing standard file names
3 * \ingroup file_files
4 *
5 */
6
7#ifndef file_stdFileName_hpp
8#define file_stdFileName_hpp
9
10#include <filesystem>
11#include <chrono>
12#include <format>
13
14#include <flatlogs/flatlogs.hpp>
15
16#include "../common/defaults.hpp"
17
18#include "../common/exceptions.hpp"
19
20#include "stdSubDir.hpp"
21#include "fileTimes.hpp"
22
23namespace MagAOX
24{
25namespace file
26{
27
28#ifdef XWCTEST_NAMESPACE
29namespace XWCTEST_NAMESPACE
30{
31#endif
32
33/// Organize and analyze the name of a standard file name
34template <typename verboseT = XWC_DEFAULT_VERBOSITY>
36{
37
38 protected:
39 std::string m_fullName; ///< The full name of the file, including path
40 std::string m_baseName; ///< The base name of the file, not including path
41
42 std::string m_extension; ///< The extension of the file
43
44 std::string m_appName; ///< The name of the application which wrote the file
45
46 stdSubDir<verboseT> m_subDir; ///< The subdirectory of the file
47
48 int m_hour{ 0 }; ///< The hour of the timestamp
49 int m_minute{ 0 }; ///< The minute of the timestamp
50 int m_second{ 0 }; ///< The second of the timestamp
51 int m_nsec{ 0 }; ///< The nanosecond of the timestamp
52
53 flatlogs::timespecX m_timestamp{ 0, 0 }; ///< The timestamp
54
55 bool m_valid{ false }; ///< Whether or not the filename parsed correctly and the components are valid
56
57 public:
58 /// Default c'tor
60
61 /// Construct from a full name
62 /** This calls fullName(const std::string &), which parses the input and
63 * populates all fields.
64 *
65 * On success, sets `m_valid=true`
66 *
67 *
68 *
69 * \throws nested MagAOX::xwcException on an exception from fullName.
70 *
71 */
72 explicit stdFileName( const std::string &fullName /**< [in] The new full name of the file (including the path)*/ );
73
74 /// Assignment operator from string
75 /** Sets the full name, which is the only way to set any of the values. This parses the input and
76 * populates all fields.
77 *
78 * On success, sets `m_valid=true`
79 *
80 * On error, sets `m_valid=false`
81 *
82 * \returns a reference the `this`
83 *
84 * \throws nested MagAOX::xwcException on an exception from fullName.
85 *
86 */
87 stdFileName &operator=( const std::string &fullname /**< [in] The new full name of the file
88 (including the path)*/
89 );
90
91 /// Sets the full name
92 /** Setting the full name is the only way to set any of the values. This parses the input and
93 * populates all fields.
94 *
95 * \throws nested MagAOX::xwcException on a bad_alloc exception.
96 *
97 */
98 mx::error_t fullName( const std::string &fullName /**< [in] The new full name of the file (including the path)*/ );
99
100 /// Get the current value of m_fullName
101 /**
102 * \returns the current value of m_fullName
103 *
104 */
105 const std::string &fullName( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
106
107 /// Get the current value of m_baseName
108 /**
109 * \returns the current value of m_baseName
110 *
111 */
112 const std::string &baseName( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
113
114 /// Get the current value of
115 /**
116 * \returns the current value of
117 *
118 */
119 const std::string &extension( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
120
121 /// Get the current value of m_appName
122 /**
123 * \returns the current value of m_appName
124 *
125 */
126 const std::string &appName( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
127
128 /// Get the current value of m_subDir
129 /**
130 * \returns the current value of m_subDir
131 *
132 */
133 const stdSubDir<verboseT> &subDir( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
134
135 /// Get the year
136 /**
137 * \returns year() from m_subDir
138 *
139 */
140 int year( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
141
142 /// Get the month
143 /**
144 * \returns month from m_subDir
145 *
146 */
147 unsigned month( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
148
149 /// Get the day
150 /**
151 * \returns day from m_subDir
152 *
153 */
154 unsigned day( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
155
156 /// Get the current value of m_hour
157 /**
158 * \returns the current value of m_hour
159 *
160 */
161 int hour( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
162
163 /// Get the current value of m_minute
164 /**
165 * \returns the current value of m_minute
166 *
167 */
168 int minute( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
169
170 /// Get the current value of m_second
171 /**
172 * \returns the current value of m_second
173 *
174 */
175 int second( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
176
177 /// Get the current value of m_nsec
178 /**
179 * \returns the current value of m_nsec
180 *
181 */
182 int nsec( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
183
184 /// Get the current value of m_valid
185 /**
186 * \returns the current value of m_valid
187 *
188 */
189 flatlogs::timespecX timestamp( mx::error_t *errc = nullptr /**< [in] [optional] error code */ ) const;
190
191 /// Get the current value of
192 /**
193 * \returns the current value of
194 *
195 */
196 bool valid() const;
197
198 /// Set all stored values to invalid values
199 void invalidate();
200};
201
202template <class verboseT>
207
208template <class verboseT>
210{
211 try
212 {
213 mx::error_t errc = fullName( fn );
214 if( errc != mx::error_t::noerror )
215 {
216 mx::error_report( errc, "from fullName" );
217 }
218 }
219 catch( ... )
220 {
221 std::throw_with_nested( xwcException( "from fullName" ) );
222 }
223}
224
225template <class verboseT>
227{
228 try
229 {
230 mx::error_t errc = fullName( fn );
231
232 if( errc != mx::error_t::noerror )
233 {
234 mx::error_report<verboseT>( errc, "from fullName" );
235 }
236
237 return *this;
238 }
239 catch( ... ) // will be bad_alloc
240 {
241 std::throw_with_nested( xwcException( "from fullName" ) );
242 }
243}
244
245template <class verboseT>
246mx::error_t stdFileName<verboseT>::fullName( const std::string &fn )
247{
248 // assume it's false beginning at any modification
249 invalidate();
250
251 try
252 {
253 // clang-format off
254 #ifdef XWCTEST_STDFILENAME_FULLNAME_BAD_ALLOC
255 throw std::bad_alloc(); // LCOV_EXCL_LINE
256 #endif
257
258 #ifdef XWCTEST_STDFILENAME_FULLNAME_EXCEPTION
259 throw std::exception(); // LCOV_EXCL_LINE
260 #endif
261 // clang-format on
262
263 m_fullName = fn;
264 }
265 catch( const std::bad_alloc &e )
266 {
267 std::throw_with_nested( xwcException( "from std::string" ) );
268 }
269 catch( const std::exception &e )
270 {
271 return mx::error_report<verboseT>( mx::error_t::std_exception, "from std::string" );
272 }
273
274 try
275 {
276 // clang-format off
277 #ifdef XWCTEST_STDFILENAME_FULLNAME_FS_BAD_ALLOC
278 throw std::bad_alloc(); // LCOV_EXCL_LINE
279 #endif
280
281 #ifdef XWCTEST_STDFILENAME_FULLNAME_FS_FILESYSTEM_ERROR
282 throw std::filesystem::filesystem_error("test", std::error_code(10, std::system_category())); // LCOV_EXCL_LINE
283 #endif
284
285 #ifdef XWCTEST_STDFILENAME_FULLNAME_FS_EXCEPTION
286 throw std::exception(); // LCOV_EXCL_LINE
287 #endif
288 // clang-format on
289
290 std::filesystem::path p( m_fullName );
291
292 m_baseName = p.filename();
293 m_extension = p.extension();
294 }
295 catch( const std::bad_alloc &e )
296 {
297 std::throw_with_nested( xwcException( "extracting basename and extension" ) );
298 }
299 catch( const std::filesystem::filesystem_error &e )
300 {
301 return mx::error_report<verboseT>( mx::error_t::std_filesystem_error,
302 "extracting basename and extension " + m_fullName );
303 }
304 catch( const std::exception &e )
305 {
306 return mx::error_report<verboseT>( mx::error_t::std_exception,
307 "extracting basename and extension from " + m_fullName );
308 }
309
310 if( m_extension == "" )
311 {
312 return mx::error_report<verboseT>( mx::error_t::invalidarg, "No extension found in: " + m_fullName );
313 }
314
315 std::string YYYY, MM, DD, hh, mm, ss, nn;
316
317 try
318 {
319 mx_error_check( parseFilePath( m_appName, YYYY, MM, DD, hh, mm, ss, nn, m_baseName ) );
320 }
321 catch( const xwcException &e ) // a bad_alloc
322 {
323 std::throw_with_nested( xwcException( "parsing filename" ) );
324 }
325
326 mx::error_t errc;
327 int year = mx::ioutils::stoT<int>( YYYY, &errc );
328 mx_error_check_code( errc );
329
330 unsigned int month = mx::ioutils::stoT<unsigned int>( MM, &errc );
331 mx_error_check_code( errc );
332
333 unsigned int day = mx::ioutils::stoT<unsigned int>( DD, &errc );
334 mx_error_check_code( errc );
335
336 m_hour = mx::ioutils::stoT<int>( hh, &errc );
337 mx_error_check_code( errc );
338
339 m_minute = mx::ioutils::stoT<int>( mm, &errc );
340 mx_error_check_code( errc );
341
342 m_second = mx::ioutils::stoT<int>( ss, &errc );
343 mx_error_check_code( errc );
344
345 m_nsec = mx::ioutils::stoT<int>( nn, &errc );
346 mx_error_check_code( errc );
347
348 try
349 {
350 mx_error_check( m_subDir.ymd( year, month, day ) );
351 }
352 catch( ... )
353 {
354 std::throw_with_nested( xwcException( "from stdSubDir::ymd" ) );
355 }
356
357 tm tmst;
358 tmst.tm_year = year - 1900;
359 tmst.tm_mon = month - 1;
360 tmst.tm_mday = day;
361 tmst.tm_hour = m_hour;
362 tmst.tm_min = m_minute;
363 tmst.tm_sec = m_second;
364
365 errno = 0;
366 time_t tgm = timegm( &tmst );
367
368 // clang-format off
369 #ifdef XWCTEST_STDFILENAME_FULLNAME_TIMEGM
370 tgm = static_cast<time_t>( -1 ); // LCOV_EXCL_LINE
371 errno = EOVERFLOW; // LCOV_EXCL_LINE
372 #endif
373 #ifdef XWCTEST_STDFILENAME_FULLNAME_TIMEGM_OTHER
374 tgm = static_cast<time_t>( -1 ); // LCOV_EXCL_LINE
375 #endif
376 // clang-format on
377
378 if( tgm == static_cast<time_t>( -1 ) )
379 {
380 if( errno != 0 )
381 {
382 return mx::error_report<verboseT>( mx::errno2error_t( errno ), "error from timegm" );
383 }
384
385 return mx::error_report<verboseT>( mx::error_t::error, "error from timegm" );
386 }
387
388 m_timestamp.time_s = tgm;
389 m_timestamp.time_ns = m_nsec;
390
391 m_valid = true;
392
393 return mx::error_t::noerror;
394}
395
396template <class verboseT>
397const std::string &stdFileName<verboseT>::fullName( mx::error_t *errc ) const
398{
399 if( !m_valid )
400 {
401 if( errc )
402 {
403 *errc = mx::error_t::invalidconfig;
404 }
405 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
406 }
407 else if( errc )
408 {
409 *errc = mx::error_t::noerror;
410 }
411
412 return m_fullName;
413}
414
415template <class verboseT>
416const std::string &stdFileName<verboseT>::baseName( mx::error_t *errc ) const
417{
418 if( !m_valid )
419 {
420 if( errc )
421 {
422 *errc = mx::error_t::invalidconfig;
423 }
424 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
425 }
426 else if( errc )
427 {
428 *errc = mx::error_t::noerror;
429 }
430
431 return m_baseName;
432}
433
434template <class verboseT>
435const std::string &stdFileName<verboseT>::extension( mx::error_t *errc ) const
436{
437 if( !m_valid )
438 {
439 if( errc )
440 {
441 *errc = mx::error_t::invalidconfig;
442 }
443 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
444 }
445 else if( errc )
446 {
447 *errc = mx::error_t::noerror;
448 }
449
450 return m_extension;
451}
452
453template <class verboseT>
454const std::string &stdFileName<verboseT>::appName( mx::error_t *errc ) const
455{
456 if( !m_valid )
457 {
458 if( errc )
459 {
460 *errc = mx::error_t::invalidconfig;
461 }
462 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
463 }
464 else if( errc )
465 {
466 *errc = mx::error_t::noerror;
467 }
468
469 return m_appName;
470}
471
472template <class verboseT>
473const stdSubDir<verboseT> &stdFileName<verboseT>::subDir( mx::error_t *errc ) const
474{
475 if( !m_valid )
476 {
477 if( errc )
478 {
479 *errc = mx::error_t::invalidconfig;
480 }
481 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
482 }
483 else if( errc )
484 {
485 *errc = mx::error_t::noerror;
486 }
487
488 return m_subDir;
489}
490
491template <class verboseT>
492int stdFileName<verboseT>::year( mx::error_t *errc ) const
493{
494 if( !m_valid )
495 {
496 if( errc )
497 {
498 *errc = mx::error_t::invalidconfig;
499 }
500 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
501 return std::numeric_limits<int>::max();
502 }
503 else if( errc )
504 {
505 *errc = mx::error_t::noerror;
506 }
507
508 return m_subDir.year();
509}
510
511template <class verboseT>
512unsigned int stdFileName<verboseT>::month( mx::error_t *errc ) const
513{
514 if( !m_valid )
515 {
516 if( errc )
517 {
518 *errc = mx::error_t::invalidconfig;
519 }
520 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
521 return std::numeric_limits<unsigned int>::max();
522 }
523 else if( errc )
524 {
525 *errc = mx::error_t::noerror;
526 }
527
528 return m_subDir.month();
529}
530
531template <class verboseT>
532unsigned int stdFileName<verboseT>::day( mx::error_t *errc ) const
533{
534 if( !m_valid )
535 {
536 if( errc )
537 {
538 *errc = mx::error_t::invalidconfig;
539 }
540 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
541 return std::numeric_limits<unsigned int>::max();
542 }
543 else if( errc )
544 {
545 *errc = mx::error_t::noerror;
546 }
547
548 return m_subDir.day();
549}
550
551template <class verboseT>
552int stdFileName<verboseT>::hour( mx::error_t *errc ) const
553{
554 if( !m_valid )
555 {
556 if( errc )
557 {
558 *errc = mx::error_t::invalidconfig;
559 }
560 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
561 return std::numeric_limits<int>::max();
562 }
563 else if( errc )
564 {
565 *errc = mx::error_t::noerror;
566 }
567
568 return m_hour;
569}
570
571template <class verboseT>
572int stdFileName<verboseT>::minute( mx::error_t *errc ) const
573{
574 if( !m_valid )
575 {
576 if( errc )
577 {
578 *errc = mx::error_t::invalidconfig;
579 }
580 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
581 return std::numeric_limits<int>::max();
582 }
583 else if( errc )
584 {
585 *errc = mx::error_t::noerror;
586 }
587
588 return m_minute;
589}
590
591template <class verboseT>
592int stdFileName<verboseT>::second( mx::error_t *errc ) const
593{
594 if( !m_valid )
595 {
596 if( errc )
597 {
598 *errc = mx::error_t::invalidconfig;
599 }
600 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
601 return std::numeric_limits<int>::max();
602 }
603 else if( errc )
604 {
605 *errc = mx::error_t::noerror;
606 }
607
608 return m_second;
609}
610
611template <class verboseT>
612int stdFileName<verboseT>::nsec( mx::error_t *errc ) const
613{
614 if( !m_valid )
615 {
616 if( errc )
617 {
618 *errc = mx::error_t::invalidconfig;
619 }
620 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
621 return std::numeric_limits<int>::max();
622 }
623 else if( errc )
624 {
625 *errc = mx::error_t::noerror;
626 }
627
628 return m_nsec;
629}
630
631template <class verboseT>
633{
634 if( !m_valid )
635 {
636 if( errc )
637 {
638 *errc = mx::error_t::invalidconfig;
639 }
640 mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
641 return flatlogs::timespecX( 0, 0 );
642 }
643 else if( errc )
644 {
645 *errc = mx::error_t::noerror;
646 }
647
648 return m_timestamp;
649}
650
651template <class verboseT>
653{
654 return m_valid;
655}
656
657template <class verboseT>
659{
660 m_appName.clear();
661 m_baseName.clear();
662 m_extension.clear();
663 m_baseName.clear();
664 m_fullName.clear();
665
666 m_subDir.invalidate();
667
668 m_valid = false;
669}
670
671#ifndef XWCTEST_NAMESPACE
672extern template class stdFileName<XWC_DEFAULT_VERBOSITY>;
673#endif
674
675/// Sort predicate for stdFileNames
676/** Sorting is on 'fullName()'
677 */
678template <class stdFileNameT>
680{
681 /// Comparison operator.
682 /** \returns true if a < b
683 * \returns false otherwise
684 */
685 bool operator()( const stdFileNameT &a, const stdFileNameT &b ) const
686 {
687 return ( a.baseName() < b.baseName() );
688 }
689};
690
691#ifdef XWCTEST_NAMESPACE
692}
693#endif
694
695} // namespace file
696} // namespace MagAOX
697
698#endif // file_stdFileName_hpp
#define XWCTEST_NAMESPACE
Organize and analyze the name of a standard file name.
std::string m_baseName
The base name of the file, not including path.
stdSubDir< verboseT > m_subDir
The subdirectory of the file.
void invalidate()
Set all stored values to invalid values.
std::string m_extension
The extension of the file.
std::string m_appName
The name of the application which wrote the file.
std::string m_fullName
The full name of the file, including path.
Manage a standard subdirectory.
Definition stdSubDir.hpp:36
Augments an exception with the source file and line.
go_lp b(m_lp.m_c)
go_lp a(m_lp.m_c)
Utilities for working with file timestamps.
Flatlogs single include file.
mx::error_t timestamp(std::string &tstamp, const tm &uttime, long ts_nsec)
Get the filename timestamp from the breakdown for a time.
Definition fileTimes.hpp:44
mx::error_t parseFilePath(std::string &devName, std::string &YYYY, std::string &MM, std::string &DD, std::string &hh, std::string &mm, std::string &ss, std::string &nn, const std::string &fname)
Parse a standard XWCTk timestamp filepath.
Definition dm.hpp:28
The stdSubDir class for managing subdirectories.
Sort predicate for stdFileNames.
bool operator()(const stdFileNameT &a, const stdFileNameT &b) const
Comparison operator.
A fixed-width timespec structure.
Definition timespecX.hpp:35