libmoost
|
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