1 /*
2  * RVersionSettings.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant
7  * to the terms of a commercial license agreement with RStudio, then
8  * this program is licensed to you under the terms of version 3 of the
9  * GNU Affero General Public License. This program is distributed WITHOUT
10  * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
11  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
12  * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
13  *
14  */
15 
16 #ifndef SESSION_SESSION_RVERSION_SETTINGS_HPP
17 #define SESSION_SESSION_RVERSION_SETTINGS_HPP
18 
19 // header-only file for access to r version settings from many contexts
20 
21 #include <core/SharedSettings.hpp>
22 
23 #include <session/SessionScopes.hpp>
24 
25 #define kRVersionSettings              "rversion-settings"
26 #define kSettingDefaultRVersion        "defaultRVersion"
27 #define kSettingDefaultRVersionHome    "defaultRVersionHome"
28 #define kSettingDefaultRVersionLabel   "defaultRVersionLabel"
29 #define kRestoreProjectRVersionFlag    "restoreProjectRVersion"
30 #define kRVersionSuffix                "-RVersion"
31 #define kRVersionHomeSuffix            "-RVersionHome"
32 #define kRVersionLabelSuffix           "-RVersionLabel"
33 #define kRVersionProjectFile           "RVersion"
34 
35 namespace rstudio {
36 namespace session {
37 
38 class RVersionSettings : public core::SharedSettings
39 {
40 public:
RVersionSettings(const core::FilePath & userScratchPath,const core::FilePath & sharedStoragePath)41    explicit RVersionSettings(const core::FilePath& userScratchPath,
42                              const core::FilePath& sharedStoragePath)
43       : core::SharedSettings(rVersionSettingsPath(userScratchPath)),
44         userScratchPath_(userScratchPath),
45         sharedStoragePath_(sharedStoragePath)
46    {
47    }
48 
defaultRVersion()49    std::string defaultRVersion()
50    {
51       return readSetting(kSettingDefaultRVersion);
52    }
53 
defaultRVersionHome()54    std::string defaultRVersionHome()
55    {
56       return readSetting(kSettingDefaultRVersionHome);
57    }
58 
defaultRVersionLabel()59    std::string defaultRVersionLabel()
60    {
61       return readSetting(kSettingDefaultRVersionLabel);
62    }
63 
setDefaultRVersion(const std::string & version,const std::string & versionHome,const std::string & versionLabel)64    void setDefaultRVersion(const std::string& version,
65                            const std::string& versionHome,
66                            const std::string& versionLabel)
67    {
68       writeSetting(kSettingDefaultRVersion, version);
69       writeSetting(kSettingDefaultRVersionHome, versionHome);
70       writeSetting(kSettingDefaultRVersionLabel, versionLabel);
71    }
72 
restoreProjectRVersion()73    bool restoreProjectRVersion()
74    {
75       return readSetting(kRestoreProjectRVersionFlag) != "0";
76    }
77 
setRestoreProjectRVersion(bool restoreProjectRVersion)78    void setRestoreProjectRVersion(bool restoreProjectRVersion)
79    {
80       writeSetting(kRestoreProjectRVersionFlag, restoreProjectRVersion ? "1" : "0");
81    }
82 
setProjectLastRVersion(const std::string & projectDir,const core::FilePath & sharedProjectScratchPath,const std::string & version,const std::string & versionHome,const std::string & versionLabel)83    void setProjectLastRVersion(const std::string& projectDir,
84                                const core::FilePath& sharedProjectScratchPath,
85                                const std::string& version,
86                                const std::string& versionHome,
87                                const std::string& versionLabel)
88    {
89       // get a project id
90       core::r_util::FilePathToProjectId filePathToProjectId =
91                            session::filePathToProjectId(userScratchPath_,
92                                                         sharedStoragePath_);
93       core::r_util::ProjectId projectId = filePathToProjectId(projectDir);
94 
95       // save the version
96       writeProjectSetting(projectId.asString(), kRVersionSuffix, version);
97       writeProjectSetting(projectId.asString(), kRVersionHomeSuffix, versionHome);
98       writeProjectSetting(projectId.asString(), kRVersionLabelSuffix, versionLabel);
99 
100       // save R version in the project itself; used as a hint on preferred R version
101       // when opening a project for the first time on a different machine/container
102       if (!sharedProjectScratchPath.isEmpty())
103       {
104          writeSettingToPath(sharedProjectScratchPath, kRVersionProjectFile, version);
105       }
106    }
107 
readProjectLastRVersion(const std::string & projectDir,const core::FilePath & sharedProjectScratchPath,std::string * pVersion,std::string * pVersionHome,std::string * pVersionLabel)108    void readProjectLastRVersion(const std::string& projectDir,
109                                 const core::FilePath& sharedProjectScratchPath,
110                                 std::string* pVersion,
111                                 std::string* pVersionHome,
112                                 std::string* pVersionLabel)
113    {
114       // get a project id
115       core::r_util::FilePathToProjectId filePathToProjectId =
116                            session::filePathToProjectId(userScratchPath_,
117                                                         sharedStoragePath_);
118       core::r_util::ProjectId projectId = filePathToProjectId(projectDir);
119 
120       *pVersion = readProjectSetting(projectId.asString(),
121                                      kRVersionSuffix);
122       *pVersionHome = readProjectSetting(projectId.asString(),
123                                          kRVersionHomeSuffix);
124       *pVersionLabel = readProjectSetting(projectId.asString(),
125                                           kRVersionLabelSuffix);
126 
127       // if no local setting for R version, check for hint in .Rproj.user
128       if ((pVersion->empty() || pVersionHome->empty()) && !sharedProjectScratchPath.isEmpty())
129       {
130          *pVersion = readSettingFromPath(sharedProjectScratchPath, kRVersionProjectFile);
131          pVersionHome->clear();
132       }
133    }
134 
135 private:
writeProjectSetting(const std::string & projectId,const char * name,const std::string & value)136    void writeProjectSetting(const std::string& projectId,
137                             const char* name,
138                             const std::string& value)
139    {
140       writeSetting((projectId + name).c_str(), value);
141    }
142 
readProjectSetting(const std::string & projectId,const char * name)143    std::string readProjectSetting(const std::string& projectId,
144                                   const char* name)
145 
146    {
147       return readSetting((projectId + name).c_str());
148    }
149 
150 private:
151 
rVersionSettingsPath(const core::FilePath & userScratchPath)152    static core::FilePath rVersionSettingsPath(
153                      const core::FilePath& userScratchPath)
154    {
155       using namespace rstudio::core;
156       FilePath settingsPath = userScratchPath.completePath(kRVersionSettings);
157       Error error = settingsPath.ensureDirectory();
158       if (error)
159          LOG_ERROR(error);
160 
161       return settingsPath;
162    }
163 
164    core::FilePath userScratchPath_;
165    core::FilePath sharedStoragePath_;
166 };
167 
168 
169 } // namespace session
170 } // namespace rstudio
171 
172 #endif // SESSION_SESSION_RVERSION_SETTINGS_HPP
173