Skip to content
Snippets Groups Projects
clipp.h 188 KiB
Newer Older
  • Learn to ignore specific revisions
  • public:
        //---------------------------------------------------------------
        using value_type     = section;
        using const_iterator = section_store::const_iterator;
        using size_type      = section_store::size_type;
    
    
        //---------------------------------------------------------------
        man_page&
        append_section(string title, string content)
        {
            sections_.emplace_back(std::move(title), std::move(content));
            return *this;
        }
        //-----------------------------------------------------
        man_page&
        prepend_section(string title, string content)
        {
            sections_.emplace(sections_.begin(),
                              std::move(title), std::move(content));
            return *this;
        }
    
    
        //---------------------------------------------------------------
        const section& operator [] (size_type index) const noexcept {
            return sections_[index];
        }
    
        //---------------------------------------------------------------
        size_type size() const noexcept { return sections_.size(); }
    
        bool empty() const noexcept { return sections_.empty(); }
    
    
        //---------------------------------------------------------------
        const_iterator begin() const noexcept { return sections_.begin(); }
        const_iterator end()   const noexcept { return sections_.end(); }
    
    
        //---------------------------------------------------------------
        man_page& program_name(const string& n) {
            progName_ = n;
            return *this;
        }
        man_page& program_name(string&& n) {
            progName_ = std::move(n);
            return *this;
        }
        const string& program_name() const noexcept {
            return progName_;
        }
    
    
        //---------------------------------------------------------------
        man_page& section_row_spacing(int rows) {
            sectionSpc_ = rows > 0 ? rows : 0;
            return *this;
        }
        int section_row_spacing() const noexcept { return sectionSpc_; }
    
    
    private:
        int sectionSpc_ = 1;
        section_store sections_;
        string progName_;
    };
    
    
    
    /*************************************************************************//**
     *
     * @brief generates man sections from command line parameters
     *        with sections "synopsis" and "options"
     *
     *****************************************************************************/
    inline man_page
    make_man_page(const group& params,
                  doc_string progname = "",
                  const doc_formatting& fmt = doc_formatting{})
    {
        man_page man;
        man.append_section("SYNOPSIS", usage_lines(params,progname,fmt).str());
        man.append_section("OPTIONS", documentation(params,fmt).str());
        return man;
    }
    
    
    
    /*************************************************************************//**
     *
     * @brief   generates man page based on command line parameters
     *
     *****************************************************************************/
    template<class OStream>
    OStream&
    operator << (OStream& os, const man_page& man)
    {
        bool first = true;
        const auto secSpc = doc_string(man.section_row_spacing() + 1, '\n');
        for(const auto& section : man) {
            if(!section.content().empty()) {
                if(first) first = false; else os << secSpc;
                if(!section.title().empty()) os << section.title() << '\n';
                os << section.content();
            }
        }
        os << '\n';
        return os;
    }
    
    
    
    
    
    /*************************************************************************//**
     *
     * @brief printing methods for debugging command line interfaces
     *
     *****************************************************************************/
    namespace debug {
    
    
    /*************************************************************************//**
     *
     * @brief prints first flag or value label of a parameter
     *
     *****************************************************************************/
    inline doc_string doc_label(const parameter& p)
    {
        if(!p.flags().empty()) return p.flags().front();
        if(!p.label().empty()) return p.label();
        return doc_string{"<?>"};
    }
    
    inline doc_string doc_label(const group&)
    {
        return "<group>";
    }
    
    inline doc_string doc_label(const pattern& p)
    {
        return p.is_group() ? doc_label(p.as_group()) : doc_label(p.as_param());
    }
    
    
    /*************************************************************************//**
     *
     * @brief prints parsing result
     *
     *****************************************************************************/
    template<class OStream>
    void print(OStream& os, const parsing_result& result)
    {
        for(const auto& m : result) {
            os << "#" << m.index() << " " << m.arg() << " -> ";
            auto p = m.param();
            if(p) {
                os << doc_label(*p) << " \t";
                if(m.repeat() > 0) {
                    os << (m.bad_repeat() ? "[bad repeat " : "[repeat ")
                       <<  m.repeat() << "]";
                }
                if(m.blocked())  os << " [blocked]";
                if(m.conflict()) os << " [conflict]";
                os << '\n';
            }
            else {
                os << " [unmapped]\n";
            }
        }
    
        for(const auto& m : result.missing()) {
            auto p = m.param();
            if(p) {
                os << doc_label(*p) << " \t";
                os << " [missing after " << m.after_index() << "]\n";
            }
        }
    }
    
    
    /*************************************************************************//**
     *
     * @brief prints parameter label and some properties
     *
     *****************************************************************************/
    template<class OStream>
    void print(OStream& os, const parameter& p)
    {
        if(p.blocking()) os << '!';
        if(!p.required()) os << '[';
        os << doc_label(p);
        if(p.repeatable()) os << "...";
        if(!p.required()) os << "]";
    }
    
    
    //-------------------------------------------------------------------
    template<class OStream>
    void print(OStream& os, const group& g, int level = 0);
    
    
    /*************************************************************************//**
     *
     * @brief prints group or parameter; uses indentation
     *
     *****************************************************************************/
    template<class OStream>
    void print(OStream& os, const pattern& param, int level = 0)
    {
        if(param.is_group()) {
            print(os, param.as_group(), level);
        }
        else {
            os << doc_string(4*level, ' ');
            print(os, param.as_param());
        }
    }
    
    
    /*************************************************************************//**
     *
     * @brief prints group and its contents; uses indentation
     *
     *****************************************************************************/
    template<class OStream>
    void print(OStream& os, const group& g, int level)
    {
        auto indent = doc_string(4*level, ' ');
        os << indent;
        if(g.blocking()) os << '!';
        if(g.joinable()) os << 'J';
        os << (g.exclusive() ? "(|\n" : "(\n");
        for(const auto& p : g) {
            print(os, p, level+1);
        }
        os << '\n' << indent << (g.exclusive() ? "|)" : ")");
        if(g.repeatable()) os << "...";
        os << '\n';
    }
    
    
    } // namespace debug
    } //namespace clipp
    
    #endif