libmoost
/home/mhx/git/github/libmoost/include/moost/service/option_validator.hpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 #ifndef FM_LAST_MOOST_SERVICE_OPTION_VALIDATOR_H_
00029 #define FM_LAST_MOOST_SERVICE_OPTION_VALIDATOR_H_
00030 
00035 #include <string>
00036 
00037 #include <boost/program_options.hpp>
00038 #include <boost/noncopyable.hpp>
00039 #include <boost/filesystem.hpp>
00040 #include <boost/shared_ptr.hpp>
00041 #include <boost/regex.hpp>
00042 
00043 #include "../utils/foreach.hpp"
00044 #include "../utils/assert.hpp"
00045 
00046 namespace moost { namespace service {
00047 
00048 namespace validator {
00049 
00050 typedef std::map<std::string, std::string> constraints_map_t;
00051 
00052 class base
00053 {
00054 private:
00055    std::string m_longopt;
00056    std::string m_desc;
00057    bool m_mandatory;
00058 
00059 public:
00060    base()
00061      : m_mandatory(false)
00062    {
00063    }
00064 
00065    virtual ~base()
00066    {
00067    }
00068 
00069    virtual void validate(const boost::program_options::variables_map& vm, const constraints_map_t& constraints) const
00070    {
00071       if (m_mandatory && vm.count(longopt()) == 0)
00072       {
00073          throw std::runtime_error("please specify the " + desc() + " with --" + longopt());
00074       }
00075 
00076       operator()(vm, constraints);
00077    }
00078 
00079    virtual void operator()(const boost::program_options::variables_map& vm, const constraints_map_t& constraints) const = 0;
00080 
00081    base *mandatory(bool mandatory = true)
00082    {
00083       m_mandatory = mandatory;
00084       return this;
00085    }
00086 
00087    void set_option(const std::string& longopt, const std::string& desc)
00088    {
00089       m_longopt = longopt;
00090       m_desc = desc;
00091    }
00092 
00093    const std::string& longopt() const
00094    {
00095       return m_longopt;
00096    }
00097 
00098    const std::string& desc() const
00099    {
00100       return m_desc;
00101    }
00102 };
00103 
00104 template <typename T>
00105 class typed_base : public base
00106 {
00107 private:
00108    const T& m_data;
00109 
00110 public:
00111    typed_base(const T& data)
00112      : m_data(data)
00113    {
00114    }
00115 
00116    const T& data() const
00117    {
00118       return m_data;
00119    }
00120 };
00121 
00122 class cfile : public typed_base<std::string>
00123 {
00124 private:
00125    const bool m_must_exist;
00126 
00127 public:
00128    cfile(const std::string& file, bool must_exist)
00129      : typed_base<std::string>(file)
00130      , m_must_exist(must_exist)
00131    {
00132    }
00133 
00134    void operator()(const boost::program_options::variables_map& vm, const constraints_map_t& constraints) const
00135    {
00136       if (vm.count(longopt()) > 0)
00137       {
00138          if (m_must_exist && !boost::filesystem::exists(boost::filesystem::path(data())))
00139          {
00140             throw std::runtime_error("the " + desc() + " specified with --" + longopt() + " does not exist: " + data());
00141          }
00142 
00143          constraints_map_t::const_iterator it = constraints.find("absolute_filenames");
00144 
00145          if (it != constraints.end() && boost::lexical_cast<bool>(it->second))
00146          {
00147             moost::utils::assert_absolute_path(data(), desc());
00148          }
00149       }
00150    }
00151 };
00152 
00153 inline boost::shared_ptr<base>
00154 file(const std::string& file, bool must_exist = true)
00155 {
00156    return boost::shared_ptr<base>(new cfile(file, must_exist));
00157 }
00158 
00159 template <typename T>
00160 class cnumber : public typed_base<T>
00161 {
00162 private:
00163    const T m_min;
00164    const T m_max;
00165 
00166 public:
00167    cnumber(const T& num, const T& min, const T& max)
00168      : typed_base<T>(num)
00169      , m_min(min)
00170      , m_max(max)
00171    {
00172    }
00173 
00174    void operator()(const boost::program_options::variables_map& vm, const constraints_map_t&) const
00175    {
00176       if (vm.count(this->longopt()) > 0)
00177       {
00178          if (this->data() < m_min || this->data() > m_max)
00179          {
00180             throw std::runtime_error("the " + this->desc() + " specified with --" + this->longopt() + " is out of range");
00181          }
00182       }
00183    }
00184 };
00185 
00186 template <typename T>
00187 inline boost::shared_ptr<base>
00188 number(const T& num, const T& min = std::numeric_limits<T>::min(), const T& max = std::numeric_limits<T>::max())
00189 {
00190    return boost::shared_ptr<base>(new cnumber<T>(num, min, max));
00191 }
00192 
00193 class cregex : public typed_base<std::string>
00194 {
00195 private:
00196    const std::string m_regex;
00197    boost::smatch *m_results;
00198    boost::regex_constants::match_flag_type m_match_flags;
00199 
00200 public:
00201    cregex(const std::string& text, const std::string& regex, boost::smatch *results,
00202          boost::regex_constants::match_flag_type match_flags)
00203      : typed_base<std::string>(text)
00204      , m_regex(regex)
00205      , m_results(results)
00206      , m_match_flags(match_flags)
00207    {
00208    }
00209 
00210    void operator()(const boost::program_options::variables_map& vm, const constraints_map_t&) const
00211    {
00212       if (vm.count(longopt()) > 0)
00213       {
00214          boost::regex rx(m_regex);
00215 
00216          if (!(m_results ? boost::regex_match(data(), *m_results, rx, m_match_flags) : boost::regex_match(data(), rx)))
00217          {
00218             throw std::runtime_error("the " + desc() + " specified with --" + longopt() + " doesn't match its specification: " + data());
00219          }
00220       }
00221    }
00222 };
00223 
00224 inline boost::shared_ptr<base>
00225 regex(const std::string& text, const std::string& regex, boost::smatch *results = 0,
00226       boost::regex_constants::match_flag_type match_flags = boost::regex_constants::match_default)
00227 {
00228    return boost::shared_ptr<base>(new cregex(text, regex, results, match_flags));
00229 }
00230 
00231 }
00232 
00233 class option_validator : public boost::noncopyable
00234 {
00235 private:
00236    std::vector< boost::shared_ptr<validator::base const> > m_validators;
00237 
00238 public:
00239    void add(boost::shared_ptr<validator::base const> validator)
00240    {
00241       m_validators.push_back(validator);
00242    }
00243 
00244    void operator()(const boost::program_options::variables_map& vm, const validator::constraints_map_t& constraints) const
00245    {
00246       foreach(boost::shared_ptr<validator::base const> validator, m_validators)
00247       {
00248          validator->validate(vm, constraints);
00249       }
00250    }
00251 };
00252 
00253 } }
00254 
00255 #endif