Line data Source code
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 :
23 : namespace MagAOX
24 : {
25 : namespace file
26 : {
27 :
28 : #ifdef XWCTEST_NAMESPACE
29 : namespace XWCTEST_NAMESPACE
30 : {
31 : #endif
32 :
33 : /// Organize and analyze the name of a standard file name
34 : template <typename verboseT = XWC_DEFAULT_VERBOSITY>
35 : class stdFileName
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
59 : stdFileName();
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 :
202 : template <class verboseT>
203 58 : stdFileName<verboseT>::stdFileName()
204 : {
205 58 : return;
206 0 : }
207 :
208 : template <class verboseT>
209 52 : stdFileName<verboseT>::stdFileName( const std::string &fn )
210 : {
211 : try
212 : {
213 52 : mx::error_t errc = fullName( fn );
214 50 : if( errc != mx::error_t::noerror )
215 : {
216 6 : mx::error_report( errc, "from fullName" );
217 : }
218 : }
219 4 : catch( ... )
220 : {
221 8 : std::throw_with_nested( xwcException( "from fullName" ) );
222 : }
223 60 : }
224 :
225 : template <class verboseT>
226 3 : stdFileName<verboseT> &stdFileName<verboseT>::operator=( const std::string &fn )
227 : {
228 : try
229 : {
230 3 : mx::error_t errc = fullName( fn );
231 :
232 2 : if( errc != mx::error_t::noerror )
233 : {
234 2 : mx::error_report<verboseT>( errc, "from fullName" );
235 : }
236 :
237 2 : return *this;
238 : }
239 2 : catch( ... ) // will be bad_alloc
240 : {
241 4 : std::throw_with_nested( xwcException( "from fullName" ) );
242 : }
243 : }
244 :
245 : template <class verboseT>
246 107 : mx::error_t stdFileName<verboseT>::fullName( const std::string &fn )
247 : {
248 : // assume it's false beginning at any modification
249 107 : 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 104 : m_fullName = fn;
264 : }
265 4 : catch( const std::bad_alloc &e )
266 : {
267 4 : std::throw_with_nested( xwcException( "from std::string" ) );
268 : }
269 4 : catch( const std::exception &e )
270 : {
271 4 : 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 97 : std::filesystem::path p( m_fullName );
291 :
292 97 : m_baseName = p.filename();
293 97 : m_extension = p.extension();
294 97 : }
295 9 : catch( const std::bad_alloc &e )
296 : {
297 8 : std::throw_with_nested( xwcException( "extracting basename and extension" ) );
298 : }
299 4 : catch( const std::filesystem::filesystem_error &e )
300 : {
301 4 : return mx::error_report<verboseT>( mx::error_t::std_filesystem_error,
302 4 : "extracting basename and extension " + m_fullName );
303 : }
304 6 : catch( const std::exception &e )
305 : {
306 6 : return mx::error_report<verboseT>( mx::error_t::std_exception,
307 6 : "extracting basename and extension from " + m_fullName );
308 : }
309 :
310 97 : if( m_extension == "" )
311 : {
312 1 : return mx::error_report<verboseT>( mx::error_t::invalidarg, "No extension found in: " + m_fullName );
313 : }
314 :
315 96 : std::string YYYY, MM, DD, hh, mm, ss, nn;
316 :
317 : try
318 : {
319 96 : mx_error_check( parseFilePath( m_appName, YYYY, MM, DD, hh, mm, ss, nn, m_baseName ) );
320 : }
321 1 : catch( const xwcException &e ) // a bad_alloc
322 : {
323 0 : std::throw_with_nested( xwcException( "parsing filename" ) );
324 : }
325 :
326 : mx::error_t errc;
327 94 : int year = mx::ioutils::stoT<int>( YYYY, &errc );
328 94 : mx_error_check_code( errc );
329 :
330 93 : unsigned int month = mx::ioutils::stoT<unsigned int>( MM, &errc );
331 93 : mx_error_check_code( errc );
332 :
333 92 : unsigned int day = mx::ioutils::stoT<unsigned int>( DD, &errc );
334 92 : mx_error_check_code( errc );
335 :
336 90 : m_hour = mx::ioutils::stoT<int>( hh, &errc );
337 90 : mx_error_check_code( errc );
338 :
339 89 : m_minute = mx::ioutils::stoT<int>( mm, &errc );
340 89 : mx_error_check_code( errc );
341 :
342 88 : m_second = mx::ioutils::stoT<int>( ss, &errc );
343 88 : mx_error_check_code( errc );
344 :
345 87 : m_nsec = mx::ioutils::stoT<int>( nn, &errc );
346 87 : mx_error_check_code( errc );
347 :
348 : try
349 : {
350 86 : mx_error_check( m_subDir.ymd( year, month, day ) );
351 : }
352 2 : catch( ... )
353 : {
354 4 : std::throw_with_nested( xwcException( "from stdSubDir::ymd" ) );
355 : }
356 :
357 : tm tmst;
358 84 : tmst.tm_year = year - 1900;
359 84 : tmst.tm_mon = month - 1;
360 84 : tmst.tm_mday = day;
361 84 : tmst.tm_hour = m_hour;
362 84 : tmst.tm_min = m_minute;
363 84 : tmst.tm_sec = m_second;
364 :
365 84 : errno = 0;
366 84 : 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 84 : if( tgm == static_cast<time_t>( -1 ) )
379 : {
380 2 : if( errno != 0 )
381 : {
382 2 : return mx::error_report<verboseT>( mx::errno2error_t( errno ), "error from timegm" );
383 : }
384 :
385 2 : return mx::error_report<verboseT>( mx::error_t::error, "error from timegm" );
386 : }
387 :
388 82 : m_timestamp.time_s = tgm;
389 82 : m_timestamp.time_ns = m_nsec;
390 :
391 82 : m_valid = true;
392 :
393 82 : return mx::error_t::noerror;
394 108 : }
395 :
396 : template <class verboseT>
397 24 : const std::string &stdFileName<verboseT>::fullName( mx::error_t *errc ) const
398 : {
399 24 : if( !m_valid )
400 : {
401 1 : if( errc )
402 : {
403 1 : *errc = mx::error_t::invalidconfig;
404 : }
405 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
406 : }
407 23 : else if( errc )
408 : {
409 1 : *errc = mx::error_t::noerror;
410 : }
411 :
412 24 : return m_fullName;
413 : }
414 :
415 : template <class verboseT>
416 142 : const std::string &stdFileName<verboseT>::baseName( mx::error_t *errc ) const
417 : {
418 142 : if( !m_valid )
419 : {
420 1 : if( errc )
421 : {
422 1 : *errc = mx::error_t::invalidconfig;
423 : }
424 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
425 : }
426 141 : else if( errc )
427 : {
428 1 : *errc = mx::error_t::noerror;
429 : }
430 :
431 142 : return m_baseName;
432 : }
433 :
434 : template <class verboseT>
435 4 : const std::string &stdFileName<verboseT>::extension( mx::error_t *errc ) const
436 : {
437 4 : if( !m_valid )
438 : {
439 1 : if( errc )
440 : {
441 1 : *errc = mx::error_t::invalidconfig;
442 : }
443 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
444 : }
445 3 : else if( errc )
446 : {
447 1 : *errc = mx::error_t::noerror;
448 : }
449 :
450 4 : return m_extension;
451 : }
452 :
453 : template <class verboseT>
454 25 : const std::string &stdFileName<verboseT>::appName( mx::error_t *errc ) const
455 : {
456 25 : if( !m_valid )
457 : {
458 1 : if( errc )
459 : {
460 1 : *errc = mx::error_t::invalidconfig;
461 : }
462 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
463 : }
464 24 : else if( errc )
465 : {
466 1 : *errc = mx::error_t::noerror;
467 : }
468 :
469 25 : return m_appName;
470 : }
471 :
472 : template <class verboseT>
473 18 : const stdSubDir<verboseT> &stdFileName<verboseT>::subDir( mx::error_t *errc ) const
474 : {
475 18 : if( !m_valid )
476 : {
477 1 : if( errc )
478 : {
479 1 : *errc = mx::error_t::invalidconfig;
480 : }
481 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
482 : }
483 17 : else if( errc )
484 : {
485 15 : *errc = mx::error_t::noerror;
486 : }
487 :
488 18 : return m_subDir;
489 : }
490 :
491 : template <class verboseT>
492 4 : int stdFileName<verboseT>::year( mx::error_t *errc ) const
493 : {
494 4 : if( !m_valid )
495 : {
496 1 : if( errc )
497 : {
498 1 : *errc = mx::error_t::invalidconfig;
499 : }
500 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
501 1 : return std::numeric_limits<int>::max();
502 : }
503 3 : else if( errc )
504 : {
505 1 : *errc = mx::error_t::noerror;
506 : }
507 :
508 3 : return m_subDir.year();
509 : }
510 :
511 : template <class verboseT>
512 4 : unsigned int stdFileName<verboseT>::month( mx::error_t *errc ) const
513 : {
514 4 : if( !m_valid )
515 : {
516 1 : if( errc )
517 : {
518 1 : *errc = mx::error_t::invalidconfig;
519 : }
520 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
521 1 : return std::numeric_limits<unsigned int>::max();
522 : }
523 3 : else if( errc )
524 : {
525 1 : *errc = mx::error_t::noerror;
526 : }
527 :
528 3 : return m_subDir.month();
529 : }
530 :
531 : template <class verboseT>
532 4 : unsigned int stdFileName<verboseT>::day( mx::error_t *errc ) const
533 : {
534 4 : if( !m_valid )
535 : {
536 1 : if( errc )
537 : {
538 1 : *errc = mx::error_t::invalidconfig;
539 : }
540 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
541 1 : return std::numeric_limits<unsigned int>::max();
542 : }
543 3 : else if( errc )
544 : {
545 1 : *errc = mx::error_t::noerror;
546 : }
547 :
548 3 : return m_subDir.day();
549 : }
550 :
551 : template <class verboseT>
552 4 : int stdFileName<verboseT>::hour( mx::error_t *errc ) const
553 : {
554 4 : if( !m_valid )
555 : {
556 1 : if( errc )
557 : {
558 1 : *errc = mx::error_t::invalidconfig;
559 : }
560 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
561 1 : return std::numeric_limits<int>::max();
562 : }
563 3 : else if( errc )
564 : {
565 1 : *errc = mx::error_t::noerror;
566 : }
567 :
568 3 : return m_hour;
569 : }
570 :
571 : template <class verboseT>
572 4 : int stdFileName<verboseT>::minute( mx::error_t *errc ) const
573 : {
574 4 : if( !m_valid )
575 : {
576 1 : if( errc )
577 : {
578 1 : *errc = mx::error_t::invalidconfig;
579 : }
580 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
581 1 : return std::numeric_limits<int>::max();
582 : }
583 3 : else if( errc )
584 : {
585 1 : *errc = mx::error_t::noerror;
586 : }
587 :
588 3 : return m_minute;
589 : }
590 :
591 : template <class verboseT>
592 4 : int stdFileName<verboseT>::second( mx::error_t *errc ) const
593 : {
594 4 : if( !m_valid )
595 : {
596 1 : if( errc )
597 : {
598 1 : *errc = mx::error_t::invalidconfig;
599 : }
600 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
601 1 : return std::numeric_limits<int>::max();
602 : }
603 3 : else if( errc )
604 : {
605 1 : *errc = mx::error_t::noerror;
606 : }
607 :
608 3 : return m_second;
609 : }
610 :
611 : template <class verboseT>
612 4 : int stdFileName<verboseT>::nsec( mx::error_t *errc ) const
613 : {
614 4 : if( !m_valid )
615 : {
616 1 : if( errc )
617 : {
618 1 : *errc = mx::error_t::invalidconfig;
619 : }
620 2 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
621 1 : return std::numeric_limits<int>::max();
622 : }
623 3 : else if( errc )
624 : {
625 1 : *errc = mx::error_t::noerror;
626 : }
627 :
628 3 : return m_nsec;
629 : }
630 :
631 : template <class verboseT>
632 56 : flatlogs::timespecX stdFileName<verboseT>::timestamp( mx::error_t *errc ) const
633 : {
634 56 : if( !m_valid )
635 : {
636 3 : if( errc )
637 : {
638 3 : *errc = mx::error_t::invalidconfig;
639 : }
640 6 : mx::error_report<verboseT>( mx::error_t::invalidconfig, "attempt to access while invalid" );
641 3 : return flatlogs::timespecX( 0, 0 );
642 : }
643 53 : else if( errc )
644 : {
645 18 : *errc = mx::error_t::noerror;
646 : }
647 :
648 53 : return m_timestamp;
649 : }
650 :
651 : template <class verboseT>
652 79 : bool stdFileName<verboseT>::valid() const
653 : {
654 79 : return m_valid;
655 : }
656 :
657 : template <class verboseT>
658 107 : void stdFileName<verboseT>::invalidate()
659 : {
660 107 : m_appName.clear();
661 107 : m_baseName.clear();
662 107 : m_extension.clear();
663 107 : m_baseName.clear();
664 107 : m_fullName.clear();
665 :
666 107 : m_subDir.invalidate();
667 :
668 107 : m_valid = false;
669 107 : }
670 :
671 : #ifndef XWCTEST_NAMESPACE
672 : extern template class stdFileName<XWC_DEFAULT_VERBOSITY>;
673 : #endif
674 :
675 : /// Sort predicate for stdFileNames
676 : /** Sorting is on 'fullName()'
677 : */
678 : template <class stdFileNameT>
679 : struct compStdFileName
680 : {
681 : /// Comparison operator.
682 : /** \returns true if a < b
683 : * \returns false otherwise
684 : */
685 69 : bool operator()( const stdFileNameT &a, const stdFileNameT &b ) const
686 : {
687 69 : 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
|