* Implemented new future features: 'lazy' * Implemented new module 'fs' with directory operationsmaster
| @@ -6,6 +6,7 @@ | |||||
| #include "future.pre.h" | #include "future.pre.h" | ||||
| #include "future/map.h" | #include "future/map.h" | ||||
| #include "future/lazy.h" | |||||
| #include "future/and_then.h" | #include "future/and_then.h" | ||||
| namespace asyncpp | namespace asyncpp | ||||
| @@ -3,6 +3,7 @@ | |||||
| #include "future.h" | #include "future.h" | ||||
| #include "future/map.inl" | #include "future/map.inl" | ||||
| #include "future/lazy.inl" | |||||
| #include "future/and_then.inl" | #include "future/and_then.inl" | ||||
| #ifdef asyncpp_timing | #ifdef asyncpp_timing | ||||
| @@ -27,10 +27,10 @@ namespace __future { | |||||
| using base_future_type = base_future<value_type, this_type>; | using base_future_type = base_future<value_type, this_type>; | ||||
| using result_type = typename base_future_type::result_type; | using result_type = typename base_future_type::result_type; | ||||
| public: | |||||
| lambda_type lambda; | |||||
| first_future_type first; | |||||
| second_future_type_ptr second; | |||||
| private: | |||||
| lambda_type _lambda; | |||||
| first_future_type _first; | |||||
| second_future_type_ptr _second; | |||||
| public: | public: | ||||
| /** | /** | ||||
| @@ -17,8 +17,8 @@ namespace __future { | |||||
| ::and_then_impl( | ::and_then_impl( | ||||
| X_future&& p_first, | X_future&& p_first, | ||||
| X_lambda&& p_lambda) | X_lambda&& p_lambda) | ||||
| : first (std::forward<X_future>(p_first)) | |||||
| , lambda(std::forward<X_lambda>(p_lambda)) | |||||
| : _first (std::forward<X_future>(p_first)) | |||||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||||
| { } | { } | ||||
| template< | template< | ||||
| @@ -30,17 +30,17 @@ namespace __future { | |||||
| { | { | ||||
| while (true) | while (true) | ||||
| { | { | ||||
| if (second) | |||||
| if (_second) | |||||
| { | { | ||||
| return second->poll(); | |||||
| return _second->poll(); | |||||
| } | } | ||||
| else | else | ||||
| { | { | ||||
| auto r = first.poll(); | |||||
| auto r = _first.poll(); | |||||
| if (!r) | if (!r) | ||||
| return result_type::not_ready(); | return result_type::not_ready(); | ||||
| second = std::make_unique<second_future_type>(r.call(lambda)); | |||||
| _second = std::make_unique<second_future_type>(r.call(_lambda)); | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,64 @@ | |||||
| #pragma once | |||||
| #include "../future.pre.h" | |||||
| namespace asyncpp | |||||
| { | |||||
| namespace __future | |||||
| { | |||||
| template<typename T_lambda> | |||||
| struct lazy_impl final : | |||||
| public base_future< | |||||
| decltype(std::declval<T_lambda>()()), | |||||
| lazy_impl<T_lambda> | |||||
| > | |||||
| { | |||||
| public: | |||||
| using lambda_type = T_lambda; | |||||
| using value_type = decltype(std::declval<lambda_type>()()); | |||||
| using this_type = lazy_impl<lambda_type>; | |||||
| using base_future_type = base_future<value_type, this_type>; | |||||
| using result_type = typename base_future_type::result_type; | |||||
| private: | |||||
| lambda_type _lambda; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| template<typename X_lambda> | |||||
| inline lazy_impl( | |||||
| X_lambda&& p_lambda); | |||||
| public: /* future */ | |||||
| /** | |||||
| * @brief Poll the result from the future. | |||||
| */ | |||||
| template<typename X = value_type> | |||||
| inline auto poll() | |||||
| -> std::enable_if_t< | |||||
| std::is_void_v<X>, | |||||
| result_type>; | |||||
| /** | |||||
| * @brief Poll the result from the future. | |||||
| */ | |||||
| template<typename X = value_type> | |||||
| inline auto poll() | |||||
| -> std::enable_if_t< | |||||
| !std::is_void_v<X>, | |||||
| result_type>; | |||||
| }; | |||||
| } | |||||
| /** | |||||
| * @brief Create a lazy evaluated future. | |||||
| */ | |||||
| template<typename X_lambda> | |||||
| inline auto lazy(X_lambda&& p_lambda); | |||||
| } | |||||
| @@ -0,0 +1,53 @@ | |||||
| #pragma once | |||||
| #include "lazy.h" | |||||
| namespace asyncpp | |||||
| { | |||||
| namespace __future | |||||
| { | |||||
| /* lazy_impl */ | |||||
| template<typename T_lambda> | |||||
| template<typename X_lambda> | |||||
| lazy_impl<T_lambda>::lazy_impl( | |||||
| X_lambda&& p_lambda) | |||||
| : _lambda(std::forward<X_lambda>(p_lambda)) | |||||
| { } | |||||
| template<typename T_lambda> | |||||
| template<typename X> | |||||
| inline auto lazy_impl<T_lambda> | |||||
| ::poll() | |||||
| -> std::enable_if_t< | |||||
| std::is_void_v<X>, | |||||
| result_type> | |||||
| { | |||||
| _lambda(); | |||||
| return result_type::ready(); | |||||
| } | |||||
| template<typename T_lambda> | |||||
| template<typename X> | |||||
| inline auto lazy_impl<T_lambda> | |||||
| ::poll() | |||||
| -> std::enable_if_t< | |||||
| !std::is_void_v<X>, | |||||
| result_type> | |||||
| { | |||||
| return result_type::ready(_lambda()); | |||||
| } | |||||
| } | |||||
| template<typename X_lambda> | |||||
| inline auto lazy(X_lambda&& p_lambda) | |||||
| { | |||||
| using lambda_type = X_lambda; | |||||
| using lazy_type = __future::lazy_impl<lambda_type>; | |||||
| return lazy_type(std::forward<X_lambda>(p_lambda)); | |||||
| } | |||||
| } | |||||
| @@ -23,9 +23,9 @@ namespace __future { | |||||
| using base_future_type = base_future<value_type, this_type>; | using base_future_type = base_future<value_type, this_type>; | ||||
| using result_type = typename base_future_type::result_type; | using result_type = typename base_future_type::result_type; | ||||
| public: | |||||
| future_type future; | |||||
| lambda_type lambda; | |||||
| private: | |||||
| future_type _future; | |||||
| lambda_type _lambda; | |||||
| public: | public: | ||||
| /** | /** | ||||
| @@ -17,8 +17,8 @@ namespace __future { | |||||
| ::map_impl( | ::map_impl( | ||||
| X_future&& p_future, | X_future&& p_future, | ||||
| X_lambda&& p_lambda) | X_lambda&& p_lambda) | ||||
| : future(std::forward<X_future>(p_future)) | |||||
| , lambda(std::forward<X_lambda>(p_lambda)) | |||||
| : _future(std::forward<X_future>(p_future)) | |||||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||||
| { } | { } | ||||
| template< | template< | ||||
| @@ -28,9 +28,9 @@ namespace __future { | |||||
| map_impl<T_future, T_lambda> | map_impl<T_future, T_lambda> | ||||
| ::poll() | ::poll() | ||||
| { | { | ||||
| auto r = future.poll(); | |||||
| auto r = _future.poll(); | |||||
| return r | return r | ||||
| ? result_type::ready(r.call(lambda)) | |||||
| ? result_type::ready(r.call(_lambda)) | |||||
| : result_type::not_ready(); | : result_type::not_ready(); | ||||
| } | } | ||||
| @@ -5,8 +5,6 @@ | |||||
| #include "result.h" | #include "result.h" | ||||
| #include "stream.pre.h" | #include "stream.pre.h" | ||||
| #include "stream/for_each.h" | |||||
| namespace asyncpp | namespace asyncpp | ||||
| { | { | ||||
| @@ -47,6 +45,44 @@ namespace asyncpp | |||||
| template<typename X_lambda> | template<typename X_lambda> | ||||
| inline auto for_each(X_lambda&& p_lambda) &&; | inline auto for_each(X_lambda&& p_lambda) &&; | ||||
| public: | |||||
| /** | |||||
| * @brief Transforms the stream from one type to another type | |||||
| * by executing the passed lambda for each value. | |||||
| */ | |||||
| template<typename X_lambda> | |||||
| inline auto map(X_lambda&& p_lambda) const &; | |||||
| /** | |||||
| * @brief Transforms the stream from one type to another type | |||||
| * by executing the passed lambda for each value. | |||||
| */ | |||||
| template<typename X_lambda> | |||||
| inline auto map(X_lambda&& p_lambda) &; | |||||
| /** | |||||
| * @brief Transforms the stream from one type to another type | |||||
| * by executing the passed lambda for each value. | |||||
| */ | |||||
| template<typename X_lambda> | |||||
| inline auto map(X_lambda&& p_lambda) &&; | |||||
| public: | |||||
| /** | |||||
| * @brief Flatten the stream of streams to a single stream. | |||||
| */ | |||||
| inline auto flatten() const &; | |||||
| /** | |||||
| * @brief Flatten the stream of streams to a single stream. | |||||
| */ | |||||
| inline auto flatten() &; | |||||
| /** | |||||
| * @brief Flatten the stream of streams to a single stream. | |||||
| */ | |||||
| inline auto flatten() &&; | |||||
| #ifdef asyncpp_timing | #ifdef asyncpp_timing | ||||
| public: | public: | ||||
| /** | /** | ||||
| @@ -2,6 +2,8 @@ | |||||
| #include "stream.h" | #include "stream.h" | ||||
| #include "stream/map.inl" | |||||
| #include "stream/flatten.inl" | |||||
| #include "stream/for_each.inl" | #include "stream/for_each.inl" | ||||
| #ifdef asyncpp_timing | #ifdef asyncpp_timing | ||||
| @@ -67,6 +69,106 @@ namespace asyncpp | |||||
| std::forward<X_lambda>(p_lambda)); | std::forward<X_lambda>(p_lambda)); | ||||
| } | } | ||||
| /* base_stream::map */ | |||||
| template< | |||||
| typename T_value, | |||||
| typename T_derived> | |||||
| template< | |||||
| typename X_lambda> | |||||
| auto base_stream<T_value, T_derived> | |||||
| ::map(X_lambda&& p_lambda) const & | |||||
| { | |||||
| using lambda_type = X_lambda; | |||||
| using map_type = __stream::map_impl<derived_type, lambda_type>; | |||||
| auto& self = static_cast<derived_type const &>(*this); | |||||
| return map_type( | |||||
| std::forward<derived_type const &>(self), | |||||
| std::forward<X_lambda>(p_lambda)); | |||||
| } | |||||
| template< | |||||
| typename T_value, | |||||
| typename T_derived> | |||||
| template< | |||||
| typename X_lambda> | |||||
| auto base_stream<T_value, T_derived> | |||||
| ::map(X_lambda&& p_lambda) & | |||||
| { | |||||
| using lambda_type = X_lambda; | |||||
| using map_type = __stream::map_impl<derived_type, lambda_type>; | |||||
| auto& self = static_cast<derived_type &>(*this); | |||||
| return map_type( | |||||
| std::forward<derived_type &>(self), | |||||
| std::forward<X_lambda>(p_lambda)); | |||||
| } | |||||
| template< | |||||
| typename T_value, | |||||
| typename T_derived> | |||||
| template< | |||||
| typename X_lambda> | |||||
| auto base_stream<T_value, T_derived> | |||||
| ::map(X_lambda&& p_lambda) && | |||||
| { | |||||
| using lambda_type = X_lambda; | |||||
| using map_type = __stream::map_impl<derived_type, lambda_type>; | |||||
| auto& self = static_cast<derived_type &>(*this); | |||||
| return map_type( | |||||
| std::forward<derived_type &&>(self), | |||||
| std::forward<X_lambda>(p_lambda)); | |||||
| } | |||||
| /* base_stream::flatten */ | |||||
| template< | |||||
| typename T_value, | |||||
| typename T_derived> | |||||
| auto base_stream<T_value, T_derived> | |||||
| ::flatten() const & | |||||
| { | |||||
| using flatten_type = __stream::flatten_impl<derived_type>; | |||||
| auto& self = static_cast<derived_type const &>(*this); | |||||
| return flatten_type( | |||||
| std::forward<derived_type const &>(self)); | |||||
| } | |||||
| template< | |||||
| typename T_value, | |||||
| typename T_derived> | |||||
| auto base_stream<T_value, T_derived> | |||||
| ::flatten() & | |||||
| { | |||||
| using flatten_type = __stream::flatten_impl<derived_type>; | |||||
| auto& self = static_cast<derived_type &>(*this); | |||||
| return flatten_type( | |||||
| std::forward<derived_type &>(self)); | |||||
| } | |||||
| template< | |||||
| typename T_value, | |||||
| typename T_derived> | |||||
| auto base_stream<T_value, T_derived> | |||||
| ::flatten() && | |||||
| { | |||||
| using flatten_type = __stream::flatten_impl<derived_type>; | |||||
| auto& self = static_cast<derived_type &>(*this); | |||||
| return flatten_type( | |||||
| std::forward<derived_type &&>(self)); | |||||
| } | |||||
| /* base_stream::timeout */ | /* base_stream::timeout */ | ||||
| #ifdef asyncpp_timing | #ifdef asyncpp_timing | ||||
| @@ -0,0 +1,47 @@ | |||||
| #pragma once | |||||
| #include <memory> | |||||
| #include <asyncpp/core/stream.pre.h> | |||||
| namespace asyncpp { | |||||
| namespace __stream { | |||||
| template< | |||||
| typename T_stream> | |||||
| struct flatten_impl final : | |||||
| public base_stream< | |||||
| typename T_stream::value_type::value_type, | |||||
| flatten_impl<T_stream> | |||||
| > | |||||
| { | |||||
| public: | |||||
| using stream_type = T_stream; | |||||
| using inner_stream_type = typename stream_type::value_type; | |||||
| using inner_stream_ptr_u = std::unique_ptr<inner_stream_type>; | |||||
| using value_type = typename inner_stream_type::value_type; | |||||
| using this_type = flatten_impl<stream_type>; | |||||
| using base_stream_type = base_stream<value_type, this_type>; | |||||
| using result_type = typename base_stream_type::result_type; | |||||
| private: | |||||
| stream_type _stream; | |||||
| inner_stream_ptr_u _inner; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| template< | |||||
| typename X_stream> | |||||
| inline flatten_impl( | |||||
| X_stream&& p_stream); | |||||
| public: /* future */ | |||||
| /** | |||||
| * @brief Poll the result from the future. | |||||
| */ | |||||
| inline result_type poll(); | |||||
| }; | |||||
| } } | |||||
| @@ -0,0 +1,49 @@ | |||||
| #pragma once | |||||
| #include "flatten.h" | |||||
| namespace asyncpp { | |||||
| namespace __stream { | |||||
| /* flatten_impl */ | |||||
| template< | |||||
| typename T_stream> | |||||
| template< | |||||
| typename X_stream> | |||||
| flatten_impl<T_stream>::flatten_impl( | |||||
| X_stream&& p_stream) | |||||
| : _stream (std::forward<X_stream>(p_stream)) | |||||
| , _inner () | |||||
| { } | |||||
| template< | |||||
| typename T_stream> | |||||
| typename flatten_impl<T_stream>::result_type | |||||
| flatten_impl<T_stream> | |||||
| ::poll() | |||||
| { | |||||
| while (true) | |||||
| { | |||||
| if (_inner) | |||||
| { | |||||
| auto r = _inner->poll(); | |||||
| if (!r.is_done()) | |||||
| return r; | |||||
| _inner.reset(); | |||||
| } | |||||
| auto r = _stream.poll(); | |||||
| if (r.is_done()) | |||||
| return result_type::done(); | |||||
| if (r.is_not_ready()) | |||||
| return result_type::not_ready(); | |||||
| _inner = std::make_unique<inner_stream_type>(std::move(*r)); | |||||
| } | |||||
| } | |||||
| } } | |||||
| @@ -22,9 +22,9 @@ namespace __stream { | |||||
| using base_future_type = base_future<value_type, this_type>; | using base_future_type = base_future<value_type, this_type>; | ||||
| using result_type = typename base_future_type::result_type; | using result_type = typename base_future_type::result_type; | ||||
| public: | |||||
| stream_type stream; | |||||
| lambda_type lambda; | |||||
| private: | |||||
| stream_type _stream; | |||||
| lambda_type _lambda; | |||||
| public: | public: | ||||
| /** | /** | ||||
| @@ -17,8 +17,8 @@ namespace __stream { | |||||
| ::for_each_impl( | ::for_each_impl( | ||||
| X_stream&& p_stream, | X_stream&& p_stream, | ||||
| X_lambda&& p_lambda) | X_lambda&& p_lambda) | ||||
| : stream(std::forward<X_stream>(p_stream)) | |||||
| , lambda(std::forward<X_lambda>(p_lambda)) | |||||
| : _stream(std::forward<X_stream>(p_stream)) | |||||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||||
| { } | { } | ||||
| template< | template< | ||||
| @@ -30,14 +30,14 @@ namespace __stream { | |||||
| { | { | ||||
| while (true) | while (true) | ||||
| { | { | ||||
| auto r = stream.poll(); | |||||
| auto r = _stream.poll(); | |||||
| if (r.is_done()) | if (r.is_done()) | ||||
| return result_type::ready(); | return result_type::ready(); | ||||
| if (r.is_not_ready()) | if (r.is_not_ready()) | ||||
| return result_type::not_ready(); | return result_type::not_ready(); | ||||
| r.call(lambda); | |||||
| r.call(_lambda); | |||||
| } | } | ||||
| } | } | ||||
| @@ -0,0 +1,48 @@ | |||||
| #pragma once | |||||
| #include <asyncpp/core/stream.pre.h> | |||||
| namespace asyncpp { | |||||
| namespace __stream { | |||||
| template< | |||||
| typename T_stream, | |||||
| typename T_lambda> | |||||
| struct map_impl final : | |||||
| public base_stream< | |||||
| decltype(std::declval<T_lambda>()(std::declval<typename T_stream::value_type>())), | |||||
| map_impl<T_stream, T_lambda> | |||||
| > | |||||
| { | |||||
| public: | |||||
| using stream_type = T_stream; | |||||
| using lambda_type = T_lambda; | |||||
| using inner_value_type = typename stream_type::value_type; | |||||
| using value_type = decltype(std::declval<lambda_type>()(std::declval<inner_value_type>())); | |||||
| using this_type = map_impl<stream_type, lambda_type>; | |||||
| using base_stream_type = base_stream<value_type, this_type>; | |||||
| using result_type = typename base_stream_type::result_type; | |||||
| private: | |||||
| stream_type _stream; | |||||
| lambda_type _lambda; | |||||
| public: | |||||
| /** | |||||
| * @brief Constructor. | |||||
| */ | |||||
| template< | |||||
| typename X_stream, | |||||
| typename X_lambda> | |||||
| inline map_impl( | |||||
| X_stream&& p_stream, | |||||
| X_lambda&& p_lambda); | |||||
| public: /* future */ | |||||
| /** | |||||
| * @brief Poll the result from the future. | |||||
| */ | |||||
| inline result_type poll(); | |||||
| }; | |||||
| } } | |||||
| @@ -0,0 +1,40 @@ | |||||
| #pragma once | |||||
| #include "map.h" | |||||
| namespace asyncpp { | |||||
| namespace __stream { | |||||
| /* map_impl */ | |||||
| template< | |||||
| typename T_stream, | |||||
| typename T_lambda> | |||||
| template< | |||||
| typename X_stream, | |||||
| typename X_lambda> | |||||
| map_impl<T_stream, T_lambda>::map_impl( | |||||
| X_stream&& p_stream, | |||||
| X_lambda&& p_lambda) | |||||
| : _stream(std::forward<X_stream>(p_stream)) | |||||
| , _lambda(std::forward<X_lambda>(p_lambda)) | |||||
| { } | |||||
| template< | |||||
| typename T_stream, | |||||
| typename T_lambda> | |||||
| typename map_impl<T_stream, T_lambda>::result_type | |||||
| map_impl<T_stream, T_lambda> | |||||
| ::poll() | |||||
| { | |||||
| auto r = _stream.poll(); | |||||
| if (r.is_done()) | |||||
| return result_type::done(); | |||||
| if (r.is_ready()) | |||||
| return result_type::ready(r.call(_lambda)); | |||||
| return result_type::not_ready(); | |||||
| } | |||||
| } } | |||||
| @@ -0,0 +1,5 @@ | |||||
| #pragma once | |||||
| #include "fs/directory.h" | |||||
| #include "fs/directory.inl" | |||||
| @@ -0,0 +1,43 @@ | |||||
| #pragma once | |||||
| #include <string> | |||||
| #include <cppfs/path.h> | |||||
| #include <asyncpp/core/stream.pre.h> | |||||
| #include <asyncpp/core/future/lazy.h> | |||||
| namespace asyncpp { | |||||
| namespace fs { | |||||
| /** | |||||
| * @brief Create future to read the content of a directory. | |||||
| */ | |||||
| inline auto read_dir(const cppfs::path& p_path); | |||||
| /** | |||||
| * @brief Create future to read the content of a directory including the subdirectories. | |||||
| */ | |||||
| inline auto read_dir_all(const cppfs::path& p_path); | |||||
| /** | |||||
| * @brief Create a new future to create a directory. | |||||
| */ | |||||
| inline auto create_dir(const cppfs::path& p_path); | |||||
| /** | |||||
| * @brief Create a new future to create a directory and all it's parent directories. | |||||
| */ | |||||
| inline auto create_dir_all(const cppfs::path& p_path); | |||||
| /** | |||||
| * @brief Create a new future to remove an empty directory. | |||||
| */ | |||||
| inline auto remove_dir(const cppfs::path& p_path); | |||||
| /** | |||||
| * @brief Create a new future to remove a directory and all it's content. | |||||
| */ | |||||
| inline auto remove_dir_all(const cppfs::path& p_path); | |||||
| } } | |||||
| @@ -0,0 +1,167 @@ | |||||
| #pragma once | |||||
| #include <vector> | |||||
| #include <cppfs/directory.h> | |||||
| #include "directory.h" | |||||
| namespace asyncpp { | |||||
| namespace fs { | |||||
| namespace __impl | |||||
| { | |||||
| struct read_dir_impl | |||||
| : public base_stream< | |||||
| cppfs::directory::entry, | |||||
| read_dir_impl> | |||||
| { | |||||
| public: | |||||
| using value_type = cppfs::directory::entry; | |||||
| using this_type = read_dir_impl; | |||||
| using base_future_type = base_stream<value_type, this_type>; | |||||
| using result_type = typename base_future_type::result_type; | |||||
| private: | |||||
| cppfs::directory::iterator _it; | |||||
| cppfs::directory::iterator _end; | |||||
| public: | |||||
| inline read_dir_impl( | |||||
| const cppfs::path& p_path) | |||||
| { | |||||
| cppfs::directory d(p_path); | |||||
| if (d.exists()) | |||||
| { | |||||
| _it = d.begin(); | |||||
| _end = d.end(); | |||||
| } | |||||
| } | |||||
| public: | |||||
| inline result_type poll() | |||||
| { | |||||
| if (_it == _end) | |||||
| return result_type::done(); | |||||
| auto ret = result_type::ready(*_it); | |||||
| ++_it; | |||||
| return ret; | |||||
| } | |||||
| }; | |||||
| struct read_dir_all_impl | |||||
| : public base_stream< | |||||
| cppfs::directory::entry, | |||||
| read_dir_all_impl> | |||||
| { | |||||
| public: | |||||
| using value_type = cppfs::directory::entry; | |||||
| using this_type = read_dir_impl; | |||||
| using base_future_type = base_stream<value_type, this_type>; | |||||
| using result_type = typename base_future_type::result_type; | |||||
| private: | |||||
| struct entry | |||||
| { | |||||
| cppfs::directory::iterator it; | |||||
| cppfs::directory::iterator end; | |||||
| }; | |||||
| using entry_stack = std::vector<entry>; | |||||
| entry_stack _stack; | |||||
| public: | |||||
| inline read_dir_all_impl( | |||||
| const cppfs::path& p_path) | |||||
| { | |||||
| cppfs::directory d(p_path); | |||||
| if (d.exists()) | |||||
| { | |||||
| _stack.emplace_back(entry { | |||||
| d.begin(), | |||||
| d.end() | |||||
| }); | |||||
| } | |||||
| } | |||||
| public: | |||||
| inline result_type poll() | |||||
| { | |||||
| while (true) | |||||
| { | |||||
| if (_stack.empty()) | |||||
| return result_type::done(); | |||||
| auto& e = _stack.back(); | |||||
| if (e.it == e.end) | |||||
| { | |||||
| _stack.pop_back(); | |||||
| continue; | |||||
| } | |||||
| auto ret = result_type::ready(std::move(*e.it)); | |||||
| cppfs::directory d(e.it->path); | |||||
| ++e.it; | |||||
| if (d.exists()) | |||||
| { | |||||
| _stack.emplace_back(entry { | |||||
| d.begin(), | |||||
| d.end(), | |||||
| }); | |||||
| } | |||||
| return ret; | |||||
| } | |||||
| } | |||||
| }; | |||||
| } | |||||
| auto read_dir(const cppfs::path& p_path) | |||||
| { | |||||
| return __impl::read_dir_impl(p_path); | |||||
| } | |||||
| auto read_dir_all(const cppfs::path& p_path) | |||||
| { | |||||
| return __impl::read_dir_all_impl(p_path); | |||||
| } | |||||
| auto create_dir(const cppfs::path& p_path) | |||||
| { | |||||
| return lazy([d = cppfs::directory(p_path)]{ | |||||
| if (!d.exists()) | |||||
| d.create(false); | |||||
| }); | |||||
| } | |||||
| auto create_dir_all(const cppfs::path& p_path) | |||||
| { | |||||
| return lazy([d = cppfs::directory(p_path)]{ | |||||
| if (!d.exists()) | |||||
| d.create(true); | |||||
| }); | |||||
| } | |||||
| auto remove_dir(const cppfs::path& p_path) | |||||
| { | |||||
| return lazy([d = cppfs::directory(p_path)]{ | |||||
| if (d.exists()) | |||||
| d.remove(false); | |||||
| }); | |||||
| } | |||||
| auto remove_dir_all(const cppfs::path& p_path) | |||||
| { | |||||
| return lazy([d = cppfs::directory(p_path)]{ | |||||
| if (d.exists()) | |||||
| d.remove(true); | |||||
| }); | |||||
| } | |||||
| } } | |||||
| @@ -4,6 +4,7 @@ Include ( cotire OPTIONAL RESULT_VARIABLE HAS | |||||
| Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | Include ( pedantic OPTIONAL RESULT_VARIABLE HAS_PEDANTIC ) | ||||
| Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | Include ( strip_symbols OPTIONAL RESULT_VARIABLE HAS_STRIP_SYMBOLS ) | ||||
| Find_Package ( cppfs REQUIRED ) | |||||
| Find_Package ( cppcore REQUIRED ) | Find_Package ( cppcore REQUIRED ) | ||||
| # Interface Library ############################################################################### | # Interface Library ############################################################################### | ||||
| @@ -16,6 +17,7 @@ Target_Include_Directories ( asyncpp | |||||
| $<INSTALL_INTERFACE:${ASYNCPP_INSTALL_DIR_INCLUDE}> ) | $<INSTALL_INTERFACE:${ASYNCPP_INSTALL_DIR_INCLUDE}> ) | ||||
| Target_Link_Libraries ( asyncpp | Target_Link_Libraries ( asyncpp | ||||
| INTERFACE | INTERFACE | ||||
| cppfs::cppfs | |||||
| cppcore::cppcore ) | cppcore::cppcore ) | ||||
| # Install ######################################################################################### | # Install ######################################################################################### | ||||
| @@ -72,5 +72,21 @@ TEST(future_tests, and_then) | |||||
| auto r = f.poll(); | auto r = f.poll(); | ||||
| ASSERT_TRUE(r); | ASSERT_TRUE(r); | ||||
| ASSERT_EQ (5, *r); | ASSERT_EQ (5, *r); | ||||
| ASSERT_EQ (2, f.first._count); | |||||
| } | |||||
| TEST(future_tests, lazy) | |||||
| { | |||||
| int i = 1; | |||||
| auto f = lazy([&]{ | |||||
| ++i; | |||||
| return i + 1; | |||||
| }); | |||||
| ASSERT_EQ(i, 1); | |||||
| auto r = f.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (2, i); | |||||
| ASSERT_EQ (3, *r); | |||||
| } | } | ||||
| @@ -73,3 +73,72 @@ TEST(stream_tests, for_each) | |||||
| auto r = f.poll(); | auto r = f.poll(); | ||||
| ASSERT_TRUE(r); | ASSERT_TRUE(r); | ||||
| } | } | ||||
| TEST(stream_tests, map) | |||||
| { | |||||
| delay d { 5, 0, 0 }; | |||||
| auto s = d | |||||
| .map([](int i){ | |||||
| return std::to_string(i); | |||||
| }); | |||||
| auto r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, "1"); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, "2"); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, "3"); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, "4"); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, "5"); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r.is_done()); | |||||
| } | |||||
| TEST(stream_tests, flatten) | |||||
| { | |||||
| delay d { 3, 0, 0 }; | |||||
| auto s = d | |||||
| .map([](int i){ | |||||
| return delay { i * 100 + 2, 0, i * 100 }; | |||||
| }) | |||||
| .flatten(); | |||||
| auto r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, 101); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, 102); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, 201); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, 202); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, 301); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r); | |||||
| ASSERT_EQ (*r, 302); | |||||
| r = s.poll(); | |||||
| ASSERT_TRUE(r.is_done()); | |||||
| } | |||||
| @@ -0,0 +1,210 @@ | |||||
| #include <gtest/gtest.h> | |||||
| #include <asyncpp.h> | |||||
| #include <asyncpp/fs.h> | |||||
| using namespace ::testing; | |||||
| using namespace ::asyncpp; | |||||
| struct auto_remove_dir | |||||
| : public cppfs::directory | |||||
| { | |||||
| public: | |||||
| using cppfs::directory::directory; | |||||
| inline ~auto_remove_dir() | |||||
| { | |||||
| if (exists()) | |||||
| remove(true); | |||||
| } | |||||
| }; | |||||
| class directory_tests | |||||
| : public ::testing::Test | |||||
| { | |||||
| protected: | |||||
| static const std::string& path_test_dir() | |||||
| { | |||||
| static const std::string value("/tmp/asyncpp/test"); | |||||
| return value; | |||||
| } | |||||
| static const std::string& path_test_dir_single() | |||||
| { | |||||
| static const std::string value("/tmp/asyncpp/test/single"); | |||||
| return value; | |||||
| } | |||||
| static const std::string& path_test_dir_parent() | |||||
| { | |||||
| static const std::string value("/tmp/asyncpp/test/parent"); | |||||
| return value; | |||||
| } | |||||
| static const std::string& path_test_dir_child() | |||||
| { | |||||
| static const std::string value("/tmp/asyncpp/test/parent/child"); | |||||
| return value; | |||||
| } | |||||
| void SetUp() override | |||||
| { | |||||
| cppfs::directory d(path_test_dir()); | |||||
| if (!d.exists()) | |||||
| d.create(true); | |||||
| } | |||||
| void TearDown() override | |||||
| { | |||||
| cppfs::directory d(path_test_dir()); | |||||
| if (d.exists()) | |||||
| d.remove(true); | |||||
| } | |||||
| }; | |||||
| TEST_F(directory_tests, read_dir) | |||||
| { | |||||
| using namespace cppfs; | |||||
| using namespace std::string_literals; | |||||
| auto s = fs::read_dir("."s); | |||||
| auto v = std::vector<std::string>(); | |||||
| auto r = decltype(s)::result_type::not_ready(); | |||||
| while ((r = s.poll()).is_ready()) | |||||
| v.emplace_back(r->path.parts().back()); | |||||
| ASSERT_TRUE(r.is_done()); | |||||
| std::sort(v.begin(), v.end(), cppcore::op_invariant_string_less { }); | |||||
| EXPECT_EQ(v, std::vector<std::string>({ | |||||
| "asyncpp", | |||||
| "CMakeLists.txt", | |||||
| "helper", | |||||
| })); | |||||
| } | |||||
| TEST_F(directory_tests, read_dir_all) | |||||
| { | |||||
| using namespace cppfs; | |||||
| using namespace std::string_literals; | |||||
| auto s = fs::read_dir_all("."s); | |||||
| auto r = decltype(s)::result_type::not_ready(); | |||||
| auto entries = std::vector<cppfs::directory::entry>(); | |||||
| while ((r = s.poll()).is_ready()) | |||||
| entries.emplace_back(*r); | |||||
| ASSERT_TRUE(r.is_done()); | |||||
| std::sort(entries.begin(), entries.end()); | |||||
| std::vector<std::string> paths; | |||||
| paths.reserve(entries.size()); | |||||
| std::transform( | |||||
| entries.begin(), | |||||
| entries.end(), | |||||
| std::back_inserter(paths), | |||||
| [](auto& e) { | |||||
| auto s = std::to_string(static_cast<int>(e.type)) + e.path.str(); | |||||
| return s; | |||||
| }); | |||||
| EXPECT_EQ(paths, std::vector<std::string>({ | |||||
| "1./asyncpp", | |||||
| "1./asyncpp/core", | |||||
| "2./asyncpp/core/future_tests.cpp", | |||||
| "2./asyncpp/core/result_tests.cpp", | |||||
| "2./asyncpp/core/stream_tests.cpp", | |||||
| "2./asyncpp/core/task_tests.cpp", | |||||
| "1./asyncpp/executor", | |||||
| "2./asyncpp/executor/current_thread_tests.cpp", | |||||
| "1./asyncpp/fs", | |||||
| "2./asyncpp/fs/directory_tests.cpp", | |||||
| "1./asyncpp/timing", | |||||
| "2./asyncpp/timing/delay_tests.cpp", | |||||
| "2./asyncpp/timing/interval_tests.cpp", | |||||
| "2./asyncpp/timing/timeout_tests.cpp", | |||||
| "2./asyncpp/timing/timer_tests.cpp", | |||||
| "1./helper", | |||||
| "2./helper/now_mock.h", | |||||
| "2./helper/runtime_mock.h", | |||||
| "2./helper/test_delay.h", | |||||
| "2./CMakeLists.txt", | |||||
| })); | |||||
| } | |||||
| TEST_F(directory_tests, create_dir) | |||||
| { | |||||
| auto f = | |||||
| lazy([path=path_test_dir_single()]{ | |||||
| return path; | |||||
| }) | |||||
| .and_then(fs::create_dir); | |||||
| auto_remove_dir d(path_test_dir_single()); | |||||
| ASSERT_FALSE(d.exists()); | |||||
| auto r = f.poll(); | |||||
| ASSERT_TRUE (r); | |||||
| ASSERT_TRUE (d.exists()); | |||||
| } | |||||
| TEST_F(directory_tests, create_dir_all) | |||||
| { | |||||
| auto f = | |||||
| lazy([path=path_test_dir_child()]{ | |||||
| return path; | |||||
| }) | |||||
| .and_then(fs::create_dir_all); | |||||
| auto_remove_dir d(path_test_dir_child()); | |||||
| ASSERT_FALSE(d.exists()); | |||||
| auto r = f.poll(); | |||||
| ASSERT_TRUE (r); | |||||
| ASSERT_TRUE (d.exists()); | |||||
| } | |||||
| TEST_F(directory_tests, remove_dir) | |||||
| { | |||||
| auto f = | |||||
| lazy([path=path_test_dir_single()]{ | |||||
| return path; | |||||
| }) | |||||
| .and_then(fs::remove_dir); | |||||
| auto_remove_dir d(path_test_dir_single()); | |||||
| d.create(true); | |||||
| ASSERT_TRUE (d.exists()); | |||||
| auto r = f.poll(); | |||||
| ASSERT_TRUE (r); | |||||
| ASSERT_FALSE(d.exists()); | |||||
| } | |||||
| TEST_F(directory_tests, remove_dir_all) | |||||
| { | |||||
| auto f = | |||||
| lazy([path=path_test_dir_parent()]{ | |||||
| return path; | |||||
| }) | |||||
| .and_then(fs::remove_dir_all); | |||||
| auto_remove_dir d(path_test_dir_child()); | |||||
| d.create(true); | |||||
| ASSERT_TRUE (d.exists()); | |||||
| auto r = f.poll(); | |||||
| ASSERT_TRUE (r); | |||||
| ASSERT_FALSE(d.exists()); | |||||
| } | |||||