readme-builder 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +125 -0
- data/bin/readme-builder +3 -0
- data/ext/readme_generator/binding.cpp +61 -0
- data/ext/readme_generator/extconf.rb +60 -0
- data/ext/readme_generator/include/build_detector.hpp +44 -0
- data/ext/readme_generator/include/language_detector.hpp +47 -0
- data/ext/readme_generator/include/license.hpp +61 -0
- data/ext/readme_generator/include/readme_builder.hpp +62 -0
- data/ext/readme_generator/src/build_detector.cpp +210 -0
- data/ext/readme_generator/src/language_detector.cpp +199 -0
- data/ext/readme_generator/src/license.cpp +197 -0
- data/ext/readme_generator/src/main.cpp +40 -0
- data/ext/readme_generator/src/readme_builder.cpp +226 -0
- data/lib/readme_builder/version.rb +4 -0
- data/lib/readme_builder.rb +31 -0
- metadata +104 -0
@@ -0,0 +1,199 @@
|
|
1
|
+
/**
|
2
|
+
* @file language_detector.cpp
|
3
|
+
* @brief Programming language detection system
|
4
|
+
* @details Implements a sophisticated system for detecting programming languages
|
5
|
+
* used in a project by analyzing file extensions and content patterns.
|
6
|
+
* Supports multiple detection strategies including extension matching
|
7
|
+
* and content-based analysis.
|
8
|
+
*
|
9
|
+
* @author Generated by README Builder
|
10
|
+
* @date 2025-02-17
|
11
|
+
*
|
12
|
+
* @note This implementation supports extensibility through custom language
|
13
|
+
* detection rules and pattern matching.
|
14
|
+
*/
|
15
|
+
|
16
|
+
|
17
|
+
#include "../include/language_detector.hpp"
|
18
|
+
#include <iostream>
|
19
|
+
#include <fstream>
|
20
|
+
#include <vector>
|
21
|
+
#include <regex>
|
22
|
+
|
23
|
+
|
24
|
+
/**
|
25
|
+
* @brief Constructor initializing supported language mappings
|
26
|
+
* @details Sets up the initial mapping of file extensions to programming languages
|
27
|
+
*/
|
28
|
+
|
29
|
+
LanguageDetector::LanguageDetector() {
|
30
|
+
// Initialize extension map
|
31
|
+
extensionMap = {
|
32
|
+
{".py", "Python"},
|
33
|
+
{".js", "JavaScript"},
|
34
|
+
{".ts", "TypeScript"},
|
35
|
+
{".cpp", "C++"},
|
36
|
+
{".hpp", "C++"},
|
37
|
+
{".h", "C/C++"},
|
38
|
+
{".c", "C"},
|
39
|
+
{".java", "Java"},
|
40
|
+
{".rs", "Rust"},
|
41
|
+
{".go", "Go"},
|
42
|
+
{".rb", "Ruby"},
|
43
|
+
{".php", "PHP"},
|
44
|
+
{".cs", "C#"},
|
45
|
+
{".swift", "Swift"},
|
46
|
+
{".kt", "Kotlin"},
|
47
|
+
{".r", "R"},
|
48
|
+
{".scala", "Scala"},
|
49
|
+
{".m", "Objective-C"},
|
50
|
+
{".sql", "SQL"},
|
51
|
+
{".sh", "Shell"}
|
52
|
+
};
|
53
|
+
}
|
54
|
+
|
55
|
+
/**
|
56
|
+
* @brief Analyzes file content to determine the programming language
|
57
|
+
* @details Examines file contents for language-specific patterns such as
|
58
|
+
* shebangs and syntax patterns when extension-based detection
|
59
|
+
* is insufficient
|
60
|
+
*
|
61
|
+
* @param filePath Path to the file being analyzed
|
62
|
+
* @return std::string Detected programming language or empty string if unknown
|
63
|
+
*/
|
64
|
+
|
65
|
+
std::string LanguageDetector::analyzeFileContent(const std::string& filePath) {
|
66
|
+
std::ifstream file(filePath);
|
67
|
+
if (!file.is_open()) return "";
|
68
|
+
|
69
|
+
std::string line;
|
70
|
+
std::vector<std::string> firstLines;
|
71
|
+
int lineCount = 0;
|
72
|
+
|
73
|
+
// Read first 10 lines for analysis
|
74
|
+
while (std::getline(file, line) && lineCount < 10) {
|
75
|
+
firstLines.push_back(line);
|
76
|
+
lineCount++;
|
77
|
+
}
|
78
|
+
|
79
|
+
// Pattern matching for specific languages
|
80
|
+
for (const auto& line : firstLines) {
|
81
|
+
// Python shebang
|
82
|
+
if (std::regex_search(line, std::regex("^#!/usr/bin/env python"))) {
|
83
|
+
return "Python";
|
84
|
+
}
|
85
|
+
// Node.js shebang
|
86
|
+
if (std::regex_search(line, std::regex("^#!/usr/bin/env node"))) {
|
87
|
+
return "JavaScript";
|
88
|
+
}
|
89
|
+
// Ruby shebang
|
90
|
+
if (std::regex_search(line, std::regex("^#!/usr/bin/env ruby"))) {
|
91
|
+
return "Ruby";
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
return "";
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* @brief Analyzes all source files in a project
|
100
|
+
* @details Recursively traverses the project's src directory, analyzing each file
|
101
|
+
* to determine its programming language using both extension-based and
|
102
|
+
* content-based detection methods
|
103
|
+
*
|
104
|
+
* @param projectPath Root path of the project to analyze
|
105
|
+
*/
|
106
|
+
|
107
|
+
void LanguageDetector::analyzeProject(const std::string& projectPath) {
|
108
|
+
try {
|
109
|
+
// Only analyze files in the src directory
|
110
|
+
std::filesystem::path srcPath = std::filesystem::path(projectPath) / "src";
|
111
|
+
if (!std::filesystem::exists(srcPath)) {
|
112
|
+
return;
|
113
|
+
}
|
114
|
+
|
115
|
+
for (const auto& entry : std::filesystem::directory_iterator(srcPath)) {
|
116
|
+
if (!entry.is_regular_file()) continue;
|
117
|
+
if (entry.path().filename().string()[0] == '.') continue;
|
118
|
+
|
119
|
+
std::string extension = entry.path().extension().string();
|
120
|
+
std::string detectedLanguage;
|
121
|
+
|
122
|
+
// First try extension-based detection
|
123
|
+
if (extensionMap.find(extension) != extensionMap.end()) {
|
124
|
+
detectedLanguage = extensionMap[extension];
|
125
|
+
} else {
|
126
|
+
// If extension not found, try content analysis
|
127
|
+
detectedLanguage = analyzeFileContent(entry.path().string());
|
128
|
+
}
|
129
|
+
|
130
|
+
if (!detectedLanguage.empty()) {
|
131
|
+
languageStats[detectedLanguage]++;
|
132
|
+
}
|
133
|
+
}
|
134
|
+
} catch (const std::filesystem::filesystem_error& e) {
|
135
|
+
std::cerr << "Error accessing directory: " << e.what() << std::endl;
|
136
|
+
}
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* @brief Retrieves the current language statistics
|
141
|
+
* @return std::unordered_map<std::string, int> Map of languages to file counts
|
142
|
+
*/
|
143
|
+
|
144
|
+
std::unordered_map<std::string, int> LanguageDetector::getLanguageStatistics() const {
|
145
|
+
return languageStats;
|
146
|
+
}
|
147
|
+
|
148
|
+
/**
|
149
|
+
* @brief Resets all language statistics
|
150
|
+
* @details Clears all accumulated language statistics, preparing for a new analysis
|
151
|
+
*/
|
152
|
+
|
153
|
+
void LanguageDetector::reset() {
|
154
|
+
languageStats.clear();
|
155
|
+
}
|
156
|
+
|
157
|
+
/**
|
158
|
+
* @brief Adds a custom file extension to language mapping
|
159
|
+
* @details Allows users to extend the language detection capabilities with
|
160
|
+
* custom file extensions
|
161
|
+
*
|
162
|
+
* @param extension File extension including the dot (e.g., ".custom")
|
163
|
+
* @param language Name of the programming language
|
164
|
+
*/
|
165
|
+
|
166
|
+
void LanguageDetector::addCustomExtension(const std::string& extension, const std::string& language) {
|
167
|
+
extensionMap[extension] = language;
|
168
|
+
}
|
169
|
+
|
170
|
+
/**
|
171
|
+
* @brief Gets the total number of analyzed files
|
172
|
+
* @return int Total number of files analyzed
|
173
|
+
*/
|
174
|
+
|
175
|
+
int LanguageDetector::getTotalFiles() const {
|
176
|
+
int total = 0;
|
177
|
+
for (const auto& stat : languageStats) {
|
178
|
+
total += stat.second;
|
179
|
+
}
|
180
|
+
return total;
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
* @brief Determines the most frequently used programming language
|
185
|
+
* @return std::string Name of the primary programming language
|
186
|
+
*/
|
187
|
+
std::string LanguageDetector::getPrimaryLanguage() const {
|
188
|
+
std::string primaryLang;
|
189
|
+
int maxCount = 0;
|
190
|
+
|
191
|
+
for (const auto& stat : languageStats) {
|
192
|
+
if (stat.second > maxCount) {
|
193
|
+
maxCount = stat.second;
|
194
|
+
primaryLang = stat.first;
|
195
|
+
}
|
196
|
+
}
|
197
|
+
|
198
|
+
return primaryLang;
|
199
|
+
}
|
@@ -0,0 +1,197 @@
|
|
1
|
+
/**
|
2
|
+
* @file license.cpp
|
3
|
+
* @brief License management and generation system
|
4
|
+
* @details Implements a comprehensive system for managing and generating software
|
5
|
+
* licenses. This system supports multiple common open source licenses
|
6
|
+
* including MIT, Apache 2.0, and GPL-3.0. Each license is stored with
|
7
|
+
* its full text, title, and a brief description explaining its key
|
8
|
+
* characteristics and use cases.
|
9
|
+
*
|
10
|
+
* @author Generated by README Builder
|
11
|
+
* @date 2025-02-17
|
12
|
+
*
|
13
|
+
* @note This implementation follows the official license texts from their
|
14
|
+
* respective sources and automatically handles the insertion of
|
15
|
+
* copyright year and holder information.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#include "license.hpp"
|
19
|
+
#include <ctime>
|
20
|
+
#include <stdexcept>
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @brief Constructs a new License manager
|
24
|
+
* @details Initializes the license management system by:
|
25
|
+
* 1. Getting the current year for copyright notices
|
26
|
+
* 2. Setting up the license template database
|
27
|
+
*
|
28
|
+
* The constructor prepares all supported license templates with their
|
29
|
+
* official texts and descriptions for later use.
|
30
|
+
*/
|
31
|
+
|
32
|
+
License::License() {
|
33
|
+
currentYear = getCurrentYear();
|
34
|
+
initializeLicenses();
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* @brief Initializes the database of supported licenses
|
39
|
+
* @details Sets up the internal storage of license templates, including:
|
40
|
+
* - Full license text with placeholders
|
41
|
+
* - License titles
|
42
|
+
* - Descriptions explaining license characteristics
|
43
|
+
*
|
44
|
+
* Currently supports:
|
45
|
+
* - MIT License
|
46
|
+
* - Apache License 2.0
|
47
|
+
* - GNU General Public License v3.0
|
48
|
+
*
|
49
|
+
* Each license is stored with placeholders for dynamic content
|
50
|
+
* (year and copyright holder) that get replaced during generation.
|
51
|
+
*/
|
52
|
+
|
53
|
+
void License::initializeLicenses() {
|
54
|
+
licenses["MIT"] = {
|
55
|
+
"MIT License",
|
56
|
+
"MIT License\n\n"
|
57
|
+
"Copyright (c) [year] [fullname]\n\n"
|
58
|
+
"Permission is hereby granted, free of charge, to any person obtaining a copy "
|
59
|
+
"of this software and associated documentation files (the \"Software\"), to deal "
|
60
|
+
"in the Software without restriction, including without limitation the rights "
|
61
|
+
"to use, copy, modify, merge, publish, distribute, sublicense, and/or sell "
|
62
|
+
"copies of the Software, and to permit persons to whom the Software is "
|
63
|
+
"furnished to do so, subject to the following conditions:\n\n"
|
64
|
+
"The above copyright notice and this permission notice shall be included in all "
|
65
|
+
"copies or substantial portions of the Software.\n\n"
|
66
|
+
"THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR "
|
67
|
+
"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, "
|
68
|
+
"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE "
|
69
|
+
"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER "
|
70
|
+
"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, "
|
71
|
+
"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE "
|
72
|
+
"SOFTWARE.",
|
73
|
+
"A permissive license that lets people do anything with your code with proper attribution and without warranty."
|
74
|
+
};
|
75
|
+
|
76
|
+
licenses["Apache-2.0"] = {
|
77
|
+
"Apache License 2.0",
|
78
|
+
" Apache License\n"
|
79
|
+
" Version 2.0, January 2004\n"
|
80
|
+
" http://www.apache.org/licenses/\n\n"
|
81
|
+
"Copyright [year] [fullname]\n\n"
|
82
|
+
"Licensed under the Apache License, Version 2.0 (the \"License\");\n"
|
83
|
+
"you may not use this file except in compliance with the License.\n"
|
84
|
+
"You may obtain a copy of the License at\n\n"
|
85
|
+
" http://www.apache.org/licenses/LICENSE-2.0\n\n"
|
86
|
+
"Unless required by applicable law or agreed to in writing, software\n"
|
87
|
+
"distributed under the License is distributed on an \"AS IS\" BASIS,\n"
|
88
|
+
"WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n"
|
89
|
+
"See the License for the specific language governing permissions and\n"
|
90
|
+
"limitations under the License.",
|
91
|
+
"A permissive license that also provides an express grant of patent rights from contributors to users."
|
92
|
+
};
|
93
|
+
|
94
|
+
licenses["GPL-3.0"] = {
|
95
|
+
"GNU General Public License v3.0",
|
96
|
+
"Copyright (C) [year] [fullname]\n\n"
|
97
|
+
"This program is free software: you can redistribute it and/or modify "
|
98
|
+
"it under the terms of the GNU General Public License as published by "
|
99
|
+
"the Free Software Foundation, either version 3 of the License, or "
|
100
|
+
"(at your option) any later version.\n\n"
|
101
|
+
"This program is distributed in the hope that it will be useful, "
|
102
|
+
"but WITHOUT ANY WARRANTY; without even the implied warranty of "
|
103
|
+
"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
|
104
|
+
"GNU General Public License for more details.\n\n"
|
105
|
+
"You should have received a copy of the GNU General Public License "
|
106
|
+
"along with this program. If not, see <https://www.gnu.org/licenses/>.",
|
107
|
+
"A copyleft license that requires anyone who distributes your code or a derivative work to make the source available under the same terms."
|
108
|
+
};
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* @brief Gets the current year for copyright notices
|
113
|
+
* @details Retrieves the current year from the system clock for use
|
114
|
+
* in license copyright statements. The year is formatted as
|
115
|
+
* a string to maintain consistency with license templates.
|
116
|
+
*
|
117
|
+
* @return std::string The current year as a string
|
118
|
+
*/
|
119
|
+
|
120
|
+
std::string License::getCurrentYear() const {
|
121
|
+
time_t now = time(0);
|
122
|
+
tm* ltm = localtime(&now);
|
123
|
+
return std::to_string(1900 + ltm->tm_year);
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* @brief Lists all available license types
|
128
|
+
* @details Returns a vector of supported license identifiers (e.g., "MIT",
|
129
|
+
* "Apache-2.0", "GPL-3.0") that can be used with getLicenseContent().
|
130
|
+
*
|
131
|
+
* @return std::vector<std::string> List of available license identifiers
|
132
|
+
*/
|
133
|
+
|
134
|
+
std::vector<std::string> License::getAvailableLicenses() const {
|
135
|
+
std::vector<std::string> available;
|
136
|
+
for (const auto& [key, value] : licenses) {
|
137
|
+
available.push_back(key);
|
138
|
+
}
|
139
|
+
return available;
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* @brief Generates a complete license text for a specific license type
|
144
|
+
* @details Creates a fully formatted license text by:
|
145
|
+
* 1. Retrieving the appropriate license template
|
146
|
+
* 2. Replacing [year] placeholder with current year
|
147
|
+
* 3. Replacing [fullname] placeholder with copyright holder
|
148
|
+
*
|
149
|
+
* @param licenseType The type of license to generate (e.g., "MIT")
|
150
|
+
* @param copyrightHolder The name of the copyright holder
|
151
|
+
* @return std::string Complete license text with all placeholders replaced
|
152
|
+
*
|
153
|
+
* @throws std::runtime_error If the requested license type is not supported
|
154
|
+
*
|
155
|
+
* @see getAvailableLicenses() For a list of supported license types
|
156
|
+
*/
|
157
|
+
|
158
|
+
std::string License::getLicenseContent(const std::string& licenseType,
|
159
|
+
const std::string& copyrightHolder) const {
|
160
|
+
if (licenses.find(licenseType) == licenses.end()) {
|
161
|
+
throw std::runtime_error("Invalid license type");
|
162
|
+
}
|
163
|
+
|
164
|
+
std::string content = licenses.at(licenseType).content;
|
165
|
+
|
166
|
+
// Replace placeholders
|
167
|
+
size_t yearPos = content.find("[year]");
|
168
|
+
if (yearPos != std::string::npos) {
|
169
|
+
content.replace(yearPos, 6, currentYear);
|
170
|
+
}
|
171
|
+
|
172
|
+
size_t namePos = content.find("[fullname]");
|
173
|
+
if (namePos != std::string::npos) {
|
174
|
+
content.replace(namePos, 10, copyrightHolder);
|
175
|
+
}
|
176
|
+
|
177
|
+
return content;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* @brief Retrieves the description of a specific license
|
182
|
+
* @details Returns a human-readable description of the license's key
|
183
|
+
* characteristics, including its permissions, conditions, and
|
184
|
+
* limitations.
|
185
|
+
*
|
186
|
+
* @param licenseType The type of license to describe (e.g., "MIT")
|
187
|
+
* @return std::string Description of the license
|
188
|
+
*
|
189
|
+
* @throws std::runtime_error If the requested license type is not supported
|
190
|
+
*/
|
191
|
+
|
192
|
+
std::string License::getLicenseDescription(const std::string& licenseType) const {
|
193
|
+
if (licenses.find(licenseType) == licenses.end()) {
|
194
|
+
throw std::runtime_error("Invalid license type");
|
195
|
+
}
|
196
|
+
return licenses.at(licenseType).description;
|
197
|
+
}
|
@@ -0,0 +1,40 @@
|
|
1
|
+
/**
|
2
|
+
* @file main.cpp
|
3
|
+
* @brief Main entry point for the README generation system
|
4
|
+
* @details This file contains the main function that orchestrates the README
|
5
|
+
* generation process, including project analysis and user input collection.
|
6
|
+
*
|
7
|
+
* @author Generated by README Builder
|
8
|
+
* @date 2025-02-17
|
9
|
+
*/
|
10
|
+
|
11
|
+
#include "../include/readme_builder.hpp"
|
12
|
+
#include <iostream>
|
13
|
+
|
14
|
+
/**
|
15
|
+
* @brief Program entry point
|
16
|
+
* @details Initializes the README generator, collects user input, and generates
|
17
|
+
* a comprehensive README.md file with project statistics and structure.
|
18
|
+
*
|
19
|
+
* @return int Exit code (0 for success, 1 for error)
|
20
|
+
*
|
21
|
+
* @throws std::exception Various exceptions from the README generation process
|
22
|
+
*/
|
23
|
+
|
24
|
+
int main() {
|
25
|
+
try {
|
26
|
+
ReadmeBuilder generator;
|
27
|
+
|
28
|
+
// First get user input for project name and description
|
29
|
+
generator.run();
|
30
|
+
|
31
|
+
// Then analyze the project for language statistics
|
32
|
+
generator.generateWithLanguageDetection();
|
33
|
+
|
34
|
+
std::cout << "README.md has been generated successfully with project structure and language statistics!" << std::endl;
|
35
|
+
} catch (const std::exception& e) {
|
36
|
+
std::cerr << "Error: " << e.what() << std::endl;
|
37
|
+
return 1;
|
38
|
+
}
|
39
|
+
return 0;
|
40
|
+
}
|
@@ -0,0 +1,226 @@
|
|
1
|
+
/**
|
2
|
+
* @file readme_builder.cpp
|
3
|
+
* @brief README generation system
|
4
|
+
* @details Implements a comprehensive README generator that analyzes project structure,
|
5
|
+
* detects programming languages used, and creates well-structured documentation.
|
6
|
+
* The system automatically detects project characteristics and generates
|
7
|
+
* appropriate installation instructions based on the build system used.
|
8
|
+
*
|
9
|
+
* @author Generated by README Builder
|
10
|
+
* @date 2025-02-17
|
11
|
+
*
|
12
|
+
* @note This implementation focuses on generating standardized, informative README
|
13
|
+
* files that follow best practices in project documentation.
|
14
|
+
*/
|
15
|
+
|
16
|
+
|
17
|
+
#include "../include/readme_builder.hpp"
|
18
|
+
#include "../include/language_detector.hpp"
|
19
|
+
#include "../include/build_detector.hpp"
|
20
|
+
#include <iostream>
|
21
|
+
#include <fstream>
|
22
|
+
#include <sstream>
|
23
|
+
#include <iomanip>
|
24
|
+
#include <ctime>
|
25
|
+
|
26
|
+
/**
|
27
|
+
* @brief Locates the root directory of the project
|
28
|
+
* @details Searches upward from the given path until it finds common project markers
|
29
|
+
* such as .git directory, src directory, or CMakeLists.txt
|
30
|
+
*
|
31
|
+
* @param startPath The path to start searching from
|
32
|
+
* @return std::string The absolute path to the project root
|
33
|
+
*/
|
34
|
+
|
35
|
+
std::string ReadmeBuilder::findProjectRoot(const std::string& startPath) const {
|
36
|
+
std::filesystem::path current(startPath);
|
37
|
+
|
38
|
+
while (current.has_parent_path()) {
|
39
|
+
bool hasGit = std::filesystem::exists(current / ".git");
|
40
|
+
bool hasSrc = std::filesystem::exists(current / "src");
|
41
|
+
bool hasCMake = std::filesystem::exists(current / "CMakeLists.txt");
|
42
|
+
|
43
|
+
if (hasGit || hasSrc || hasCMake) {
|
44
|
+
return current.string();
|
45
|
+
}
|
46
|
+
|
47
|
+
current = current.parent_path();
|
48
|
+
}
|
49
|
+
|
50
|
+
return startPath;
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* @brief Gets the current date in YYYY-MM-DD format
|
55
|
+
* @return std::string Formatted current date
|
56
|
+
*/
|
57
|
+
|
58
|
+
std::string ReadmeBuilder::getCurrentDate() const {
|
59
|
+
time_t now = time(0);
|
60
|
+
tm* ltm = localtime(&now);
|
61
|
+
std::string date = std::to_string(1900 + ltm->tm_year) + "-" +
|
62
|
+
std::to_string(1 + ltm->tm_mon) + "-" +
|
63
|
+
std::to_string(ltm->tm_mday);
|
64
|
+
return date;
|
65
|
+
}
|
66
|
+
|
67
|
+
/**
|
68
|
+
* @brief Generates the language statistics section of the README
|
69
|
+
* @details Creates a formatted section showing the percentage and count of files
|
70
|
+
* for each programming language detected in the project
|
71
|
+
*
|
72
|
+
* @return std::string Markdown-formatted language statistics
|
73
|
+
*/
|
74
|
+
|
75
|
+
std::string ReadmeBuilder::generateLanguageSection() const {
|
76
|
+
std::stringstream ss;
|
77
|
+
ss << "## Languages Used\n\n";
|
78
|
+
|
79
|
+
for (const auto& [language, count] : languageStats) {
|
80
|
+
double percentage = (static_cast<double>(count) / totalFiles) * 100;
|
81
|
+
ss << "- " << language << ": " << count << " files ("
|
82
|
+
<< std::fixed << std::setprecision(1) << percentage << "%)\n";
|
83
|
+
}
|
84
|
+
|
85
|
+
return ss.str();
|
86
|
+
}
|
87
|
+
|
88
|
+
/**
|
89
|
+
* @brief Generates a tree-like structure of project source files
|
90
|
+
* @details Recursively traverses the project directory and creates a formatted
|
91
|
+
* listing of source files, focusing on the src directory
|
92
|
+
*
|
93
|
+
* @param path The directory path to analyze
|
94
|
+
* @param depth Current recursion depth
|
95
|
+
* @return std::string Markdown-formatted project structure
|
96
|
+
*/
|
97
|
+
|
98
|
+
std::string ReadmeBuilder::generateProjectStructure(const std::string& path, int depth) const {
|
99
|
+
std::stringstream ss;
|
100
|
+
|
101
|
+
if (depth == 0) {
|
102
|
+
ss << "\n## Source Files\n\n";
|
103
|
+
}
|
104
|
+
|
105
|
+
// Only process src directory
|
106
|
+
std::filesystem::path srcPath = std::filesystem::path(path) / "src";
|
107
|
+
if (!std::filesystem::exists(srcPath)) {
|
108
|
+
return ss.str();
|
109
|
+
}
|
110
|
+
|
111
|
+
for (const auto& entry : std::filesystem::directory_iterator(srcPath)) {
|
112
|
+
if (!entry.is_regular_file()) continue;
|
113
|
+
if (entry.path().filename().string()[0] == '.') continue;
|
114
|
+
|
115
|
+
ss << "- " << entry.path().filename().string() << "\n";
|
116
|
+
}
|
117
|
+
|
118
|
+
return ss.str();
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* @brief Generates the README.md file
|
123
|
+
* @details Combines all gathered information and user input to create a comprehensive
|
124
|
+
* README.md file in the project root directory
|
125
|
+
*
|
126
|
+
* @throws std::runtime_error If unable to create or write to README.md
|
127
|
+
*/
|
128
|
+
|
129
|
+
void ReadmeBuilder::generateFile() {
|
130
|
+
std::filesystem::path readmePath = std::filesystem::path(projectRoot) / "README.md";
|
131
|
+
std::ofstream readme(readmePath);
|
132
|
+
if (!readme.is_open()) {
|
133
|
+
throw std::runtime_error("Could not create README.md file in project root");
|
134
|
+
}
|
135
|
+
|
136
|
+
readme << "# " << projectName << "\n\n"
|
137
|
+
<< description << "\n\n"
|
138
|
+
<< "Last updated: " << getCurrentDate() << "\n\n";
|
139
|
+
|
140
|
+
if (totalFiles > 0) {
|
141
|
+
readme << "## Project Overview\n\n"
|
142
|
+
<< "This project is primarily written in " << primaryLanguage
|
143
|
+
<< " and contains " << totalFiles << " source files.\n\n"
|
144
|
+
<< generateLanguageSection()
|
145
|
+
<< generateProjectStructure(projectRoot);
|
146
|
+
}
|
147
|
+
|
148
|
+
// Use the build system detector to generate installation instructions
|
149
|
+
BuildSystemDetector detector(projectRoot);
|
150
|
+
readme << detector.generateInstallationInstructions();
|
151
|
+
|
152
|
+
readme << "## Usage\n\n"
|
153
|
+
<< "Add instructions on how to use your project.\n\n"
|
154
|
+
<< "## Contributing\n\n"
|
155
|
+
<< "Instructions for how to contribute to the project.\n\n"
|
156
|
+
<< "## License\n\n"
|
157
|
+
<< "Specify your project license here.\n";
|
158
|
+
|
159
|
+
readme.close();
|
160
|
+
std::cout << "README.md has been generated successfully in project root!" << std::endl;
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* @brief Constructor for ReadmeBuilder with pre-analyzed statistics
|
165
|
+
* @details Initializes the README builder with pre-computed language statistics
|
166
|
+
*
|
167
|
+
* @param name Project name
|
168
|
+
* @param stats Map of language statistics
|
169
|
+
* @param primary Primary programming language used
|
170
|
+
* @param total Total number of source files
|
171
|
+
*/
|
172
|
+
|
173
|
+
ReadmeBuilder::ReadmeBuilder(const std::string& name,
|
174
|
+
const std::unordered_map<std::string, int>& stats,
|
175
|
+
const std::string& primary,
|
176
|
+
int total)
|
177
|
+
: projectName(name), languageStats(stats), primaryLanguage(primary), totalFiles(total) {
|
178
|
+
projectRoot = findProjectRoot(std::filesystem::current_path().string());
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* @brief Interactively collects project information from user
|
183
|
+
* @details Prompts the user for project title and description
|
184
|
+
*
|
185
|
+
* @throws std::runtime_error If required information is not provided
|
186
|
+
*/
|
187
|
+
|
188
|
+
void ReadmeBuilder::run() {
|
189
|
+
std::cout << "Enter project title: ";
|
190
|
+
std::getline(std::cin, projectName);
|
191
|
+
|
192
|
+
if (projectName.empty()) {
|
193
|
+
throw std::runtime_error("Project title cannot be empty");
|
194
|
+
}
|
195
|
+
|
196
|
+
std::cout << "Enter project description: ";
|
197
|
+
std::getline(std::cin, description);
|
198
|
+
|
199
|
+
if (description.empty()) {
|
200
|
+
throw std::runtime_error("Project description cannot be empty");
|
201
|
+
}
|
202
|
+
|
203
|
+
generateFile();
|
204
|
+
}
|
205
|
+
|
206
|
+
/**
|
207
|
+
* @brief Analyzes project and generates README with language statistics
|
208
|
+
* @details Uses LanguageDetector to analyze the project structure and
|
209
|
+
* automatically generate language statistics
|
210
|
+
*/
|
211
|
+
|
212
|
+
void ReadmeBuilder::generateWithLanguageDetection() {
|
213
|
+
LanguageDetector detector;
|
214
|
+
detector.analyzeProject(projectRoot);
|
215
|
+
|
216
|
+
languageStats = detector.getLanguageStatistics();
|
217
|
+
primaryLanguage = detector.getPrimaryLanguage();
|
218
|
+
totalFiles = detector.getTotalFiles();
|
219
|
+
|
220
|
+
if (projectName.empty()) {
|
221
|
+
projectName = std::filesystem::path(projectRoot).filename().string();
|
222
|
+
}
|
223
|
+
|
224
|
+
generateFile();
|
225
|
+
}
|
226
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# lib/readme_builder.rb
|
2
|
+
|
3
|
+
##
|
4
|
+
# The ReadmeBuilder module provides functionality to automatically generate
|
5
|
+
# README and LICENSE files for software projects through a command-line interface.
|
6
|
+
#
|
7
|
+
# @example Using from command line
|
8
|
+
# $ readme_builder
|
9
|
+
# Enter project name: My Project
|
10
|
+
# Enter description: A great tool
|
11
|
+
# Select license: MIT
|
12
|
+
#
|
13
|
+
# @author Your Name
|
14
|
+
# @since 0.1.0
|
15
|
+
module ReadmeBuilder
|
16
|
+
##
|
17
|
+
# Custom error class for ReadmeBuilder-specific exceptions.
|
18
|
+
# Used to distinguish errors from this gem from standard Ruby errors.
|
19
|
+
#
|
20
|
+
# @example Handling specific errors
|
21
|
+
# begin
|
22
|
+
# # Some readme_builder operation
|
23
|
+
# rescue ReadmeBuilder::Error => e
|
24
|
+
# puts "ReadmeBuilder error: #{e.message}"
|
25
|
+
# end
|
26
|
+
class Error < StandardError; end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Load version and native extension
|
30
|
+
require 'readme_builder/version'
|
31
|
+
require 'readme_builder/native' # Load the compiled C++ extension
|