[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/pearsonca/rstudio/master/src/cpp/core/ConfigProfile.cpp [Back]  [Original]

/*
 * ConfigProfile.cpp
 *
 * Copyright (C) 2018 by RStudio, PBC
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include 
#include 
#include 

#include 

#include 

namespace rstudio {
namespace core {

using namespace boost::property_tree;

Error ConfigProfile::validateParam(const std::string& paramName,
                                   const std::string& paramValue) const
{
   ValidatorMap::const_iterator iter = validators_.find(paramName);

   if (iter == validators_.end())
      return Success();

   const ValidatorFunc& validator = iter->second;

   std::string errorMessage;
   if (!validator(paramValue, &errorMessage))
   {
      return systemError(boost::system::errc::protocol_error,
                         "Param " + paramName + " is not valid: " + errorMessage,
                         ERROR_LOCATION);
   }

   return Success();
}

Error ConfigProfile::load(const FilePath& filePath)
{
   std::string contents;

   Error error = readStringFromFile(filePath, &contents);
   if (error)
      return error;

   error = parseString(contents);
   if (error)
   {
      std::string description = error.getProperty("description");
      description += " in file " + filePath.getAbsolutePath();
      error.addOrUpdateProperty("description", description);
   }

   return error;
}

Error ConfigProfile::parseString(const std::string& profileStr)
{
   std::istringstream stream{profileStr};

   // parse the profile (ini syntax)
   ptree profileTree;
   try
   {
      ini_parser::read_ini(stream, profileTree);
   }
   catch (const ini_parser_error& error)
   {
      return systemError(boost::system::errc::protocol_error,
                         "Could not parse config profile: " + error.message(),
                         ERROR_LOCATION);
   }

   // build section overrides
   std::vector levels;
   for (const ptree::value_type& child : profileTree)
   {
      boost::optional matchingLevel;
      for (const Level& level : sections_)
      {
         // if the section level starts with the defined section name
         // from a prior call to addSections, we have a matching section level
         if (boost::algorithm::starts_with(child.first, level.second))
         {
            matchingLevel = level;
            break;
         }
      }

      if (!matchingLevel)
      {
         // no matching section level - it was not specified and is thus
         // an erroneous section
         return systemError(boost::system::errc::protocol_error,
                            "Invalid config section " + child.first,
                            ERROR_LOCATION);
      }

      std::map values;
      for (const ptree::value_type val : child.second)
      {
         // check to see if the parameter within the section has been registered
         const std::string& paramName = val.first;

         DefaultParamValuesMap::const_iterator defaultIter = defaultValues_.find(paramName);
         if (defaultIter == defaultValues_.end())
         {
            // we require every param to have a default value / be registered so error out here
            return systemError(boost::system::errc::protocol_error,
                               "Unknown param " + paramName + " specified",
                               ERROR_LOCATION);
         }

         std::string paramValue = val.second.get_value();

         Error error = validateParam(paramName, paramValue);
         if (error)
            return error;

         values[paramName] = paramValue;
      }

      std::string levelValue = child.first.substr(matchingLevel.get().second.size());
      levels.push_back({{matchingLevel.get().first, levelValue}, values});
   }

   // stable sort the levels in ascending level number
   // this will preserve the order of same-level section overrides
   std::stable_sort(
            levels.begin(),
            levels.end(),
            [](const LevelValues& a, const LevelValues& b) { return a.first.first < b.first.first; });

   // assign the temporary variable levels to the class member
   // this assures that we can safely call this method multiple times, preserving the last
   // good configuration in case we error out above due to invalid configuration
   levels_ = levels;

   return Success();
}

std::vector ConfigProfile::getLevelNames(uint32_t level) const
{
   std::vector levelNames;

   for (const LevelValues& value : levels_)
   {
      if (value.first.first == level)
         levelNames.push_back(value.first.second);
   }

   return levelNames;
}

} // core
} // namespace rstudio

Web Proxy Viewer  |  New URL  |  Original Page