<commit_before>#include "CppUnitTest.h"
#include <algorithm>
#include <sstream>
#include <cctype>
#include <string>
#include <vector>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace std;

namespace algo
{
    struct token
    {
        enum toktype { root_node, named_node, edge_name, horizontal_edge, ascending_edge_part, descending_edge_part, vertical_edge_part };
        toktype type;
        string name;
    };

    bool operator==(const token& lhs, const token& rhs)
    {
        return lhs.type == rhs.type
            && lhs.name == rhs.name;
    }

    struct ascii_tree
    {
        static vector<token> tokenize(const string& s)
        {
            vector<token> tokens;
            enum { none, open_square_brace, close_square_brace, asterisk, dash, name_char } prev = none;
            size_t marker = 0, marked_length = 0;

            for (size_t i = 0; i < s.size(); ++i)
            {
                auto ch = s[i];

                if (ch == '[')
                {
                    prev = open_square_brace;
                }
                else if (ch == ']')
                {
                    if (prev == asterisk)
                    {
                        tokens.emplace_back(token { token::root_node, "" });
                    }
                    else
                    {
                        tokens.emplace_back(token { token::named_node, s.substr(marker, marked_length) });
                    }

                    prev = close_square_brace;
                }
                else if (ch == '*')
                {
                    prev = asterisk;
                }
                else if (ch == '-')
                {
                    if (prev == name_char)
                    {
                        tokens.emplace_back(token { token::horizontal_edge, s.substr(marker, marked_length) });
                    }

                    prev = dash;
                }
                else if (ch == '/')
                {
                    tokens.emplace_back(token { token::ascending_edge_part, "" });
                }
                else if (ch == '\\')
                {
                    tokens.emplace_back(token { token::descending_edge_part, "" });
                }
                else if (ch == '|')
                {
                    tokens.emplace_back(token { token::vertical_edge_part, "" });
                }
                else if (isalnum(ch) || ch == '_')
                {
                    if (prev != name_char)
                    {
                        marker = i;
                        marked_length = 0;
                    }

                    prev = name_char;
                    ++marked_length;
                }
            }

            if (prev == name_char)
            {
                tokens.emplace_back(token { token::edge_name, s.substr(marker, marked_length) });
            }

            return tokens;
        }
    };
}

namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework
{
    template<>
    wstring ToString<algo::token::toktype>(const algo::token::toktype& type)
    {
        switch (type)
        {
        case algo::token::root_node:
            return L"root_node";
        case algo::token::named_node:
            return L"named_node";
        case algo::token::edge_name:
            return L"edge_name";
        case algo::token::horizontal_edge:
            return L"horizontal_edge";
        case algo::token::ascending_edge_part:
            return L"ascending_edge_part";
        case algo::token::descending_edge_part:
            return L"descending_edge_part";
        case algo::token::vertical_edge_part:
            return L"vertical_edge_part";
        default:
            return L"unknown token";
        }
    }
}}}

