|
22 | 22 | import * as storageModule from './module'; |
23 | 23 |
|
24 | 24 |
|
| 25 | +/** |
| 26 | + * Paths and file names for Blocks Projects |
| 27 | + * |
| 28 | + * Files in a project are stored in a directory whose name is the project name. All files have |
| 29 | + * the extension '.json' and contain JSON text. |
| 30 | + * |
| 31 | + * Project information is stored in a file called 'project.info.json'. |
| 32 | + * |
| 33 | + * Modules |
| 34 | + * Files that contain blocks are called modules. During deployment, a Python file is created for |
| 35 | + * each module. Each Python file contains a Python class definition. |
| 36 | + * There are three types of modules: |
| 37 | + * robot |
| 38 | + * mechanism |
| 39 | + * opmode |
| 40 | + * A project consists of: |
| 41 | + * one project information file named 'project.info.json' |
| 42 | + * one robot module, named 'Robot.robot.json' |
| 43 | + * zero or more mechanisms, with the extension '.mechanism.json' |
| 44 | + * zero or more opmodes, with the extension '.opmode.json' |
| 45 | + * |
| 46 | + * The file path of the project info file is <ProjectName>/project.info.json. |
| 47 | + * The file path of a module is <ProjectName>/<ClassName>.<ModuleType>.json. |
| 48 | + */ |
| 49 | + |
| 50 | +// The class name of the Robot module that is created automatically when a new project is created. |
25 | 51 | export const CLASS_NAME_ROBOT = 'Robot'; |
| 52 | + |
| 53 | +// The class name of the OpMode module that is created automatically when a new project is created. |
26 | 54 | export const CLASS_NAME_TELEOP = 'Teleop'; |
27 | 55 |
|
| 56 | +// The extension of all JSON files is .json. |
28 | 57 | export const JSON_FILE_EXTENSION = '.json'; |
| 58 | + |
| 59 | +// The extension of a downloaded project is .blocks. |
29 | 60 | export const UPLOAD_DOWNLOAD_FILE_EXTENSION = '.blocks'; |
30 | 61 |
|
| 62 | +// The file name of the project info file. |
| 63 | +const PROJECT_INFO_FILE_NAME = 'project.info.json'; |
| 64 | + |
| 65 | +// The file name of the project info file. |
| 66 | +const ROBOT_MODULE_FILE_NAME = 'Robot.robot.json'; |
| 67 | + |
| 68 | +// A project name starts with an uppercase letter, followed by alphanumeric characters. |
31 | 69 | const REGEX_PROJECT_NAME_PART = '[A-Z][A-Za-z0-9]*'; |
| 70 | + |
| 71 | +// A module's class name starts with an uppercase letter, followed by alphanumeric characters. |
32 | 72 | const REGEX_CLASS_NAME_PART = '[A-Z][A-Za-z0-9]*'; |
| 73 | + |
| 74 | +// This regex is used to validate a class name |
| 75 | +const REGEX_CLASS_NAME = '^' + REGEX_CLASS_NAME_PART + '$' |
| 76 | + |
| 77 | +// The module type of a module path is either .robot, .mechanism, or .opmode. |
33 | 78 | const REGEX_MODULE_TYPE_PART = '\.(robot|mechanism|opmode)'; |
34 | | -const REGEX_MODULE_PATH = '^(' + REGEX_PROJECT_NAME_PART + ')/(' + REGEX_CLASS_NAME_PART + ')' + |
| 79 | + |
| 80 | +// This regex is used to match the robot module path in any project. |
| 81 | +export const REGEX_ROBOT_MODULE_PATH = '^' + REGEX_PROJECT_NAME_PART + '/' + escapeRegExp(ROBOT_MODULE_FILE_NAME) + '$'; |
| 82 | + |
| 83 | +// This regex is used to extract the project name and file name from a file path. |
| 84 | +const REGEX_FILE_PATH = '^(' + REGEX_PROJECT_NAME_PART + ')/(.*' + escapeRegExp(JSON_FILE_EXTENSION) + ')$'; |
| 85 | + |
| 86 | +// This regex is used to extract the class name from a module path. |
| 87 | +const REGEX_MODULE_PATH = '^' + REGEX_PROJECT_NAME_PART + '/(' + REGEX_CLASS_NAME_PART + ')' + |
35 | 88 | REGEX_MODULE_TYPE_PART + escapeRegExp(JSON_FILE_EXTENSION) + '$'; |
36 | | -const REGEX_MODULE_PATH_TO_FILE_NAME = '^' + REGEX_PROJECT_NAME_PART + '/(' + REGEX_CLASS_NAME_PART + |
37 | | - REGEX_MODULE_TYPE_PART + escapeRegExp(JSON_FILE_EXTENSION) + ')$'; |
38 | | -const REGEX_FILE_NAME = '^(' + REGEX_CLASS_NAME_PART + ')' + |
| 89 | + |
| 90 | +// This regex is used to extract the class name from a module file name. |
| 91 | +const REGEX_MODULE_FILE_NAME = '^(' + REGEX_CLASS_NAME_PART + ')' + |
39 | 92 | REGEX_MODULE_TYPE_PART + escapeRegExp(JSON_FILE_EXTENSION) + '$'; |
40 | 93 |
|
41 | 94 | /** |
42 | 95 | * Returns true if the given name is a valid class name. |
43 | 96 | */ |
44 | 97 | export function isValidClassName(name: string): boolean { |
45 | | - if (name) { |
46 | | - return new RegExp('^' + REGEX_CLASS_NAME_PART + '$').test(name); |
47 | | - } |
48 | | - return false; |
| 98 | + return new RegExp(REGEX_CLASS_NAME).test(name); |
49 | 99 | } |
50 | 100 |
|
51 | 101 | /** |
@@ -84,88 +134,131 @@ export function snakeCaseToPascalCase(snakeCaseName: string): string { |
84 | 134 | } |
85 | 135 |
|
86 | 136 | /** |
87 | | - * Returns the module path regex pattern for modules in the given project. |
| 137 | + * Returns a regex pattern that matches all file paths in the given project. |
| 138 | + */ |
| 139 | +export function makeFilePathRegexPattern(projectName: string): string { |
| 140 | + return '^' + escapeRegExp(projectName) + '/' + |
| 141 | + '.*' + escapeRegExp(JSON_FILE_EXTENSION) + '$'; |
| 142 | +} |
| 143 | + |
| 144 | +/** |
| 145 | + * Returns a regex pattern that matches all module paths in the given project. |
88 | 146 | */ |
89 | 147 | export function makeModulePathRegexPattern(projectName: string): string { |
90 | | - return '^' + escapeRegExp(projectName) + '/' + REGEX_CLASS_NAME_PART + |
91 | | - REGEX_MODULE_TYPE_PART + escapeRegExp(JSON_FILE_EXTENSION) + '$'; |
| 148 | + return '^' + escapeRegExp(projectName) + '/' + |
| 149 | + REGEX_CLASS_NAME_PART + REGEX_MODULE_TYPE_PART + escapeRegExp(JSON_FILE_EXTENSION) + '$'; |
92 | 150 | } |
93 | 151 |
|
| 152 | +/** |
| 153 | + * Escapes the given text so it can be used literally in a regular expression. |
| 154 | + */ |
94 | 155 | function escapeRegExp(text: string): string { |
95 | 156 | return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); |
96 | 157 | } |
97 | 158 |
|
| 159 | +/** |
| 160 | + * Returns the file path for the given project name and file name. |
| 161 | + */ |
| 162 | +export function makeFilePath(projectName: string, fileName: string): string { |
| 163 | + return projectName + '/' + fileName; |
| 164 | +} |
| 165 | + |
| 166 | +/** |
| 167 | + * Returns the project info path for the given project name. |
| 168 | + */ |
| 169 | +export function makeProjectInfoPath(projectName: string): string { |
| 170 | + return makeFilePath(projectName, PROJECT_INFO_FILE_NAME); |
| 171 | +} |
| 172 | + |
98 | 173 | /** |
99 | 174 | * Returns the module path for the given project name and class name. |
100 | 175 | */ |
101 | 176 | export function makeModulePath( |
102 | 177 | projectName: string, className: string, moduleType: storageModule.ModuleType): string { |
103 | | - return projectName + '/' + className + '.' + moduleType + JSON_FILE_EXTENSION; |
| 178 | + return makeFilePath(projectName, className + '.' + moduleType + JSON_FILE_EXTENSION); |
104 | 179 | } |
105 | 180 |
|
106 | 181 | /** |
107 | | - * Returns the robot module path for the given project names. |
| 182 | + * Returns the robot module path for the given project name. |
108 | 183 | */ |
109 | 184 | export function makeRobotPath(projectName: string): string { |
110 | 185 | return makeModulePath(projectName, CLASS_NAME_ROBOT, storageModule.ModuleType.ROBOT); |
111 | 186 | } |
112 | 187 |
|
113 | 188 | /** |
114 | | - * Returns the project name for given module path. |
| 189 | + * Returns the project name for given file path. |
115 | 190 | */ |
116 | | -export function getProjectName(modulePath: string): string { |
117 | | - const regex = new RegExp(REGEX_MODULE_PATH); |
118 | | - const result = regex.exec(modulePath) |
| 191 | +export function getProjectName(filePath: string): string { |
| 192 | + const regex = new RegExp(REGEX_FILE_PATH); |
| 193 | + const result = regex.exec(filePath) |
119 | 194 | if (!result) { |
120 | | - throw new Error('Unable to extract the project name from "' + modulePath + '"'); |
| 195 | + throw new Error('Unable to extract the project name from "' + filePath + '"'); |
121 | 196 | } |
122 | 197 | return result[1]; |
123 | 198 | } |
124 | 199 |
|
125 | 200 | /** |
126 | | - * Returns the file name for given module path. |
| 201 | + * Returns the file name for given file path. |
127 | 202 | */ |
128 | | -export function getFileName(modulePath: string): string { |
129 | | - const regex = new RegExp(REGEX_MODULE_PATH_TO_FILE_NAME); |
130 | | - const result = regex.exec(modulePath) |
| 203 | +export function getFileName(filePath: string): string { |
| 204 | + const regex = new RegExp(REGEX_FILE_PATH); |
| 205 | + const result = regex.exec(filePath) |
131 | 206 | if (!result) { |
132 | | - throw new Error('Unable to extract the project name from "' + modulePath + '"'); |
| 207 | + throw new Error('Unable to extract the file name from "' + filePath + '"'); |
133 | 208 | } |
134 | | - return result[1]; |
| 209 | + return result[2]; |
| 210 | +} |
| 211 | + |
| 212 | +/** |
| 213 | + * Returns true if the given file name is a valid module file name. |
| 214 | + */ |
| 215 | +export function isValidModuleFileName(fileName: string): boolean { |
| 216 | + return new RegExp(REGEX_MODULE_FILE_NAME).test(fileName); |
| 217 | +} |
| 218 | + |
| 219 | +/** |
| 220 | + * Returns true if the given file name is a valid project info file name. |
| 221 | + */ |
| 222 | +export function isValidProjectInfoFileName(fileName: string): boolean { |
| 223 | + if (fileName === PROJECT_INFO_FILE_NAME) { |
| 224 | + return true; |
| 225 | + } |
| 226 | + return false; |
135 | 227 | } |
136 | 228 |
|
| 229 | + |
137 | 230 | /** |
138 | | - * Returns the class name for given module path or file name. |
| 231 | + * Returns the class name for given module path or module file name. |
139 | 232 | */ |
140 | | -export function getClassName(modulePathOrFileName: string): string { |
| 233 | +export function getClassName(modulePathOrModuleFileName: string): string { |
141 | 234 | let regex = new RegExp(REGEX_MODULE_PATH); |
142 | | - let result = regex.exec(modulePathOrFileName); |
| 235 | + let result = regex.exec(modulePathOrModuleFileName); |
143 | 236 | if (result) { |
144 | | - return result[2]; |
| 237 | + return result[1]; |
145 | 238 | } |
146 | | - regex = new RegExp(REGEX_FILE_NAME); |
147 | | - result = regex.exec(modulePathOrFileName); |
| 239 | + regex = new RegExp(REGEX_MODULE_FILE_NAME); |
| 240 | + result = regex.exec(modulePathOrModuleFileName); |
148 | 241 | if (result) { |
149 | 242 | return result[1]; |
150 | 243 | } |
151 | | - throw new Error('Unable to extract the class name from "' + modulePathOrFileName + '"'); |
| 244 | + throw new Error('Unable to extract the class name from "' + modulePathOrModuleFileName + '"'); |
152 | 245 | } |
153 | 246 |
|
154 | 247 | /** |
155 | | - * Returns the module type for given module path or file name. |
| 248 | + * Returns the module type for given module path or module file name. |
156 | 249 | */ |
157 | | -export function getModuleType(modulePathOrFileName: string): storageModule.ModuleType { |
| 250 | +export function getModuleType(modulePathOrModuleFileName: string): storageModule.ModuleType { |
158 | 251 | let regex = new RegExp(REGEX_MODULE_PATH); |
159 | | - let result = regex.exec(modulePathOrFileName); |
| 252 | + let result = regex.exec(modulePathOrModuleFileName); |
160 | 253 | if (result) { |
161 | | - return storageModule.stringToModuleType(result[3]); |
| 254 | + return storageModule.stringToModuleType(result[2]); |
162 | 255 | } |
163 | | - regex = new RegExp(REGEX_FILE_NAME); |
164 | | - result = regex.exec(modulePathOrFileName); |
| 256 | + regex = new RegExp(REGEX_MODULE_FILE_NAME); |
| 257 | + result = regex.exec(modulePathOrModuleFileName); |
165 | 258 | if (result) { |
166 | 259 | return storageModule.stringToModuleType(result[2]); |
167 | 260 | } |
168 | | - throw new Error('Unable to extract the module type from "' + modulePathOrFileName + '"'); |
| 261 | + throw new Error('Unable to extract the module type from "' + modulePathOrModuleFileName + '"'); |
169 | 262 | } |
170 | 263 |
|
171 | 264 | /** |
|
0 commit comments