namespace algo { namespace spec
{
    TEST_CLASS(can_recognize_ascii_tree_tokens)
    {
        void tokens_should_match_(std::initializer_list<token> expected, vector<token>& actual)
        {
            auto mismatch_pair = std::mismatch(expected.begin(), expected.end(), actual.begin());

            if (mismatch_pair.first != expected.end() || mismatch_pair.second != actual.end())
            {
                wstring expectedName(mismatch_pair.first->name.begin(), mismatch_pair.first->name.end());
                wstring actualName(mismatch_pair.second->name.begin(), mismatch_pair.second->name.end());

                wstring message = L"Expected: " + ToString(mismatch_pair.first->type) + L" \"" + expectedName + L"\" "
                    + L"Actual: " + ToString(mismatch_pair.second->type) + L" \"" + actualName + L"\"";

                Assert::Fail(message.c_str());
            }
        }

    public:

        TEST_METHOD(should_not_recognize_any_tokens_in_an_empty_string)
        {
            auto tokens = ascii_tree::tokenize("");
            Assert::IsTrue(tokens.empty());
        }

        TEST_METHOD(should_recognize_a_root_node)
        {
            auto tokens = ascii_tree::tokenize("[*]");
            Assert::AreEqual(token::root_node, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_root_node_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" [ * ]");
            Assert::AreEqual(token::root_node, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_named_node)
        {
            const string all_chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            auto tokens = ascii_tree::tokenize("[" + all_chars + "]");
            Assert::AreEqual(token::named_node, tokens.front().type);
            Assert::AreEqual(all_chars.c_str(), tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_a_named_node_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" [ a ]");
            Assert::AreEqual(token::named_node, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("a");
            Assert::AreEqual(token::edge_name, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_edge_name_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" a ");
            Assert::AreEqual(token::edge_name, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_ascending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("/");
            Assert::AreEqual(token::ascending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_an_ascending_edge_part_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" / ");
            Assert::AreEqual(token::ascending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_descending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("\\");
            Assert::AreEqual(token::descending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_descending_edge_part_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" \\ ");
            Assert::AreEqual(token::descending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_vertical_edge_part)
        {
            auto tokens = ascii_tree::tokenize("|");
            Assert::AreEqual(token::vertical_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_vertical_edge_part_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" | ");
            Assert::AreEqual(token::vertical_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("-a-");
            Assert::AreEqual(token::horizontal_edge, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_a_horizontal_edge_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" - a - ");
            Assert::AreEqual(token::horizontal_edge, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("[*][a]");
            tokens_should_match_({ { token::root_node, "" }, { token::named_node, "a" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("[*]-a-");
            tokens_should_match_({ { token::root_node, "" }, { token::horizontal_edge, "a" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_horizontal_edge_next_to_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("-a-[b]");
            tokens_should_match_({ { token::horizontal_edge, "a" }, { token::named_node, "b" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_descending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[*]\\");
            tokens_should_match_({ { token::root_node, "" }, { token::descending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_an_ascending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[*]/");
            tokens_should_match_({ { token::root_node, "" }, { token::ascending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_vertical_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[*]|");
            tokens_should_match_({ { token::root_node, "" }, { token::vertical_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("[*]a");
            tokens_should_match_({ { token::root_node, "" }, { token::edge_name, "a" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_root_node)
        {
            auto tokens = ascii_tree::tokenize("[a][*]");
            tokens_should_match_({ { token::named_node, "a" }, { token::root_node, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("[a][b]");
            tokens_should_match_({ { token::named_node, "a" }, { token::named_node, "b" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("[a]-b-");
            tokens_should_match_({ { token::named_node, "a" }, { token::horizontal_edge, "b" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_descending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[a]\\");
            tokens_should_match_({ { token::named_node, "a" }, { token::descending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_an_ascending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[a]/");
            tokens_should_match_({ { token::named_node, "a" }, { token::ascending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_vertical_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[a]|");
            tokens_should_match_({ { token::named_node, "a" }, { token::vertical_edge_part, "" } }, tokens);
        }

    };
}}
<commit_msg>recognize a named node next to an edge name<commit_after>#include "CppUnitTest.h"
#include <algorithm>
#include <sstream>
#include <cctype>
#include <string>
#include <vector>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace std;

namespace algo
{
    struct token
    {
        enum toktype { root_node, named_node, edge_name, horizontal_edge, ascending_edge_part, descending_edge_part, vertical_edge_part };
        toktype type;
        string name;
    };

    bool operator==(const token& lhs, const token& rhs)
    {
        return lhs.type == rhs.type
            && lhs.name == rhs.name;
    }

    struct ascii_tree
    {
        static vector<token> tokenize(const string& s)
        {
            vector<token> tokens;
            enum { none, open_square_brace, close_square_brace, asterisk, dash, name_char } prev = none;
            size_t marker = 0, marked_length = 0;

            for (size_t i = 0; i < s.size(); ++i)
            {
                auto ch = s[i];

                if (ch == '[')
                {
                    prev = open_square_brace;
                }
                else if (ch == ']')
                {
                    if (prev == asterisk)
                    {
                        tokens.emplace_back(token { token::root_node, "" });
                    }
                    else
                    {
                        tokens.emplace_back(token { token::named_node, s.substr(marker, marked_length) });
                    }

                    prev = close_square_brace;
                }
                else if (ch == '*')
                {
                    prev = asterisk;
                }
                else if (ch == '-')
                {
                    if (prev == name_char)
                    {
                        tokens.emplace_back(token { token::horizontal_edge, s.substr(marker, marked_length) });
                    }

                    prev = dash;
                }
                else if (ch == '/')
                {
                    tokens.emplace_back(token { token::ascending_edge_part, "" });
                }
                else if (ch == '\\')
                {
                    tokens.emplace_back(token { token::descending_edge_part, "" });
                }
                else if (ch == '|')
                {
                    tokens.emplace_back(token { token::vertical_edge_part, "" });
                }
                else if (isalnum(ch) || ch == '_')
                {
                    if (prev != name_char)
                    {
                        marker = i;
                        marked_length = 0;
                    }

                    prev = name_char;
                    ++marked_length;
                }
            }

            if (prev == name_char)
            {
                tokens.emplace_back(token { token::edge_name, s.substr(marker, marked_length) });
            }

            return tokens;
        }
    };
}

namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework
{
    template<>
    wstring ToString<algo::token::toktype>(const algo::token::toktype& type)
    {
        switch (type)
        {
        case algo::token::root_node:
            return L"root_node";
        case algo::token::named_node:
            return L"named_node";
        case algo::token::edge_name:
            return L"edge_name";
        case algo::token::horizontal_edge:
            return L"horizontal_edge";
        case algo::token::ascending_edge_part:
            return L"ascending_edge_part";
        case algo::token::descending_edge_part:
            return L"descending_edge_part";
        case algo::token::vertical_edge_part:
            return L"vertical_edge_part";
        default:
            return L"unknown token";
        }
    }
}}}

namespace algo { namespace spec
{
    TEST_CLASS(can_recognize_ascii_tree_tokens)
    {
        void tokens_should_match_(std::initializer_list<token> expected, vector<token>& actual)
        {
            auto mismatch_pair = std::mismatch(expected.begin(), expected.end(), actual.begin());

            if (mismatch_pair.first != expected.end() || mismatch_pair.second != actual.end())
            {
                wstring expectedName(mismatch_pair.first->name.begin(), mismatch_pair.first->name.end());
                wstring actualName(mismatch_pair.second->name.begin(), mismatch_pair.second->name.end());

                wstring message = L"Expected: " + ToString(mismatch_pair.first->type) + L" \"" + expectedName + L"\" "
                    + L"Actual: " + ToString(mismatch_pair.second->type) + L" \"" + actualName + L"\"";

                Assert::Fail(message.c_str());
            }
        }

    public:

        TEST_METHOD(should_not_recognize_any_tokens_in_an_empty_string)
        {
            auto tokens = ascii_tree::tokenize("");
            Assert::IsTrue(tokens.empty());
        }

        TEST_METHOD(should_recognize_a_root_node)
        {
            auto tokens = ascii_tree::tokenize("[*]");
            Assert::AreEqual(token::root_node, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_root_node_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" [ * ]");
            Assert::AreEqual(token::root_node, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_named_node)
        {
            const string all_chars = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            auto tokens = ascii_tree::tokenize("[" + all_chars + "]");
            Assert::AreEqual(token::named_node, tokens.front().type);
            Assert::AreEqual(all_chars.c_str(), tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_a_named_node_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" [ a ]");
            Assert::AreEqual(token::named_node, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("a");
            Assert::AreEqual(token::edge_name, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_edge_name_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" a ");
            Assert::AreEqual(token::edge_name, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_ascending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("/");
            Assert::AreEqual(token::ascending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_an_ascending_edge_part_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" / ");
            Assert::AreEqual(token::ascending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_descending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("\\");
            Assert::AreEqual(token::descending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_descending_edge_part_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" \\ ");
            Assert::AreEqual(token::descending_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_vertical_edge_part)
        {
            auto tokens = ascii_tree::tokenize("|");
            Assert::AreEqual(token::vertical_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_vertical_edge_part_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" | ");
            Assert::AreEqual(token::vertical_edge_part, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("-a-");
            Assert::AreEqual(token::horizontal_edge, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_a_horizontal_edge_with_spaces)
        {
            auto tokens = ascii_tree::tokenize(" - a - ");
            Assert::AreEqual(token::horizontal_edge, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("[*][a]");
            tokens_should_match_({ { token::root_node, "" }, { token::named_node, "a" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("[*]-a-");
            tokens_should_match_({ { token::root_node, "" }, { token::horizontal_edge, "a" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_horizontal_edge_next_to_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("-a-[b]");
            tokens_should_match_({ { token::horizontal_edge, "a" }, { token::named_node, "b" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_descending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[*]\\");
            tokens_should_match_({ { token::root_node, "" }, { token::descending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_an_ascending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[*]/");
            tokens_should_match_({ { token::root_node, "" }, { token::ascending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_a_vertical_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[*]|");
            tokens_should_match_({ { token::root_node, "" }, { token::vertical_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_root_node_next_to_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("[*]a");
            tokens_should_match_({ { token::root_node, "" }, { token::edge_name, "a" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_root_node)
        {
            auto tokens = ascii_tree::tokenize("[a][*]");
            tokens_should_match_({ { token::named_node, "a" }, { token::root_node, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("[a][b]");
            tokens_should_match_({ { token::named_node, "a" }, { token::named_node, "b" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("[a]-b-");
            tokens_should_match_({ { token::named_node, "a" }, { token::horizontal_edge, "b" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_descending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[a]\\");
            tokens_should_match_({ { token::named_node, "a" }, { token::descending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_an_ascending_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[a]/");
            tokens_should_match_({ { token::named_node, "a" }, { token::ascending_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_a_vertical_edge_part)
        {
            auto tokens = ascii_tree::tokenize("[a]|");
            tokens_should_match_({ { token::named_node, "a" }, { token::vertical_edge_part, "" } }, tokens);
        }

        TEST_METHOD(should_recognize_a_named_node_next_to_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("[a]b");
            tokens_should_match_({ { token::named_node, "a" }, { token::edge_name, "b" } }, tokens);
        }

    };
}}
<|endoftext|>


<commit_before>
/*
 * Copyright (c) 2015-2022 Agalmic Ventures LLC (www.agalmicventures.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#pragma once

#include <atomic>
#include <cassert>
#include <cstdarg>
#include <cstdint>
#include <limits>
#include <map>
#include <string>
#include <vector>

#include "Werk/Logging/Log.hpp"
#include "Werk/Utility/Action.hpp"
#include "Werk/Utility/Attributes.hpp"
#include "Werk/Utility/Latch.hpp"

namespace werk
{

class Config;

/**
 * Adapter for the Config class that allows keys to be read from a number of scopes,
 * allowing for cascading configurations.
 */
class ScopedConfig
{
public:
	ScopedConfig(Config &config) : _config(config) { }
	ScopedConfig(Config &config, const std::string &scope) : ScopedConfig(config)
	{
		_scopes.push_back(scope);
	}
	ScopedConfig(Config &config, const std::string &scope1, const std::string scope2) :
		ScopedConfig(config)
	{
		_scopes.push_back(scope1);
		_scopes.push_back(scope2);
	}
	ScopedConfig(Config &config, const std::string &scope1, const std::string scope2, const std::string scope3) :
		ScopedConfig(config)
	{
		_scopes.push_back(scope1);
		_scopes.push_back(scope2);
		_scopes.push_back(scope3);
	}
	ScopedConfig(Config &config, const std::string &scope1, const std::string scope2, const std::string scope3, const std::string scope4) :
		ScopedConfig(config)
	{
		_scopes.push_back(scope1);
		_scopes.push_back(scope2);
		_scopes.push_back(scope3);
		_scopes.push_back(scope4);
	}

	//Accessors
	CHECKED std::vector<std::string> &scopes() { return _scopes; }
	CHECKED const std::vector<std::string> &scopes() const { return _scopes; }

	//Base config accessors
	void addConfigurable(Configurable *configurable);

	//Basic types
	CHECKED const char *getString(const std::string &key, const char *defaultValue=nullptr, const char *help=nullptr) const;
	CHECKED bool getBool(const std::string &key, bool defaultValue=false, const char *help=nullptr) const;
	CHECKED double getDouble(const std::string &key, double defaultValue=0, const char *help=nullptr) const;
	CHECKED int64_t getInt64(const std::string &key, int64_t defaultValue=0, const char *help=nullptr) const;
	CHECKED uint64_t getUint64(const std::string &key, uint64_t defaultValue=0, const char *help=nullptr) const;
	CHECKED uint64_t getStorageAmount(const std::string &key, uint64_t defaultValue=0, const char *help=nullptr) const;
	CHECKED uint64_t getTimeAmount(const std::string &key, uint64_t defaultValue=0, const char *help=nullptr) const;

	//List types
	const char *getStrings(const std::string &key, std::vector<std::string> &values,
		const char *defaultValue=nullptr, const char *help=nullptr, const char *delimiters=",") const;

protected:
	//Config
	Config &_config;
	std::vector<std::string> _scopes;

	void findScopedKey(const std::string &key, std::string &specificKey, std::string &foundKey) const;
};

}
<commit_msg>Fix missing line in last commit<commit_after>
/*
 * Copyright (c) 2015-2022 Agalmic Ventures LLC (www.agalmicventures.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#pragma once

#include <atomic>
#include <cassert>
#include <cstdarg>
#include <cstdint>
#include <limits>
#include <map>
#include <string>
#include <vector>

#include "Werk/Logging/Log.hpp"
#include "Werk/Utility/Action.hpp"
#include "Werk/Utility/Attributes.hpp"
#include "Werk/Utility/Latch.hpp"

namespace werk
{

class Config;
class Configurable;

/**
 * Adapter for the Config class that allows keys to be read from a number of scopes,
 * allowing for cascading configurations.
 */
class ScopedConfig
{
public:
	ScopedConfig(Config &config) : _config(config) { }
	ScopedConfig(Config &config, const std::string &scope) : ScopedConfig(config)
	{
		_scopes.push_back(scope);
	}
	ScopedConfig(Config &config, const std::string &scope1, const std::string scope2) :
		ScopedConfig(config)
	{
		_scopes.push_back(scope1);
		_scopes.push_back(scope2);
	}
	ScopedConfig(Config &config, const std::string &scope1, const std::string scope2, const std::string scope3) :
		ScopedConfig(config)
	{
		_scopes.push_back(scope1);
		_scopes.push_back(scope2);
		_scopes.push_back(scope3);
	}
	ScopedConfig(Config &config, const std::string &scope1, const std::string scope2, const std::string scope3, const std::string scope4) :
		ScopedConfig(config)
	{
		_scopes.push_back(scope1);
		_scopes.push_back(scope2);
		_scopes.push_back(scope3);
		_scopes.push_back(scope4);
	}

	//Accessors
	CHECKED std::vector<std::string> &scopes() { return _scopes; }
	CHECKED const std::vector<std::string> &scopes() const { return _scopes; }

	//Base config accessors
	void addConfigurable(Configurable *configurable);

	//Basic types
	CHECKED const char *getString(const std::string &key, const char *defaultValue=nullptr, const char *help=nullptr) const;
	CHECKED bool getBool(const std::string &key, bool defaultValue=false, const char *help=nullptr) const;
	CHECKED double getDouble(const std::string &key, double defaultValue=0, const char *help=nullptr) const;
	CHECKED int64_t getInt64(const std::string &key, int64_t defaultValue=0, const char *help=nullptr) const;
	CHECKED uint64_t getUint64(const std::string &key, uint64_t defaultValue=0, const char *help=nullptr) const;
	CHECKED uint64_t getStorageAmount(const std::string &key, uint64_t defaultValue=0, const char *help=nullptr) const;
	CHECKED uint64_t getTimeAmount(const std::string &key, uint64_t defaultValue=0, const char *help=nullptr) const;

	//List types
	const char *getStrings(const std::string &key, std::vector<std::string> &values,
		const char *defaultValue=nullptr, const char *help=nullptr, const char *delimiters=",") const;

protected:
	//Config
	Config &_config;
	std::vector<std::string> _scopes;

	void findScopedKey(const std::string &key, std::string &specificKey, std::string &foundKey) const;
};

}
<|endoftext|>


<commit_before>#include "CppUnitTest.h"
#include <string>
#include <vector>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace std;

namespace algo
{
    struct token
    {
        enum toktype { root_node, named_node, edge_name };
        toktype type;
        string name;
    };

    bool operator==(const token& lhs, const token& rhs)
    {
        return true;
    }

    struct ascii_tree
    {
        static vector<token> tokenize(const string& s)
        {
            enum { none, open_square_brace, asterisk, edge_name } prev = none;

            for (auto ch : s)
            {
                if (ch == '[')
                {
                    prev = open_square_brace;
                }
                else if (ch == ']')
                {
                    if (prev == asterisk)
                    {
                        token newtok = { token::root_node, "" };
                        return vector<token>(1, newtok);
                    }
                    else
                    {
                        string name = s.substr(1, s.size() - 2);
                        token newtok = { token::named_node, name };
                        return vector<token>(1, newtok);
                    }
                }
                else if (ch == '*')
                {
                    prev = asterisk;
                }
                else
                {
                    if (prev == none)
                    {
                        prev = edge_name;
                    }
                }
            }

            return (prev == edge_name)
                ? vector<token>(1, token { token::edge_name, s })
                : vector<token>();
        }
    };
}

namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework
{
    template<>
    wstring ToString<algo::token::toktype>(const algo::token::toktype& type)
    {
        switch (type)
        {
        case algo::token::root_node:
            return L"root_node";
        case algo::token::named_node:
            return L"named_node";
        case algo::token::edge_name:
            return L"edge_name";
        default:
            return L"unknown token";
        }
    }
}}}

namespace algo { namespace spec
{
	TEST_CLASS(can_recognize_ascii_tree_tokens)
	{
	public:

        TEST_METHOD(should_not_recognize_any_tokens_in_an_empty_string)
        {
            auto tokens = ascii_tree::tokenize("");
            Assert::IsTrue(tokens.empty());
        }

		TEST_METHOD(should_recognize_a_root_node)
		{
            auto tokens = ascii_tree::tokenize("[*]");
            Assert::AreEqual(token::root_node, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("[a]");
            Assert::AreEqual(token::named_node, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("a");
            Assert::AreEqual(token::edge_name, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

    };
}}
<commit_msg>recognize a horizontal edge<commit_after>#include "CppUnitTest.h"
#include <string>
#include <vector>

using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace std;

namespace algo
{
    struct token
    {
        enum toktype { root_node, named_node, edge_name, horizontal_edge };
        toktype type;
        string name;
    };

    bool operator==(const token& lhs, const token& rhs)
    {
        return true;
    }

    struct ascii_tree
    {
        static vector<token> tokenize(const string& s)
        {
            enum { none, open_square_brace, asterisk, dash, edge_name } prev = none;

            for (auto ch : s)
            {
                if (ch == '[')
                {
                    prev = open_square_brace;
                }
                else if (ch == ']')
                {
                    if (prev == asterisk)
                    {
                        token newtok = { token::root_node, "" };
                        return vector<token>(1, newtok);
                    }
                    else
                    {
                        string name = s.substr(1, s.size() - 2);
                        token newtok = { token::named_node, name };
                        return vector<token>(1, newtok);
                    }
                }
                else if (ch == '*')
                {
                    prev = asterisk;
                }
                else if (ch == '-')
                {
                    if (prev == dash)
                    {
                        string name = s.substr(2, s.size() - 4);
                        token newtok = { token::horizontal_edge, name };
                        return vector<token>(1, newtok);
                    }
                    prev = dash;
                }
                else
                {
                    if (prev == none)
                    {
                        prev = edge_name;
                    }
                }
            }

            return (prev == edge_name)
                ? vector<token>(1, token { token::edge_name, s })
                : vector<token>();
        }
    };
}

namespace Microsoft { namespace VisualStudio { namespace CppUnitTestFramework
{
    template<>
    wstring ToString<algo::token::toktype>(const algo::token::toktype& type)
    {
        switch (type)
        {
        case algo::token::root_node:
            return L"root_node";
        case algo::token::named_node:
            return L"named_node";
        case algo::token::edge_name:
            return L"edge_name";
        case algo::token::horizontal_edge:
            return L"horizontal_edge";
        default:
            return L"unknown token";
        }
    }
}}}

namespace algo { namespace spec
{
	TEST_CLASS(can_recognize_ascii_tree_tokens)
	{
	public:

        TEST_METHOD(should_not_recognize_any_tokens_in_an_empty_string)
        {
            auto tokens = ascii_tree::tokenize("");
            Assert::IsTrue(tokens.empty());
        }

		TEST_METHOD(should_recognize_a_root_node)
		{
            auto tokens = ascii_tree::tokenize("[*]");
            Assert::AreEqual(token::root_node, tokens.front().type);
        }

        TEST_METHOD(should_recognize_a_named_node)
        {
            auto tokens = ascii_tree::tokenize("[a]");
            Assert::AreEqual(token::named_node, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_edge_name)
        {
            auto tokens = ascii_tree::tokenize("a");
            Assert::AreEqual(token::edge_name, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

        TEST_METHOD(should_recognize_an_horizontal_edge)
        {
            auto tokens = ascii_tree::tokenize("--a--");
            Assert::AreEqual(token::horizontal_edge, tokens.front().type);
            Assert::AreEqual("a", tokens.front().name.c_str());
        }

    };
}}
<|endoftext|>


<commit_before>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// Licensed under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License. You
// may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

#include "wx/textctrl.h" // for wxEVT_TEXT_ENTER and such
#include "wx/sizer.h"
#include "wx/spinctrl.h"
#include "gui/events.hh"
#include "gui/spin-ctrl.hh"
#include "util-wx/bind-event.hh"
#include "util-wx/fwd-bind.hh"
#include "util-wx/fwd-wx.hh"
#include "util-wx/layout-wx.hh"
#include "util/convenience.hh"

namespace faint{

// Fixme: The controls in this class file duplicate eachother a lot.

class FocusRelayingSpinCtrl : public wxSpinCtrl{
public:
  FocusRelayingSpinCtrl(wxWindow* parent, wxSize size) :
    wxSpinCtrl(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size,
      wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER)
  {
    events::on_set_focus(this, [this](){
      wxCommandEvent newEvent(EVT_FAINT_SetFocusEntryControl, wxID_ANY);
      newEvent.SetEventObject(this);
      GetEventHandler()->ProcessEvent(newEvent);
    });

    events::on_kill_focus(this, [this](){
      wxCommandEvent newEvent(EVT_FAINT_KillFocusEntryControl, wxID_ANY);
      newEvent.SetEventObject(this);
      GetEventHandler()->ProcessEvent(newEvent);
    });
  }
};

class FocusRelayingSpinCtrlDouble : public wxSpinCtrlDouble{
public:
  FocusRelayingSpinCtrlDouble(wxWindow* parent, wxSize size) :
    wxSpinCtrlDouble(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, size,
      wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER)
  {
    events::on_set_focus(this, [this](){
      wxCommandEvent newEvent(EVT_FAINT_SetFocusEntryControl, wxID_ANY);
      newEvent.SetEventObject(this);
      GetEventHandler()->ProcessEvent(newEvent);
    });

    events::on_kill_focus(this, [this](){
      wxCommandEvent newEvent(EVT_FAINT_KillFocusEntryControl, wxID_ANY);
      newEvent.SetEventObject(this);
      GetEventHandler()->ProcessEvent(newEvent);
    });
  }
};

class IntSizeControl : public IntSettingCtrl{
public:
  IntSizeControl(wxWindow* parent,
    wxSize size,
    const IntSetting& setting,
    int value,
    const std::string& label) // Fixme: Why std::string?
    : IntSettingCtrl(parent, setting),
      m_changed(false)
  {
    auto sizer = new wxBoxSizer(wxVERTICAL);
    if (label.size() > 0){
      layout::add(sizer, create_label(this, label.c_str()));
    }
    m_spinCtrl = new FocusRelayingSpinCtrl(this, size);
    m_spinCtrl->SetValue(value);
    m_spinCtrl->SetRange(1, 255);
    m_spinCtrl->SetBackgroundColour(wxColour(255, 255, 255));
    sizer->Add(m_spinCtrl, 0, wxALIGN_CENTER_HORIZONTAL);
    SetSizerAndFit(sizer);

    events::on_idle(this, [this](){
      if (then_false(m_changed)){
        SendChangeEvent();
      }
    });

    bind_fwd(this, wxEVT_SPINCTRL,
      [this](wxSpinEvent& event){
        event.Skip();
        SendChangeEvent();
      });

    bind(this, wxEVT_TEXT_ENTER,
      [this](){
        SendChangeEvent();
      });
  }

  int GetValue() const override{
    return m_spinCtrl->GetValue();
  }

  void SetValue(int value) override{
    m_spinCtrl->SetValue(value);
  }

private:
  bool m_changed;
  wxSpinCtrl* m_spinCtrl;
};

IntSettingCtrl* create_int_spinner(wxWindow* parent, const wxSize& size,
  const IntSetting& setting, int value, const std::string& label)
{
  return new IntSizeControl(parent, size, setting, value, label);
}

class FloatSizeControl : public FloatSettingControl{
public:
  FloatSizeControl(wxWindow* parent, wxSize size, const FloatSetting& setting,
    coord value, const std::string& label)
    : FloatSettingControl(parent, setting),
      m_changed(false)
  {
    auto sizer = new wxBoxSizer(wxVERTICAL);
    if (label.size() > 0){
      layout::add(sizer, create_label(this, label.c_str()));
    }
    m_spinCtrl = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString,
      wxDefaultPosition, size, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER);
    m_spinCtrl->SetValue(value);
    m_spinCtrl->SetRange(0.1, 255);
    m_spinCtrl->SetBackgroundColour(wxColour(255, 255, 255));

    sizer->Add(m_spinCtrl, 0, wxALIGN_CENTER_HORIZONTAL);
    SetSizerAndFit(sizer);

    events::on_idle(this, [this](){
      if (then_false(m_changed)){
        SendChangeEvent();
      }
    });

    bind_fwd(this, wxEVT_SPINCTRLDOUBLE,
      [this](wxSpinDoubleEvent& event){
        m_changed = true;
        event.Skip();
      });

    bind(this, wxEVT_TEXT_ENTER,
      [this](){
        SendChangeEvent();
      });
  }

  coord GetValue() const override{
    return m_spinCtrl->GetValue();
  }

  void SetValue(coord value) override{
    m_spinCtrl->SetValue(value);
  }

private:
  bool m_changed;
  wxSpinCtrlDouble* m_spinCtrl;
};

FloatSettingControl* create_float_spinner(wxWindow* parent, const wxSize& size,
  const FloatSetting& setting, coord value, const std::string& label)
{
  return new FloatSizeControl(parent, size, setting, value, label);
}

class SemiFloatSizeControl : public FloatSettingControl{
public:
  SemiFloatSizeControl(wxWindow* parent, wxSize size, const FloatSetting& setting,
    coord value, const std::string& label)
  : FloatSettingControl(parent, setting)
  {
    auto sizer = new wxBoxSizer(wxVERTICAL);
    if (label.size() > 0){
      layout::add(sizer, create_label(this, label.c_str()));
    }
    m_spinCtrl = new FocusRelayingSpinCtrl(this, size);
    m_spinCtrl->SetValue(static_cast<int>(value));
    m_spinCtrl->SetRange(1, 255);
    m_spinCtrl->SetBackgroundColour(wxColour(255, 255, 255));
    m_lastValue = static_cast<int>(value);
    sizer->Add(m_spinCtrl, 0, wxALIGN_CENTER_HORIZONTAL);
    SetSizerAndFit(sizer);

    bind_fwd(this, wxEVT_SPINCTRL,
      [this](wxSpinEvent& event){
        m_changed = true;
        event.Skip();
      });

    bind(this, wxEVT_TEXT_ENTER,
      [this](){
        m_lastValue = m_spinCtrl->GetValue();
        SendChangeEvent();
      });

    events::on_idle(this, [this](){
      if (then_false(m_changed)){
        m_lastValue = m_spinCtrl->GetValue();
        SendChangeEvent();
      }
    });

    bind_fwd(this, EVT_FAINT_KillFocusEntryControl,
      [this](wxEvent& event){
        event.Skip();
        if (m_lastValue  != m_spinCtrl->GetValue()){
          SendChangeEvent();
        }
      });
  }

  coord GetValue() const override{
    return static_cast<coord>(m_spinCtrl->GetValue());
  }

  virtual void SetValue(coord value) override{
    m_spinCtrl->SetValue(static_cast<int>(value));
  }

private:
  wxSpinCtrl* m_spinCtrl;
  bool m_changed;
  int m_lastValue;
};

FloatSettingControl* create_semi_float_spinner(wxWindow* parent, const wxSize& size,
  const FloatSetting& setting, coord value, const std::string& label)
{
  return new SemiFloatSizeControl(parent, size, setting, value, label);
}

} // namespace
<commit_msg>Reduced duplication in spin-ctrl.cpp.<commit_after>// -*- coding: us-ascii-unix -*-
// Copyright 2012 Lukas Kemmer
//
// Licensed under the Apache License, Version 2.0 (the "License"); you
// may not use this file except in compliance with the License. You
// may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

#include "wx/textctrl.h" // for wxEVT_TEXT_ENTER and such
#include "wx/sizer.h"
#include "wx/spinctrl.h"
#include "gui/events.hh"
#include "gui/spin-ctrl.hh"
#include "util-wx/bind-event.hh"
#include "util-wx/fwd-bind.hh"
#include "util-wx/fwd-wx.hh"
#include "util-wx/layout-wx.hh"
#include "util/convenience.hh"

namespace faint{

// Fixme: The controls in this class file duplicate eachother a lot.

template<typename WXCTRL_T>
class FocusRelayingCtrl : public WXCTRL_T{
public:
  FocusRelayingCtrl(wxWindow* parent, const wxSize& size)
    : WXCTRL_T(parent,
        wxID_ANY,
        wxEmptyString,
        wxDefaultPosition,
        size,
        wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER)
  {
    events::on_set_focus(this, [this](){
      wxCommandEvent newEvent(EVT_FAINT_SetFocusEntryControl, wxID_ANY);
      newEvent.SetEventObject(this);
      GetEventHandler()->ProcessEvent(newEvent);
    });

    events::on_kill_focus(this, [this](){
      wxCommandEvent newEvent(EVT_FAINT_KillFocusEntryControl, wxID_ANY);
      newEvent.SetEventObject(this);
      GetEventHandler()->ProcessEvent(newEvent);
    });
  }
};

using FocusRelayingSpinCtrl = FocusRelayingCtrl<wxSpinCtrl>;
using FocusRelayingSpinCtrlDouble = FocusRelayingCtrl<wxSpinCtrlDouble>;

class IntSizeControl : public IntSettingCtrl{
public:
  IntSizeControl(wxWindow* parent,
    wxSize size,
    const IntSetting& setting,
    int value,
    const std::string& label) // Fixme: Why std::string?
    : IntSettingCtrl(parent, setting),
      m_changed(false)
  {
    auto sizer = new wxBoxSizer(wxVERTICAL);
    if (label.size() > 0){
      layout::add(sizer, create_label(this, label.c_str()));
    }
    m_spinCtrl = new FocusRelayingSpinCtrl(this, size);
    m_spinCtrl->SetValue(value);
    m_spinCtrl->SetRange(1, 255);
    m_spinCtrl->SetBackgroundColour(wxColour(255, 255, 255));
    sizer->Add(m_spinCtrl, 0, wxALIGN_CENTER_HORIZONTAL);
    SetSizerAndFit(sizer);

    events::on_idle(this, [this](){
      if (then_false(m_changed)){
        SendChangeEvent();
      }
    });

    bind_fwd(this, wxEVT_SPINCTRL,
      [this](wxSpinEvent& event){
        event.Skip();
        SendChangeEvent();
      });

    bind(this, wxEVT_TEXT_ENTER,
      [this](){
        SendChangeEvent();
      });
  }

  int GetValue() const override{
    return m_spinCtrl->GetValue();
  }

  void SetValue(int value) override{
    m_spinCtrl->SetValue(value);
  }

private:
  bool m_changed;
  wxSpinCtrl* m_spinCtrl;
};

IntSettingCtrl* create_int_spinner(wxWindow* parent, const wxSize& size,
  const IntSetting& setting, int value, const std::string& label)
{
  return new IntSizeControl(parent, size, setting, value, label);
}

class FloatSizeControl : public FloatSettingControl{
public:
  FloatSizeControl(wxWindow* parent, wxSize size, const FloatSetting& setting,
    coord value, const std::string& label)
    : FloatSettingControl(parent, setting),
      m_changed(false)
  {
    auto sizer = new wxBoxSizer(wxVERTICAL);
    if (label.size() > 0){
      layout::add(sizer, create_label(this, label.c_str()));
    }
    m_spinCtrl = new wxSpinCtrlDouble(this, wxID_ANY, wxEmptyString,
      wxDefaultPosition, size, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER);
    m_spinCtrl->SetValue(value);
    m_spinCtrl->SetRange(0.1, 255);
    m_spinCtrl->SetBackgroundColour(wxColour(255, 255, 255));

    sizer->Add(m_spinCtrl, 0, wxALIGN_CENTER_HORIZONTAL);
    SetSizerAndFit(sizer);

    events::on_idle(this, [this](){
      if (then_false(m_changed)){
        SendChangeEvent();
      }
    });

    bind_fwd(this, wxEVT_SPINCTRLDOUBLE,
      [this](wxSpinDoubleEvent& event){
        m_changed = true;
        event.Skip();
      });

    bind(this, wxEVT_TEXT_ENTER,
      [this](){
        SendChangeEvent();
      });
  }

  coord GetValue() const override{
    return m_spinCtrl->GetValue();
  }

  void SetValue(coord value) override{
    m_spinCtrl->SetValue(value);
  }

private:
  bool m_changed;
  wxSpinCtrlDouble* m_spinCtrl;
};

FloatSettingControl* create_float_spinner(wxWindow* parent, const wxSize& size,
  const FloatSetting& setting, coord value, const std::string& label)
{
  return new FloatSizeControl(parent, size, setting, value, label);
}

class SemiFloatSizeControl : public FloatSettingControl{
public:
  SemiFloatSizeControl(wxWindow* parent, wxSize size, const FloatSetting& setting,
    coord value, const std::string& label)
  : FloatSettingControl(parent, setting)
  {
    auto sizer = new wxBoxSizer(wxVERTICAL);
    if (label.size() > 0){
      layout::add(sizer, create_label(this, label.c_str()));
    }
    m_spinCtrl = new FocusRelayingSpinCtrl(this, size);
    m_spinCtrl->SetValue(static_cast<int>(value));
    m_spinCtrl->SetRange(1, 255);
    m_spinCtrl->SetBackgroundColour(wxColour(255, 255, 255));
    m_lastValue = static_cast<int>(value);
    sizer->Add(m_spinCtrl, 0, wxALIGN_CENTER_HORIZONTAL);
    SetSizerAndFit(sizer);

    bind_fwd(this, wxEVT_SPINCTRL,
      [this](wxSpinEvent& event){
        m_changed = true;
        event.Skip();
      });

    bind(this, wxEVT_TEXT_ENTER,
      [this](){
        m_lastValue = m_spinCtrl->GetValue();
        SendChangeEvent();
      });

    events::on_idle(this, [this](){
      if (then_false(m_changed)){
        m_lastValue = m_spinCtrl->GetValue();
        SendChangeEvent();
      }
    });

    bind_fwd(this, EVT_FAINT_KillFocusEntryControl,
      [this](wxEvent& event){
        event.Skip();
        if (m_lastValue  != m_spinCtrl->GetValue()){
          SendChangeEvent();
        }
      });
  }

  coord GetValue() const override{
    return static_cast<coord>(m_spinCtrl->GetValue());
  }

  virtual void SetValue(coord value) override{
    m_spinCtrl->SetValue(static_cast<int>(value));
  }

private:
  wxSpinCtrl* m_spinCtrl;
  bool m_changed;
  int m_lastValue;
};

FloatSettingControl* create_semi_float_spinner(wxWindow* parent,
  const wxSize& size,
  const FloatSetting& setting,
  coord value,
  const std::string& label)
{
  return new SemiFloatSizeControl(parent, size, setting, value, label);
}

} // namespace
<|endoftext|>


<commit_before>//
//  main.cpp
//  Unit Tests
//
//  Created by Evan McCartney-Melstad on 12/31/14.
//  Copyright (c) 2014 Evan McCartney-Melstad. All rights reserved.
//

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "../cPWP/generateSimulatedData.h"
#include "../cPWP/bamsToPWP.h"
#include "catch.hpp"
#include <string>

TEST_CASE( "Simulated reads are generated", "[generateReads]" ) {
    // Make sure that the read simulation finishes
    REQUIRE( generateReadsAndMap(1, 0.01, "0.0", "300", "50", "100000", "100", "1234", "scaffold_0.fasta") == 0);

}

TEST_CASE( "Run ANGSD on simulated reads", "[runANGSD]" ) {
    REQUIRE( runANGSDforReadCounts("bamlist.txt", "angsdOut", "10", "angsdOutLog.txt") == 0);
}

TEST_CASE( "Convert ANGSD read counts to unsigned chars for major and minor counts", "[convertCountsToBinary]") {
    REQUIRE( convertANGSDcountsToBinary("angsdOut", "angsdOut.readCounts.binary", 2, 5000) == 0); // 2 individuals, not 1, because generateReadsAndMap actually generates n+1 individuals, since one individual is identical to the reference genome. And 5000 as a max because we don't want to exclude any loci for this test
}

TEST_CASE( "Calculate PWP from the binary representations of the ANGSD readcounts", "[calcPWP]") {
    REQUIRE( calcPWPfromBinaryFile ("angsdOut.readCounts.binary", 70000, 2) == 0);
}
<commit_msg>Troubleshooting<commit_after>//
//  main.cpp
//  Unit Tests
//
//  Created by Evan McCartney-Melstad on 12/31/14.
//  Copyright (c) 2014 Evan McCartney-Melstad. All rights reserved.
//

#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
#include "../cPWP/generateSimulatedData.h"
#include "../cPWP/bamsToPWP.h"
#include "catch.hpp"
#include <string>

TEST_CASE( "Simulated reads are generated", "[generateReads]" ) {
    // Make sure that the read simulation finishes
    REQUIRE( generateReadsAndMap(1, 0.01, "0.0", "300", "50", "1000000", "100", "1234", "scaffold_0.fasta") == 0);

}

TEST_CASE( "Run ANGSD on simulated reads", "[runANGSD]" ) {
    REQUIRE( runANGSDforReadCounts("bamlist.txt", "angsdOut", "10", "angsdOutLog.txt") == 0);
}

TEST_CASE( "Convert ANGSD read counts to unsigned chars for major and minor counts", "[convertCountsToBinary]") {
    REQUIRE( convertANGSDcountsToBinary("angsdOut", "angsdOut.readCounts.binary", 2, 5000) == 0); // 2 individuals, not 1, because generateReadsAndMap actually generates n+1 individuals, since one individual is identical to the reference genome. And 5000 as a max because we don't want to exclude any loci for this test
}

TEST_CASE( "Calculate PWP from the binary representations of the ANGSD readcounts", "[calcPWP]") {
    REQUIRE( calcPWPfromBinaryFile ("angsdOut.readCounts.binary", 70000, 2) == 0);
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/content/display/display_color_manager_chromeos.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/qcms/src/qcms.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/gamma_ramp_rgb_entry.h"
#include "ui/display/types/native_display_delegate.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"

using content::BrowserThread;

namespace ash {

namespace {

bool ParseFile(const base::FilePath& path,
               DisplayColorManager::ColorCalibrationData* data) {
  qcms_profile* display_profile = qcms_profile_from_path(path.value().c_str());

  if (!display_profile) {
    LOG(WARNING) << "Unable to load ICC file: " << path.value();
    return false;
  }

  size_t vcgt_channel_length =
      qcms_profile_get_vcgt_channel_length(display_profile);
  if (!vcgt_channel_length) {
    LOG(WARNING) << "No vcgt table in ICC file: " << path.value();
    return false;
  }

  std::vector<uint16_t> vcgt_data;
  vcgt_data.resize(vcgt_channel_length * 3);
  if (!qcms_profile_get_vcgt_rgb_channels(display_profile, &vcgt_data[0])) {
    LOG(WARNING) << "Unable to get vcgt data";
    qcms_profile_release(display_profile);
    return false;
  }

  data->lut.resize(vcgt_channel_length);
  for (size_t i = 0; i < vcgt_channel_length; ++i) {
    data->lut[i].r = vcgt_data[i];
    data->lut[i].g = vcgt_data[vcgt_channel_length + i];
    data->lut[i].b = vcgt_data[(vcgt_channel_length * 2) + i];
  }
  qcms_profile_release(display_profile);
  return true;
}

base::FilePath PathForDisplaySnapshot(const ui::DisplaySnapshot* snapshot) {
  if (gfx::Display::IsInternalDisplayId(snapshot->display_id())) {
    const base::CommandLine* command_line =
        base::CommandLine::ForCurrentProcess();
    if (command_line->HasSwitch(
            chromeos::switches::kInternalDisplayColorProfileFile)) {
      const base::FilePath& path = command_line->GetSwitchValuePath(
          chromeos::switches::kInternalDisplayColorProfileFile);
      return base::FilePath(path);
    }
  }

  base::FilePath path;
  CHECK(
      PathService::Get(chromeos::DIR_DEVICE_COLOR_CALIBRATION_PROFILES, &path));
  path = path.Append(
      base::StringPrintf("%08" PRIx64 ".icc", snapshot->product_id()));
  return path;
}

}  // namespace

DisplayColorManager::DisplayColorManager(ui::DisplayConfigurator* configurator)
    : configurator_(configurator) {
  configurator_->AddObserver(this);
}

DisplayColorManager::~DisplayColorManager() {
  configurator_->RemoveObserver(this);
  STLDeleteValues(&calibration_map_);
}

void DisplayColorManager::OnDisplayModeChanged(
    const ui::DisplayConfigurator::DisplayStateList& display_states) {
  for (const ui::DisplaySnapshot* state : display_states) {
    if (calibration_map_[state->product_id()]) {
      ApplyDisplayColorCalibration(state->display_id(), state->product_id());
    } else {
      if (state->product_id() != ui::DisplaySnapshot::kInvalidProductID)
        LoadCalibrationForDisplay(state);
    }
  }
}

void DisplayColorManager::ApplyDisplayColorCalibration(int64_t display_id,
                                                       int64_t product_id) {
  if (calibration_map_.find(product_id) != calibration_map_.end()) {
    ColorCalibrationData* ramp = calibration_map_[product_id];
    if (!configurator_->SetGammaRamp(display_id, ramp->lut))
      LOG(WARNING) << "Error applying gamma ramp";
  }
}

void DisplayColorManager::LoadCalibrationForDisplay(
    const ui::DisplaySnapshot* display) {
  if (display->display_id() == gfx::Display::kInvalidDisplayID) {
    LOG(WARNING) << "Trying to load calibration data for invalid display id";
    return;
  }

  base::FilePath path = PathForDisplaySnapshot(display);
  VLOG(1) << "Checking ICC file " << path.value()
          << " for display id: " << display->display_id()
          << " with product id: " << display->product_id();
  if (!base::PathExists(path))  // No icc file for this display.
    return;

  scoped_ptr<ColorCalibrationData> data(new ColorCalibrationData());
  base::Callback<bool(void)> request(
      base::Bind(&ParseFile, path, base::Unretained(data.get())));
  base::PostTaskAndReplyWithResult(
      BrowserThread::GetBlockingPool(), FROM_HERE, request,
      base::Bind(&DisplayColorManager::UpdateCalibrationData, AsWeakPtr(),
                 display->display_id(), display->product_id(),
                 base::Passed(data.Pass())));
}

void DisplayColorManager::UpdateCalibrationData(
    int64_t display_id,
    int64_t product_id,
    scoped_ptr<ColorCalibrationData> data,
    bool success) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (success) {
    // The map takes over ownership of the underlying memory.
    calibration_map_[product_id] = data.release();
    ApplyDisplayColorCalibration(display_id, product_id);
  }
}

DisplayColorManager::ColorCalibrationData::ColorCalibrationData() {
}

DisplayColorManager::ColorCalibrationData::~ColorCalibrationData() {
}

}  // namespace ash
<commit_msg>Revert of Don't try to load non-existent icc file (patchset #2 id:20001 of https://codereview.chromium.org/1302273006/ )<commit_after>// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/content/display/display_color_manager_chromeos.h"

#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "chromeos/chromeos_paths.h"
#include "chromeos/chromeos_switches.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/qcms/src/qcms.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/gamma_ramp_rgb_entry.h"
#include "ui/display/types/native_display_delegate.h"
#include "ui/gfx/display.h"
#include "ui/gfx/screen.h"

using content::BrowserThread;

namespace ash {

namespace {

bool ParseFile(const base::FilePath& path,
               DisplayColorManager::ColorCalibrationData* data) {
  qcms_profile* display_profile = qcms_profile_from_path(path.value().c_str());

  if (!display_profile) {
    LOG(WARNING) << "Unable to load ICC file: " << path.value();
    return false;
  }

  size_t vcgt_channel_length =
      qcms_profile_get_vcgt_channel_length(display_profile);
  if (!vcgt_channel_length) {
    LOG(WARNING) << "No vcgt table in ICC file: " << path.value();
    return false;
  }

  std::vector<uint16_t> vcgt_data;
  vcgt_data.resize(vcgt_channel_length * 3);
  if (!qcms_profile_get_vcgt_rgb_channels(display_profile, &vcgt_data[0])) {
    LOG(WARNING) << "Unable to get vcgt data";
    qcms_profile_release(display_profile);
    return false;
  }

  data->lut.resize(vcgt_channel_length);
  for (size_t i = 0; i < vcgt_channel_length; ++i) {
    data->lut[i].r = vcgt_data[i];
    data->lut[i].g = vcgt_data[vcgt_channel_length + i];
    data->lut[i].b = vcgt_data[(vcgt_channel_length * 2) + i];
  }
  qcms_profile_release(display_profile);
  return true;
}

base::FilePath PathForDisplaySnapshot(const ui::DisplaySnapshot* snapshot) {
  if (gfx::Display::IsInternalDisplayId(snapshot->display_id())) {
    const base::CommandLine* command_line =
        base::CommandLine::ForCurrentProcess();
    if (command_line->HasSwitch(
            chromeos::switches::kInternalDisplayColorProfileFile)) {
      const base::FilePath& path = command_line->GetSwitchValuePath(
          chromeos::switches::kInternalDisplayColorProfileFile);
      return base::FilePath(path);
    }
  }

  base::FilePath path;
  CHECK(
      PathService::Get(chromeos::DIR_DEVICE_COLOR_CALIBRATION_PROFILES, &path));
  path = path.Append(
      base::StringPrintf("%08" PRIx64 ".icc", snapshot->product_id()));
  return path;
}

}  // namespace

DisplayColorManager::DisplayColorManager(ui::DisplayConfigurator* configurator)
    : configurator_(configurator) {
  configurator_->AddObserver(this);
}

DisplayColorManager::~DisplayColorManager() {
  configurator_->RemoveObserver(this);
  STLDeleteValues(&calibration_map_);
}

void DisplayColorManager::OnDisplayModeChanged(
    const ui::DisplayConfigurator::DisplayStateList& display_states) {
  for (const ui::DisplaySnapshot* state : display_states) {
    if (calibration_map_[state->product_id()]) {
      ApplyDisplayColorCalibration(state->display_id(), state->product_id());
    } else {
      if (state->product_id() != ui::DisplaySnapshot::kInvalidProductID)
        LoadCalibrationForDisplay(state);
    }
  }
}

void DisplayColorManager::ApplyDisplayColorCalibration(int64_t display_id,
                                                       int64_t product_id) {
  if (calibration_map_.find(product_id) != calibration_map_.end()) {
    ColorCalibrationData* ramp = calibration_map_[product_id];
    if (!configurator_->SetGammaRamp(display_id, ramp->lut))
      LOG(WARNING) << "Error applying gamma ramp";
  }
}

void DisplayColorManager::LoadCalibrationForDisplay(
    const ui::DisplaySnapshot* display) {
  if (display->display_id() == gfx::Display::kInvalidDisplayID) {
    LOG(WARNING) << "Trying to load calibration data for invalid display id";
    return;
  }

  base::FilePath path = PathForDisplaySnapshot(display);
  VLOG(1) << "Loading ICC file " << path.value()
          << " for display id: " << display->display_id()
          << " with product id: " << display->product_id();

  scoped_ptr<ColorCalibrationData> data(new ColorCalibrationData());
  base::Callback<bool(void)> request(
      base::Bind(&ParseFile, path, base::Unretained(data.get())));
  base::PostTaskAndReplyWithResult(
      BrowserThread::GetBlockingPool(), FROM_HERE, request,
      base::Bind(&DisplayColorManager::UpdateCalibrationData, AsWeakPtr(),
                 display->display_id(), display->product_id(),
                 base::Passed(data.Pass())));
}

void DisplayColorManager::UpdateCalibrationData(
    int64_t display_id,
    int64_t product_id,
    scoped_ptr<ColorCalibrationData> data,
    bool success) {
  DCHECK_CURRENTLY_ON(BrowserThread::UI);
  if (success) {
    // The map takes over ownership of the underlying memory.
    calibration_map_[product_id] = data.release();
    ApplyDisplayColorCalibration(display_id, product_id);
  }
}

DisplayColorManager::ColorCalibrationData::ColorCalibrationData() {
}

DisplayColorManager::ColorCalibrationData::~ColorCalibrationData() {
}

}  // namespace ash
<|endoftext|>


<commit_before>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include "caf/async/batch.hpp"
#include "caf/async/producer.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/disposable.hpp"
#include "caf/error.hpp"
#include "caf/flow/coordinator.hpp"
#include "caf/flow/subscription.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/logger.hpp"
#include "caf/make_counted.hpp"
#include "caf/ref_counted.hpp"
#include "caf/span.hpp"
#include "caf/unit.hpp"

namespace caf::flow {

/// Handle to a consumer of items.
template <class T>
class observer {
public:
  /// Internal interface of an `observer`.
  class impl : public disposable::impl {
  public:
    using input_type = T;

    virtual void on_subscribe(subscription sub) = 0;

    virtual void on_next(span<const T> items) = 0;

    virtual void on_complete() = 0;

    virtual void on_error(const error& what) = 0;

    observer as_observer() noexcept {
      return observer{intrusive_ptr<impl>(this)};
    }
  };

  using input_type = T;

  explicit observer(intrusive_ptr<impl> pimpl) noexcept
    : pimpl_(std::move(pimpl)) {
    // nop
  }

  observer& operator=(std::nullptr_t) noexcept {
    pimpl_.reset();
    return *this;
  }

  observer() noexcept = default;
  observer(observer&&) noexcept = default;
  observer(const observer&) noexcept = default;
  observer& operator=(observer&&) noexcept = default;
  observer& operator=(const observer&) noexcept = default;

  disposable as_disposable() const& noexcept {
    return disposable{pimpl_};
  }

  disposable as_disposable() && noexcept {
    return disposable{std::move(pimpl_)};
  }

  /// @pre `valid()`
  void on_complete() {
    pimpl_->on_complete();
  }

  /// @pre `valid()`
  void on_error(const error& what) {
    pimpl_->on_error(what);
  }

  /// @pre `valid()`
  void on_subscribe(subscription sub) {
    pimpl_->on_subscribe(std::move(sub));
  }

  /// @pre `valid()`
  void on_batch(const async::batch& buf) {
    pimpl_->on_batch(buf);
  }

  /// @pre `valid()`
  void on_next(span<const T> items) {
    pimpl_->on_next(items);
  }

  /// Creates a new observer from `Impl`.
  template <class Impl, class... Ts>
  [[nodiscard]] static observer make(Ts&&... xs) {
    static_assert(std::is_base_of_v<impl, Impl>);
    return observer{make_counted<Impl>(std::forward<Ts>(xs)...)};
  }

  bool valid() const noexcept {
    return pimpl_ != nullptr;
  }

  explicit operator bool() const noexcept {
    return valid();
  }

  bool operator!() const noexcept {
    return !valid();
  }

  impl* ptr() {
    return pimpl_.get();
  }

  const impl* ptr() const {
    return pimpl_.get();
  }

  const intrusive_ptr<impl>& as_intrusive_ptr() const& noexcept {
    return pimpl_;
  }

  intrusive_ptr<impl>&& as_intrusive_ptr() && noexcept {
    return std::move(pimpl_);
  }

  void swap(observer& other) {
    pimpl_.swap(other.pimpl_);
  }

private:
  intrusive_ptr<impl> pimpl_;
};

template <class T>
using observer_impl = typename observer<T>::impl;

// -- writing observed values to a buffer --------------------------------------

/// Writes observed values to a bounded buffer.
template <class Buffer>
class buffer_writer_impl : public ref_counted,
                           public observer_impl<typename Buffer::value_type>,
                           public async::producer {
public:
  // -- member types -----------------------------------------------------------

  using buffer_ptr = intrusive_ptr<Buffer>;

  using value_type = typename Buffer::value_type;

  // -- friends ----------------------------------------------------------------

  CAF_INTRUSIVE_PTR_FRIENDS(buffer_writer_impl)

  // -- constructors, destructors, and assignment operators --------------------

  buffer_writer_impl(coordinator* ctx, buffer_ptr buf)
    : ctx_(ctx), buf_(std::move(buf)) {
    CAF_ASSERT(ctx_ != nullptr);
    CAF_ASSERT(buf_ != nullptr);
  }

  ~buffer_writer_impl() {
    if (buf_)
      buf_->close();
  }

  // -- implementation of disposable::impl -------------------------------------

  void dispose() override {
    CAF_LOG_TRACE("");
    on_complete();
  }

  bool disposed() const noexcept override {
    return buf_ == nullptr;
  }

  void ref_disposable() const noexcept final {
    this->ref();
  }

  void deref_disposable() const noexcept final {
    this->deref();
  }

  // -- implementation of observer<T>::impl ------------------------------------

  void on_next(span<const value_type> items) override {
    CAF_LOG_TRACE(CAF_ARG(items));
    if (buf_)
      buf_->push(items);
  }

  void on_complete() override {
    CAF_LOG_TRACE("");
    if (buf_) {
      buf_->close();
      buf_ = nullptr;
      sub_ = nullptr;
    }
  }

  void on_error(const error& what) override {
    CAF_LOG_TRACE(CAF_ARG(what));
    if (buf_) {
      buf_->abort(what);
      buf_ = nullptr;
      sub_ = nullptr;
    }
  }

  void on_subscribe(subscription sub) override {
    CAF_LOG_TRACE("");
    if (buf_ && !sub_) {
      CAF_LOG_DEBUG("add subscription");
      sub_ = std::move(sub);
      sub_.request(buf_->capacity());
    } else {
      CAF_LOG_DEBUG("already have a subscription or buffer no longer valid");
      sub.cancel();
    }
  }

  // -- implementation of async::producer: these may get called concurrently ---

  void on_consumer_ready() override {
    // nop
  }

  void on_consumer_cancel() override {
    CAF_LOG_TRACE("");
    ctx_->schedule_fn([ptr{strong_ptr()}] {
      CAF_LOG_TRACE("");
      ptr->on_cancel();
    });
  }

  void on_consumer_demand(size_t demand) override {
    CAF_LOG_TRACE(CAF_ARG(demand));
    ctx_->schedule_fn([ptr{strong_ptr()}, demand] { //
      CAF_LOG_TRACE(CAF_ARG(demand));
      ptr->on_demand(demand);
    });
  }

  void ref_producer() const noexcept final {
    this->ref();
  }

  void deref_producer() const noexcept final {
    this->deref();
  }

private:
  void on_demand(size_t n) {
    CAF_LOG_TRACE(CAF_ARG(n));
    if (sub_)
      sub_.request(n);
  }

  void on_cancel() {
    CAF_LOG_TRACE("");
    if (sub_) {
      sub_.cancel();
      sub_ = nullptr;
    }
    buf_ = nullptr;
  }

  intrusive_ptr<buffer_writer_impl> strong_ptr() {
    return {this};
  }

  coordinator_ptr ctx_;
  buffer_ptr buf_;
  subscription sub_;
};

} // namespace caf::flow

namespace caf::detail {

template <class OnNextSignature>
struct on_next_trait;

template <class T>
struct on_next_trait<void(T)> {
  using value_type = T;

  template <class F>
  static void apply(F& f, span<const T> items) {
    for (auto&& item : items)
      f(item);
  }
};

template <class T>
struct on_next_trait<void(const T&)> {
  using value_type = T;

  template <class F>
  static void apply(F& f, span<const T> items) {
    for (auto&& item : items)
      f(item);
  }
};

template <class T>
struct on_next_trait<void(span<const T>)> {
  using value_type = T;

  template <class F>
  static void apply(F& f, span<const T> items) {
    f(items);
  }
};

template <class F>
using on_next_trait_t
  = on_next_trait<typename get_callable_trait_t<F>::fun_sig>;

template <class F>
using on_next_value_type = typename on_next_trait_t<F>::value_type;

template <class OnNext, class OnError = unit_t, class OnComplete = unit_t>
class default_observer_impl
  : public ref_counted,
    public flow::observer_impl<on_next_value_type<OnNext>> {
public:
  static_assert(std::is_invocable_v<OnError, const error&>);

  static_assert(std::is_invocable_v<OnComplete>);

  using input_type = on_next_value_type<OnNext>;

  CAF_INTRUSIVE_PTR_FRIENDS(default_observer_impl)

  explicit default_observer_impl(OnNext&& on_next_fn)
    : on_next_(std::move(on_next_fn)) {
    // nop
  }

  default_observer_impl(OnNext&& on_next_fn, OnError&& on_error_fn)
    : on_next_(std::move(on_next_fn)), on_error_(std::move(on_error_fn)) {
    // nop
  }

  default_observer_impl(OnNext&& on_next_fn, OnError&& on_error_fn,
                        OnComplete&& on_complete_fn)
    : on_next_(std::move(on_next_fn)),
      on_error_(std::move(on_error_fn)),
      on_complete_(std::move(on_complete_fn)) {
    // nop
  }

  void ref_disposable() const noexcept final {
    this->ref();
  }

  void deref_disposable() const noexcept final {
    this->deref();
  }

  void on_next(span<const input_type> items) override {
    if (!completed_) {
      on_next_trait_t<OnNext>::apply(on_next_, items);
      sub_.request(items.size());
    }
  }

  void on_error(const error& what) override {
    if (!completed_) {
      on_error_(what);
      sub_ = nullptr;
      completed_ = true;
    }
  }

  void on_complete() override {
    if (!completed_) {
      on_complete_();
      sub_ = nullptr;
      completed_ = true;
    }
  }

  void on_subscribe(flow::subscription sub) override {
    if (!completed_ && !sub_) {
      sub_ = std::move(sub);
      sub_.request(defaults::flow::buffer_size);
    } else {
      sub.cancel();
    }
  }

  void dispose() override {
    if (!completed_) {
      on_complete_();
      if (sub_) {
        sub_.cancel();
        sub_ = nullptr;
      }
      completed_ = true;
    }
  }

  bool disposed() const noexcept override {
    return completed_;
  }

private:
  bool completed_ = false;
  OnNext on_next_;
  OnError on_error_;
  OnComplete on_complete_;
  flow::subscription sub_;
};

} // namespace caf::detail

namespace caf::flow {

template <class OnNext, class OnError, class OnComplete>
auto make_observer(OnNext on_next, OnError on_error, OnComplete on_complete) {
  using impl_type = detail::default_observer_impl<OnNext, OnError, OnComplete>;
  using input_type = typename impl_type::input_type;
  auto ptr = make_counted<impl_type>(std::move(on_next), std::move(on_error),
                                     std::move(on_complete));
  return observer<input_type>{std::move(ptr)};
}

template <class OnNext, class OnError>
auto make_observer(OnNext on_next, OnError on_error) {
  using impl_type = detail::default_observer_impl<OnNext, OnError>;
  using input_type = typename impl_type::input_type;
  auto ptr = make_counted<impl_type>(std::move(on_next), std::move(on_error));
  return observer<input_type>{std::move(ptr)};
}

template <class OnNext>
auto make_observer(OnNext on_next) {
  using impl_type = detail::default_observer_impl<OnNext>;
  using input_type = typename impl_type::input_type;
  auto ptr = make_counted<impl_type>(std::move(on_next));
  return observer<input_type>{std::move(ptr)};
}

} // namespace caf::flow
<commit_msg>Fix demand handling on buffer writers<commit_after>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include "caf/async/batch.hpp"
#include "caf/async/producer.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/disposable.hpp"
#include "caf/error.hpp"
#include "caf/flow/coordinator.hpp"
#include "caf/flow/subscription.hpp"
#include "caf/intrusive_ptr.hpp"
#include "caf/logger.hpp"
#include "caf/make_counted.hpp"
#include "caf/ref_counted.hpp"
#include "caf/span.hpp"
#include "caf/unit.hpp"

namespace caf::flow {

/// Handle to a consumer of items.
template <class T>
class observer {
public:
  /// Internal interface of an `observer`.
  class impl : public disposable::impl {
  public:
    using input_type = T;

    virtual void on_subscribe(subscription sub) = 0;

    virtual void on_next(span<const T> items) = 0;

    virtual void on_complete() = 0;

    virtual void on_error(const error& what) = 0;

    observer as_observer() noexcept {
      return observer{intrusive_ptr<impl>(this)};
    }
  };

  using input_type = T;

  explicit observer(intrusive_ptr<impl> pimpl) noexcept
    : pimpl_(std::move(pimpl)) {
    // nop
  }

  observer& operator=(std::nullptr_t) noexcept {
    pimpl_.reset();
    return *this;
  }

  observer() noexcept = default;
  observer(observer&&) noexcept = default;
  observer(const observer&) noexcept = default;
  observer& operator=(observer&&) noexcept = default;
  observer& operator=(const observer&) noexcept = default;

  disposable as_disposable() const& noexcept {
    return disposable{pimpl_};
  }

  disposable as_disposable() && noexcept {
    return disposable{std::move(pimpl_)};
  }

  /// @pre `valid()`
  void on_complete() {
    pimpl_->on_complete();
  }

  /// @pre `valid()`
  void on_error(const error& what) {
    pimpl_->on_error(what);
  }

  /// @pre `valid()`
  void on_subscribe(subscription sub) {
    pimpl_->on_subscribe(std::move(sub));
  }

  /// @pre `valid()`
  void on_batch(const async::batch& buf) {
    pimpl_->on_batch(buf);
  }

  /// @pre `valid()`
  void on_next(span<const T> items) {
    pimpl_->on_next(items);
  }

  /// Creates a new observer from `Impl`.
  template <class Impl, class... Ts>
  [[nodiscard]] static observer make(Ts&&... xs) {
    static_assert(std::is_base_of_v<impl, Impl>);
    return observer{make_counted<Impl>(std::forward<Ts>(xs)...)};
  }

  bool valid() const noexcept {
    return pimpl_ != nullptr;
  }

  explicit operator bool() const noexcept {
    return valid();
  }

  bool operator!() const noexcept {
    return !valid();
  }

  impl* ptr() {
    return pimpl_.get();
  }

  const impl* ptr() const {
    return pimpl_.get();
  }

  const intrusive_ptr<impl>& as_intrusive_ptr() const& noexcept {
    return pimpl_;
  }

  intrusive_ptr<impl>&& as_intrusive_ptr() && noexcept {
    return std::move(pimpl_);
  }

  void swap(observer& other) {
    pimpl_.swap(other.pimpl_);
  }

private:
  intrusive_ptr<impl> pimpl_;
};

template <class T>
using observer_impl = typename observer<T>::impl;

// -- writing observed values to a buffer --------------------------------------

/// Writes observed values to a bounded buffer.
template <class Buffer>
class buffer_writer_impl : public ref_counted,
                           public observer_impl<typename Buffer::value_type>,
                           public async::producer {
public:
  // -- member types -----------------------------------------------------------

  using buffer_ptr = intrusive_ptr<Buffer>;

  using value_type = typename Buffer::value_type;

  // -- friends ----------------------------------------------------------------

  CAF_INTRUSIVE_PTR_FRIENDS(buffer_writer_impl)

  // -- constructors, destructors, and assignment operators --------------------

  buffer_writer_impl(coordinator* ctx, buffer_ptr buf)
    : ctx_(ctx), buf_(std::move(buf)) {
    CAF_ASSERT(ctx_ != nullptr);
    CAF_ASSERT(buf_ != nullptr);
  }

  ~buffer_writer_impl() {
    if (buf_)
      buf_->close();
  }

  // -- implementation of disposable::impl -------------------------------------

  void dispose() override {
    CAF_LOG_TRACE("");
    on_complete();
  }

  bool disposed() const noexcept override {
    return buf_ == nullptr;
  }

  void ref_disposable() const noexcept final {
    this->ref();
  }

  void deref_disposable() const noexcept final {
    this->deref();
  }

  // -- implementation of observer<T>::impl ------------------------------------

  void on_next(span<const value_type> items) override {
    CAF_LOG_TRACE(CAF_ARG(items));
    if (buf_)
      buf_->push(items);
  }

  void on_complete() override {
    CAF_LOG_TRACE("");
    if (buf_) {
      buf_->close();
      buf_ = nullptr;
      sub_ = nullptr;
    }
  }

  void on_error(const error& what) override {
    CAF_LOG_TRACE(CAF_ARG(what));
    if (buf_) {
      buf_->abort(what);
      buf_ = nullptr;
      sub_ = nullptr;
    }
  }

  void on_subscribe(subscription sub) override {
    CAF_LOG_TRACE("");
    if (buf_ && !sub_) {
      CAF_LOG_DEBUG("add subscription");
      sub_ = std::move(sub);
    } else {
      CAF_LOG_DEBUG("already have a subscription or buffer no longer valid");
      sub.cancel();
    }
  }

  // -- implementation of async::producer: these may get called concurrently ---

  void on_consumer_ready() override {
    // nop
  }

  void on_consumer_cancel() override {
    CAF_LOG_TRACE("");
    ctx_->schedule_fn([ptr{strong_ptr()}] {
      CAF_LOG_TRACE("");
      ptr->on_cancel();
    });
  }

  void on_consumer_demand(size_t demand) override {
    CAF_LOG_TRACE(CAF_ARG(demand));
    ctx_->schedule_fn([ptr{strong_ptr()}, demand] { //
      CAF_LOG_TRACE(CAF_ARG(demand));
      ptr->on_demand(demand);
    });
  }

  void ref_producer() const noexcept final {
    this->ref();
  }

  void deref_producer() const noexcept final {
    this->deref();
  }

private:
  void on_demand(size_t n) {
    CAF_LOG_TRACE(CAF_ARG(n));
    if (sub_)
      sub_.request(n);
  }

  void on_cancel() {
    CAF_LOG_TRACE("");
    if (sub_) {
      sub_.cancel();
      sub_ = nullptr;
    }
    buf_ = nullptr;
  }

  intrusive_ptr<buffer_writer_impl> strong_ptr() {
    return {this};
  }

  coordinator_ptr ctx_;
  buffer_ptr buf_;
  subscription sub_;
};

} // namespace caf::flow

namespace caf::detail {

template <class OnNextSignature>
struct on_next_trait;

template <class T>
struct on_next_trait<void(T)> {
  using value_type = T;

  template <class F>
  static void apply(F& f, span<const T> items) {
    for (auto&& item : items)
      f(item);
  }
};

template <class T>
struct on_next_trait<void(const T&)> {
  using value_type = T;

  template <class F>
  static void apply(F& f, span<const T> items) {
    for (auto&& item : items)
      f(item);
  }
};

template <class T>
struct on_next_trait<void(span<const T>)> {
  using value_type = T;

  template <class F>
  static void apply(F& f, span<const T> items) {
    f(items);
  }
};

template <class F>
using on_next_trait_t
  = on_next_trait<typename get_callable_trait_t<F>::fun_sig>;

template <class F>
using on_next_value_type = typename on_next_trait_t<F>::value_type;

template <class OnNext, class OnError = unit_t, class OnComplete = unit_t>
class default_observer_impl
  : public ref_counted,
    public flow::observer_impl<on_next_value_type<OnNext>> {
public:
  static_assert(std::is_invocable_v<OnError, const error&>);

  static_assert(std::is_invocable_v<OnComplete>);

  using input_type = on_next_value_type<OnNext>;

  CAF_INTRUSIVE_PTR_FRIENDS(default_observer_impl)

  explicit default_observer_impl(OnNext&& on_next_fn)
    : on_next_(std::move(on_next_fn)) {
    // nop
  }

  default_observer_impl(OnNext&& on_next_fn, OnError&& on_error_fn)
    : on_next_(std::move(on_next_fn)), on_error_(std::move(on_error_fn)) {
    // nop
  }

  default_observer_impl(OnNext&& on_next_fn, OnError&& on_error_fn,
                        OnComplete&& on_complete_fn)
    : on_next_(std::move(on_next_fn)),
      on_error_(std::move(on_error_fn)),
      on_complete_(std::move(on_complete_fn)) {
    // nop
  }

  void ref_disposable() const noexcept final {
    this->ref();
  }

  void deref_disposable() const noexcept final {
    this->deref();
  }

  void on_next(span<const input_type> items) override {
    if (!completed_) {
      on_next_trait_t<OnNext>::apply(on_next_, items);
      sub_.request(items.size());
    }
  }

  void on_error(const error& what) override {
    if (!completed_) {
      on_error_(what);
      sub_ = nullptr;
      completed_ = true;
    }
  }

  void on_complete() override {
    if (!completed_) {
      on_complete_();
      sub_ = nullptr;
      completed_ = true;
    }
  }

  void on_subscribe(flow::subscription sub) override {
    if (!completed_ && !sub_) {
      sub_ = std::move(sub);
      sub_.request(defaults::flow::buffer_size);
    } else {
      sub.cancel();
    }
  }

  void dispose() override {
    if (!completed_) {
      on_complete_();
      if (sub_) {
        sub_.cancel();
        sub_ = nullptr;
      }
      completed_ = true;
    }
  }

  bool disposed() const noexcept override {
    return completed_;
  }

private:
  bool completed_ = false;
  OnNext on_next_;
  OnError on_error_;
  OnComplete on_complete_;
  flow::subscription sub_;
};

} // namespace caf::detail

namespace caf::flow {

template <class OnNext, class OnError, class OnComplete>
auto make_observer(OnNext on_next, OnError on_error, OnComplete on_complete) {
  using impl_type = detail::default_observer_impl<OnNext, OnError, OnComplete>;
  using input_type = typename impl_type::input_type;
  auto ptr = make_counted<impl_type>(std::move(on_next), std::move(on_error),
                                     std::move(on_complete));
  return observer<input_type>{std::move(ptr)};
}

template <class OnNext, class OnError>
auto make_observer(OnNext on_next, OnError on_error) {
  using impl_type = detail::default_observer_impl<OnNext, OnError>;
  using input_type = typename impl_type::input_type;
  auto ptr = make_counted<impl_type>(std::move(on_next), std::move(on_error));
  return observer<input_type>{std::move(ptr)};
}

template <class OnNext>
auto make_observer(OnNext on_next) {
  using impl_type = detail::default_observer_impl<OnNext>;
  using input_type = typename impl_type::input_type;
  auto ptr = make_counted<impl_type>(std::move(on_next));
  return observer<input_type>{std::move(ptr)};
}

} // namespace caf::flow
<|endoftext|>