@repokit/core 2.0.7 → 3.0.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.
- package/Cargo.lock +113 -7
- package/Cargo.toml +3 -2
- package/README.md +45 -0
- package/dist/RepoKitConfig.d.mts +4 -1
- package/dist/RepoKitConfig.mjs +3 -1
- package/dist/RepoKitTheme.d.mts +47 -0
- package/dist/RepoKitTheme.mjs +45 -0
- package/dist/index.d.mts +4 -3
- package/dist/index.mjs +2 -1
- package/dist/types.d.mts +18 -5
- package/externals/RepoKitConfig.ts +10 -2
- package/externals/RepoKitTheme.ts +44 -0
- package/externals/index.ts +1 -0
- package/externals/types.ts +25 -4
- package/installation/install.sh +9 -3
- package/internals/argv/argv.rs +133 -0
- package/internals/argv/mod.rs +1 -0
- package/internals/configuration/configuration.rs +2 -2
- package/internals/internal_commands/help.rs +57 -45
- package/internals/internal_commands/internal_registry.rs +5 -4
- package/internals/internal_commands/list_commands.rs +3 -3
- package/internals/internal_commands/list_owners.rs +10 -10
- package/internals/internal_commands/list_themes.rs +113 -0
- package/internals/internal_commands/locate_command.rs +8 -2
- package/internals/internal_commands/mod.rs +1 -0
- package/internals/internal_commands/onboarder.rs +10 -4
- package/internals/internal_commands/register_command.rs +3 -3
- package/internals/internal_commands/search_commands.rs +3 -3
- package/internals/internal_commands/upgrade_repokit.rs +5 -1
- package/internals/internal_filesystem/internal_filesystem.rs +124 -3
- package/internals/logger/logger.rs +44 -56
- package/internals/main.rs +2 -0
- package/internals/repokit/interfaces.rs +3 -0
- package/internals/repokit/repokit.rs +9 -6
- package/internals/themes/built_in_themes/mod.rs +3 -0
- package/internals/themes/built_in_themes/money.rs +41 -0
- package/internals/themes/built_in_themes/seeing_red.rs +41 -0
- package/internals/themes/built_in_themes/the_blues.rs +41 -0
- package/internals/themes/mod.rs +5 -0
- package/internals/themes/theme.rs +108 -0
- package/internals/themes/theme_colors.rs +36 -0
- package/internals/themes/theme_inputs.rs +34 -0
- package/internals/themes/theme_registry.rs +102 -0
- package/internals/validations/command_validations.rs +8 -8
- package/media/seeing-red.webp +0 -0
- package/package.json +5 -4
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
use normalize_path::NormalizePath;
|
|
2
|
+
|
|
3
|
+
use regex::Regex;
|
|
4
|
+
use shellexpand::tilde;
|
|
2
5
|
use std::{
|
|
3
|
-
fs::File,
|
|
6
|
+
fs::{self, File},
|
|
7
|
+
io::{BufRead, BufReader, Lines},
|
|
4
8
|
path::{Path, PathBuf},
|
|
5
9
|
};
|
|
6
10
|
|
|
@@ -49,8 +53,8 @@ impl InternalFileSystem {
|
|
|
49
53
|
Logger::exit_with_info(
|
|
50
54
|
format!(
|
|
51
55
|
"To start using {}, please initialize your git repository by running {}",
|
|
52
|
-
Logger::
|
|
53
|
-
Logger::
|
|
56
|
+
Logger::with_theme(|theme| theme.highlight("Repokit")),
|
|
57
|
+
Logger::with_theme(|theme| theme.highlight("git init"))
|
|
54
58
|
)
|
|
55
59
|
.as_str(),
|
|
56
60
|
);
|
|
@@ -58,6 +62,58 @@ impl InternalFileSystem {
|
|
|
58
62
|
root
|
|
59
63
|
}
|
|
60
64
|
|
|
65
|
+
pub fn read_theme_preference(&self) -> String {
|
|
66
|
+
let default = Logger::with_registry(|registry| registry.default_theme.clone());
|
|
67
|
+
let theme = self.read_dot_file(&mut |mut lines, _| {
|
|
68
|
+
if let Some(theme_preference) = lines.nth(1)
|
|
69
|
+
&& let Ok(preference) = theme_preference
|
|
70
|
+
{
|
|
71
|
+
return preference;
|
|
72
|
+
}
|
|
73
|
+
default.to_string()
|
|
74
|
+
});
|
|
75
|
+
theme.unwrap_or(default)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
pub fn store_theme_preference(&self, theme: &str) {
|
|
79
|
+
self.read_dot_file(&mut |lines, path| {
|
|
80
|
+
let mut content: Vec<String> = lines.map(|line| line.unwrap()).collect();
|
|
81
|
+
let theme_text = theme.to_string();
|
|
82
|
+
if content.len() >= 2 {
|
|
83
|
+
content[1] = theme_text;
|
|
84
|
+
} else {
|
|
85
|
+
content.push(theme_text);
|
|
86
|
+
}
|
|
87
|
+
fs::write(path, content.join("\n"))
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
pub fn read_dot_file<R>(
|
|
92
|
+
&self,
|
|
93
|
+
func: &mut impl FnMut(Lines<BufReader<File>>, PathBuf) -> R,
|
|
94
|
+
) -> Option<R> {
|
|
95
|
+
let file_path = self.create_dot_file_if_not_exists();
|
|
96
|
+
match file_path {
|
|
97
|
+
Some(path) => {
|
|
98
|
+
if let Ok(file) = File::open(&path) {
|
|
99
|
+
let lines = BufReader::new(file).lines();
|
|
100
|
+
return Some(func(lines, path));
|
|
101
|
+
}
|
|
102
|
+
None
|
|
103
|
+
}
|
|
104
|
+
None => None,
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub fn home() -> Option<PathBuf> {
|
|
109
|
+
let expanded_path_str = tilde("~/");
|
|
110
|
+
let path = Path::new(expanded_path_str.as_ref()).normalize();
|
|
111
|
+
if path.is_absolute() && path.exists() {
|
|
112
|
+
return Some(path);
|
|
113
|
+
}
|
|
114
|
+
None
|
|
115
|
+
}
|
|
116
|
+
|
|
61
117
|
fn commands_directory(&self) -> PathBuf {
|
|
62
118
|
self.absolute(format!("{}/dist/commands", self.package_directory()).as_str())
|
|
63
119
|
}
|
|
@@ -80,4 +136,69 @@ impl InternalFileSystem {
|
|
|
80
136
|
.into_string()
|
|
81
137
|
.expect("Cannot construct path")
|
|
82
138
|
}
|
|
139
|
+
|
|
140
|
+
fn create_dot_file_if_not_exists(&self) -> Option<PathBuf> {
|
|
141
|
+
match InternalFileSystem::home() {
|
|
142
|
+
Some(home) => {
|
|
143
|
+
let dot_file_path = home.join(".repokit");
|
|
144
|
+
if !&dot_file_path.exists() {
|
|
145
|
+
let found_version = self.current_version();
|
|
146
|
+
found_version.as_ref()?;
|
|
147
|
+
let version_string = found_version.unwrap();
|
|
148
|
+
let result = fs::write(&dot_file_path, format!("{version_string}\n"));
|
|
149
|
+
if result.is_err() {
|
|
150
|
+
return None;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
Some(dot_file_path)
|
|
154
|
+
}
|
|
155
|
+
None => {
|
|
156
|
+
Logger::error(
|
|
157
|
+
"I encountered an issue when attempting to create a cache file on your machine",
|
|
158
|
+
);
|
|
159
|
+
Logger::error(
|
|
160
|
+
format!(
|
|
161
|
+
"Please create a file called {} in your home directory",
|
|
162
|
+
Logger::with_theme(|theme| theme.highlight(".repokit"))
|
|
163
|
+
)
|
|
164
|
+
.as_str(),
|
|
165
|
+
);
|
|
166
|
+
Logger::error(
|
|
167
|
+
"This file will be used to store settings and indicators that optimize how repokit runs in your repository",
|
|
168
|
+
);
|
|
169
|
+
None
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
pub fn current_version(&self) -> Option<String> {
|
|
175
|
+
let package_path = Path::new(&self.root)
|
|
176
|
+
.join(InternalFileSystem::package_directory(self))
|
|
177
|
+
.normalize();
|
|
178
|
+
let package_json_path = package_path.join("package.json");
|
|
179
|
+
let file = File::open(package_json_path);
|
|
180
|
+
if file.is_err() {
|
|
181
|
+
return None;
|
|
182
|
+
}
|
|
183
|
+
let lines = BufReader::new(file.unwrap()).lines();
|
|
184
|
+
let version_matcher = Regex::new(r#""([^"]*)""#).unwrap();
|
|
185
|
+
for line in lines.flatten() {
|
|
186
|
+
if line.contains("\"version\": ") {
|
|
187
|
+
let captures: Vec<String> = version_matcher
|
|
188
|
+
.captures_iter(&line)
|
|
189
|
+
.filter_map(|item| {
|
|
190
|
+
item.get(1)
|
|
191
|
+
.map(|match_text| match_text.as_str().to_string())
|
|
192
|
+
})
|
|
193
|
+
.collect();
|
|
194
|
+
let version = captures.get(1);
|
|
195
|
+
if version.is_some() {
|
|
196
|
+
return Some(version.unwrap().to_string());
|
|
197
|
+
} else {
|
|
198
|
+
return None;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
None
|
|
203
|
+
}
|
|
83
204
|
}
|
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
use std::process::exit;
|
|
2
2
|
use std::sync::LazyLock;
|
|
3
3
|
use std::sync::Mutex;
|
|
4
|
+
use std::sync::MutexGuard;
|
|
4
5
|
|
|
5
|
-
use colored::{ColoredString, Colorize
|
|
6
|
+
use colored::{ColoredString, Colorize};
|
|
7
|
+
|
|
8
|
+
use crate::repokit::interfaces::RepoKitConfig;
|
|
9
|
+
use crate::themes::theme::Theme;
|
|
10
|
+
use crate::themes::theme_registry::ThemeRegistry;
|
|
6
11
|
|
|
7
12
|
static REGISTERED_NAME: LazyLock<Mutex<String>> =
|
|
8
13
|
LazyLock::new(|| Mutex::new("Repokit".to_string()));
|
|
9
14
|
|
|
15
|
+
static THEMES: LazyLock<Mutex<ThemeRegistry>> = LazyLock::new(|| Mutex::new(ThemeRegistry::new()));
|
|
16
|
+
|
|
10
17
|
pub struct Logger {}
|
|
11
18
|
|
|
12
19
|
impl Logger {
|
|
13
|
-
pub fn
|
|
14
|
-
|
|
20
|
+
pub fn configure(configuration: &RepoKitConfig) {
|
|
21
|
+
Logger::set_name(&configuration.project);
|
|
22
|
+
for theme in &configuration.themes {
|
|
23
|
+
Logger::with_registry(|mut registry| registry.register_user_theme(theme))
|
|
24
|
+
}
|
|
15
25
|
}
|
|
16
26
|
|
|
17
27
|
pub fn info(message: &str) {
|
|
@@ -36,8 +46,19 @@ impl Logger {
|
|
|
36
46
|
println!("\n{}{}\n", Logger::info_prefix(), message);
|
|
37
47
|
}
|
|
38
48
|
|
|
49
|
+
pub fn with_surrounding_space<F>(mut func: impl FnMut() -> F) -> F {
|
|
50
|
+
println!();
|
|
51
|
+
let result = func();
|
|
52
|
+
println!();
|
|
53
|
+
result
|
|
54
|
+
}
|
|
55
|
+
|
|
39
56
|
pub fn log_file_path(path: &str) {
|
|
40
|
-
println!(
|
|
57
|
+
println!(
|
|
58
|
+
"\n{}{}\n",
|
|
59
|
+
Logger::indent(None),
|
|
60
|
+
Logger::with_theme(|theme| theme.highlight(path))
|
|
61
|
+
);
|
|
41
62
|
}
|
|
42
63
|
|
|
43
64
|
pub fn indent(times: Option<i32>) -> String {
|
|
@@ -45,54 +66,10 @@ impl Logger {
|
|
|
45
66
|
" ".repeat(indentation.try_into().unwrap())
|
|
46
67
|
}
|
|
47
68
|
|
|
48
|
-
pub fn blue(message: &str) -> ColoredString {
|
|
49
|
-
message.bright_blue()
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
pub fn blue_bright(message: &str) -> ColoredString {
|
|
53
|
-
message.bright_blue().bold()
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
pub fn magenta_bright(message: &str) -> ColoredString {
|
|
57
|
-
message.bright_magenta().bold()
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
pub fn magenta(message: &str) -> ColoredString {
|
|
61
|
-
message.magenta()
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
pub fn green(message: &str) -> ColoredString {
|
|
65
|
-
message.green()
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
pub fn green_bright(message: &str) -> ColoredString {
|
|
69
|
-
message.bright_green()
|
|
70
|
-
}
|
|
71
|
-
|
|
72
69
|
pub fn cyan(message: &str) -> ColoredString {
|
|
73
70
|
message.cyan()
|
|
74
71
|
}
|
|
75
72
|
|
|
76
|
-
pub fn cyan_bright(message: &str) -> ColoredString {
|
|
77
|
-
message.bright_cyan().bold()
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
pub fn gray(message: &str) -> ColoredString {
|
|
81
|
-
message.custom_color(CustomColor {
|
|
82
|
-
r: 128,
|
|
83
|
-
g: 128,
|
|
84
|
-
b: 128,
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
pub fn lime(message: &str) -> ColoredString {
|
|
89
|
-
message.custom_color(CustomColor {
|
|
90
|
-
r: 175,
|
|
91
|
-
g: 247,
|
|
92
|
-
b: 7,
|
|
93
|
-
})
|
|
94
|
-
}
|
|
95
|
-
|
|
96
73
|
pub fn file_create_error() {
|
|
97
74
|
Logger::file_error("create a file");
|
|
98
75
|
}
|
|
@@ -113,6 +90,15 @@ impl Logger {
|
|
|
113
90
|
Logger::log_file_path("https://github.com/alexfigliolia/repokit/issues");
|
|
114
91
|
}
|
|
115
92
|
|
|
93
|
+
pub fn with_theme<R>(func: impl Fn(&Theme) -> R) -> R {
|
|
94
|
+
Logger::with_registry(|registry| func(registry.current_theme()))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
pub fn with_registry<R>(func: impl Fn(MutexGuard<'_, ThemeRegistry>) -> R) -> R {
|
|
98
|
+
let registry = THEMES.lock().unwrap();
|
|
99
|
+
func(registry)
|
|
100
|
+
}
|
|
101
|
+
|
|
116
102
|
fn file_error(operation: &str) {
|
|
117
103
|
Logger::info(format!("I was unable to {operation} in your repository").as_str());
|
|
118
104
|
Logger::error("Please verify the permissions on your working directory or file a bug here");
|
|
@@ -120,15 +106,17 @@ impl Logger {
|
|
|
120
106
|
exit(0);
|
|
121
107
|
}
|
|
122
108
|
|
|
123
|
-
fn info_prefix() ->
|
|
124
|
-
format!("{}: ",
|
|
125
|
-
.bright_magenta()
|
|
126
|
-
.bold()
|
|
109
|
+
fn info_prefix() -> String {
|
|
110
|
+
Logger::with_theme(|theme| format!("{}: ", theme.prefix(®ISTERED_NAME.lock().unwrap())))
|
|
127
111
|
}
|
|
128
112
|
|
|
129
|
-
fn error_prefix() ->
|
|
130
|
-
|
|
131
|
-
.
|
|
132
|
-
|
|
113
|
+
fn error_prefix() -> String {
|
|
114
|
+
Logger::with_theme(|theme| {
|
|
115
|
+
format!("{}: ", theme.error_prefix(®ISTERED_NAME.lock().unwrap()))
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
fn set_name(value: &str) {
|
|
120
|
+
*REGISTERED_NAME.lock().unwrap() = value.to_string();
|
|
133
121
|
}
|
|
134
122
|
}
|
package/internals/main.rs
CHANGED
|
@@ -3,6 +3,7 @@ use crate::{
|
|
|
3
3
|
internal_filesystem::internal_filesystem::InternalFileSystem, repokit::repokit::RepoKit,
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
+
mod argv;
|
|
6
7
|
mod configuration;
|
|
7
8
|
mod executables;
|
|
8
9
|
mod executor;
|
|
@@ -11,6 +12,7 @@ mod internal_commands;
|
|
|
11
12
|
mod internal_filesystem;
|
|
12
13
|
mod logger;
|
|
13
14
|
mod repokit;
|
|
15
|
+
mod themes;
|
|
14
16
|
mod validations;
|
|
15
17
|
|
|
16
18
|
fn main() {
|
|
@@ -2,6 +2,8 @@ use std::collections::HashMap;
|
|
|
2
2
|
|
|
3
3
|
use serde::Deserialize;
|
|
4
4
|
|
|
5
|
+
use crate::themes::theme_inputs::RepoKitTheme;
|
|
6
|
+
|
|
5
7
|
#[derive(Debug, Deserialize, Clone)]
|
|
6
8
|
pub struct CommandDefinition {
|
|
7
9
|
pub command: String,
|
|
@@ -33,6 +35,7 @@ pub struct RepoKitConfig {
|
|
|
33
35
|
pub project: String,
|
|
34
36
|
pub thirdParty: Vec<RepoKitCommand>,
|
|
35
37
|
pub commands: HashMap<String, CommandDefinition>,
|
|
38
|
+
pub themes: Vec<RepoKitTheme>,
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
#[derive(Debug, Deserialize, Clone)]
|
|
@@ -11,6 +11,7 @@ use crate::{
|
|
|
11
11
|
},
|
|
12
12
|
executor::executor::Executor,
|
|
13
13
|
internal_commands::help::Help,
|
|
14
|
+
internal_filesystem::internal_filesystem::InternalFileSystem,
|
|
14
15
|
logger::logger::Logger,
|
|
15
16
|
repokit::interfaces::{RepoKitCommand, RepoKitConfig},
|
|
16
17
|
validations::command_validations::CommandValidations,
|
|
@@ -22,7 +23,9 @@ pub struct RepoKit {
|
|
|
22
23
|
|
|
23
24
|
impl RepoKit {
|
|
24
25
|
pub fn new(root: String, configuration: RepoKitConfig) -> RepoKit {
|
|
25
|
-
Logger::
|
|
26
|
+
Logger::configure(&configuration);
|
|
27
|
+
let theme = InternalFileSystem::new(&root).read_theme_preference();
|
|
28
|
+
Logger::with_registry(|mut registry| registry.set_theme(&root, &theme));
|
|
26
29
|
RepoKit {
|
|
27
30
|
scope: RepoKitScope {
|
|
28
31
|
root,
|
|
@@ -113,7 +116,7 @@ impl RepoKit {
|
|
|
113
116
|
Logger::info(
|
|
114
117
|
format!(
|
|
115
118
|
"I'm not aware of a command named {}",
|
|
116
|
-
Logger::
|
|
119
|
+
Logger::with_theme(|theme| theme.highlight(command))
|
|
117
120
|
)
|
|
118
121
|
.as_str(),
|
|
119
122
|
);
|
|
@@ -123,15 +126,15 @@ impl RepoKit {
|
|
|
123
126
|
Logger::info(
|
|
124
127
|
format!(
|
|
125
128
|
"The command {} was not found on {}",
|
|
126
|
-
Logger::
|
|
127
|
-
Logger::
|
|
129
|
+
Logger::with_theme(|theme| theme.highlight(sub_command)),
|
|
130
|
+
Logger::with_theme(|theme| theme.highlight(&command.name))
|
|
128
131
|
)
|
|
129
132
|
.as_str(),
|
|
130
133
|
);
|
|
131
134
|
Logger::info(
|
|
132
135
|
format!(
|
|
133
136
|
"Here are the commands that belong to {}",
|
|
134
|
-
Logger::
|
|
137
|
+
Logger::with_theme(|theme| theme.highlight(&command.name))
|
|
135
138
|
)
|
|
136
139
|
.as_str(),
|
|
137
140
|
);
|
|
@@ -142,7 +145,7 @@ impl RepoKit {
|
|
|
142
145
|
Logger::info(
|
|
143
146
|
format!(
|
|
144
147
|
"Listing available commands for {}\n",
|
|
145
|
-
Logger::
|
|
148
|
+
Logger::with_theme(|theme| theme.command(&command.name))
|
|
146
149
|
)
|
|
147
150
|
.as_str(),
|
|
148
151
|
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
use colored::Color;
|
|
2
|
+
|
|
3
|
+
use crate::themes::theme_colors::ThemeColors;
|
|
4
|
+
|
|
5
|
+
pub const MONEY: ThemeColors = ThemeColors {
|
|
6
|
+
prefixColor: Color::TrueColor {
|
|
7
|
+
r: 26,
|
|
8
|
+
g: 227,
|
|
9
|
+
b: 133,
|
|
10
|
+
},
|
|
11
|
+
commandColor: Color::TrueColor {
|
|
12
|
+
r: 82,
|
|
13
|
+
g: 234,
|
|
14
|
+
b: 74,
|
|
15
|
+
},
|
|
16
|
+
subcommandColor: Color::TrueColor {
|
|
17
|
+
r: 51,
|
|
18
|
+
g: 241,
|
|
19
|
+
b: 162,
|
|
20
|
+
},
|
|
21
|
+
argColor: Color::TrueColor {
|
|
22
|
+
r: 124,
|
|
23
|
+
g: 244,
|
|
24
|
+
b: 102,
|
|
25
|
+
},
|
|
26
|
+
descriptionColor: Color::TrueColor {
|
|
27
|
+
r: 126,
|
|
28
|
+
g: 168,
|
|
29
|
+
b: 140,
|
|
30
|
+
},
|
|
31
|
+
errorPrefixColor: Color::TrueColor {
|
|
32
|
+
r: 220,
|
|
33
|
+
g: 36,
|
|
34
|
+
b: 100,
|
|
35
|
+
},
|
|
36
|
+
highlightColor: Color::TrueColor {
|
|
37
|
+
r: 25,
|
|
38
|
+
g: 206,
|
|
39
|
+
b: 91,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
use colored::Color;
|
|
2
|
+
|
|
3
|
+
use crate::themes::theme_colors::ThemeColors;
|
|
4
|
+
|
|
5
|
+
pub const SEEING_RED: ThemeColors = ThemeColors {
|
|
6
|
+
prefixColor: Color::TrueColor {
|
|
7
|
+
r: 220,
|
|
8
|
+
g: 36,
|
|
9
|
+
b: 91,
|
|
10
|
+
},
|
|
11
|
+
commandColor: Color::TrueColor {
|
|
12
|
+
r: 220,
|
|
13
|
+
g: 36,
|
|
14
|
+
b: 36,
|
|
15
|
+
},
|
|
16
|
+
subcommandColor: Color::TrueColor {
|
|
17
|
+
r: 220,
|
|
18
|
+
g: 131,
|
|
19
|
+
b: 36,
|
|
20
|
+
},
|
|
21
|
+
argColor: Color::TrueColor {
|
|
22
|
+
r: 220,
|
|
23
|
+
g: 205,
|
|
24
|
+
b: 36,
|
|
25
|
+
},
|
|
26
|
+
descriptionColor: Color::TrueColor {
|
|
27
|
+
r: 179,
|
|
28
|
+
g: 100,
|
|
29
|
+
b: 151,
|
|
30
|
+
},
|
|
31
|
+
errorPrefixColor: Color::TrueColor {
|
|
32
|
+
r: 220,
|
|
33
|
+
g: 36,
|
|
34
|
+
b: 39,
|
|
35
|
+
},
|
|
36
|
+
highlightColor: Color::TrueColor {
|
|
37
|
+
r: 237,
|
|
38
|
+
g: 175,
|
|
39
|
+
b: 41,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
use colored::Color;
|
|
2
|
+
|
|
3
|
+
use crate::themes::theme_colors::ThemeColors;
|
|
4
|
+
|
|
5
|
+
pub const THE_BLUES: ThemeColors = ThemeColors {
|
|
6
|
+
prefixColor: Color::TrueColor {
|
|
7
|
+
r: 36,
|
|
8
|
+
g: 111,
|
|
9
|
+
b: 255,
|
|
10
|
+
},
|
|
11
|
+
commandColor: Color::TrueColor {
|
|
12
|
+
r: 52,
|
|
13
|
+
g: 96,
|
|
14
|
+
b: 255,
|
|
15
|
+
},
|
|
16
|
+
subcommandColor: Color::TrueColor {
|
|
17
|
+
r: 0,
|
|
18
|
+
g: 157,
|
|
19
|
+
b: 255,
|
|
20
|
+
},
|
|
21
|
+
argColor: Color::TrueColor {
|
|
22
|
+
r: 40,
|
|
23
|
+
g: 175,
|
|
24
|
+
b: 253,
|
|
25
|
+
},
|
|
26
|
+
descriptionColor: Color::TrueColor {
|
|
27
|
+
r: 100,
|
|
28
|
+
g: 165,
|
|
29
|
+
b: 179,
|
|
30
|
+
},
|
|
31
|
+
errorPrefixColor: Color::TrueColor {
|
|
32
|
+
r: 220,
|
|
33
|
+
g: 36,
|
|
34
|
+
b: 100,
|
|
35
|
+
},
|
|
36
|
+
highlightColor: Color::TrueColor {
|
|
37
|
+
r: 69,
|
|
38
|
+
g: 219,
|
|
39
|
+
b: 229,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
use colored::{Color, ColoredString, Colorize};
|
|
2
|
+
|
|
3
|
+
use crate::themes::{
|
|
4
|
+
theme_colors::ThemeColors,
|
|
5
|
+
theme_inputs::{RepoKitTheme, ThemeInput, ThemeInputColors},
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
#[derive(Clone)]
|
|
9
|
+
pub struct Theme {
|
|
10
|
+
pub name: String,
|
|
11
|
+
pub colors: ThemeColors,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
impl Theme {
|
|
15
|
+
pub fn new(input: ThemeInput) -> Theme {
|
|
16
|
+
Theme {
|
|
17
|
+
name: input.name,
|
|
18
|
+
colors: ThemeColors {
|
|
19
|
+
prefixColor: input.colors.prefixColor.unwrap_or(Color::BrightMagenta),
|
|
20
|
+
commandColor: input.colors.commandColor.unwrap_or(Color::BrightBlue),
|
|
21
|
+
subcommandColor: input.colors.subcommandColor.unwrap_or(Color::TrueColor {
|
|
22
|
+
r: 175,
|
|
23
|
+
g: 247,
|
|
24
|
+
b: 7,
|
|
25
|
+
}),
|
|
26
|
+
argColor: input.colors.argColor.unwrap_or(Color::Green),
|
|
27
|
+
descriptionColor: input.colors.descriptionColor.unwrap_or(Color::TrueColor {
|
|
28
|
+
r: 128,
|
|
29
|
+
g: 128,
|
|
30
|
+
b: 128,
|
|
31
|
+
}),
|
|
32
|
+
errorPrefixColor: input.colors.errorPrefixColor.unwrap_or(Color::Red),
|
|
33
|
+
highlightColor: input.colors.highlightColor.unwrap_or(Color::BrightBlue),
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
pub fn prefix(&self, msg: &str) -> ColoredString {
|
|
39
|
+
msg.color(self.colors.prefixColor).bold()
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
pub fn command(&self, msg: &str) -> ColoredString {
|
|
43
|
+
msg.color(self.colors.commandColor)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
pub fn sub_command(&self, msg: &str) -> ColoredString {
|
|
47
|
+
msg.color(self.colors.subcommandColor)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
pub fn arg(&self, msg: &str) -> ColoredString {
|
|
51
|
+
msg.color(self.colors.argColor)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
pub fn description(&self, msg: &str) -> ColoredString {
|
|
55
|
+
msg.color(self.colors.descriptionColor)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pub fn error_prefix(&self, msg: &str) -> ColoredString {
|
|
59
|
+
msg.color(self.colors.errorPrefixColor).bold()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
pub fn highlight(&self, msg: &str) -> ColoredString {
|
|
63
|
+
msg.color(self.colors.highlightColor)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
pub fn from_configuration(theme: &RepoKitTheme) -> Theme {
|
|
67
|
+
Theme::new(ThemeInput {
|
|
68
|
+
name: theme.name.clone(),
|
|
69
|
+
colors: ThemeInputColors {
|
|
70
|
+
prefixColor: Theme::parse_rgb(&theme.colors.prefixColor),
|
|
71
|
+
commandColor: Theme::parse_rgb(&theme.colors.commandColor),
|
|
72
|
+
subcommandColor: Theme::parse_rgb(&theme.colors.subcommandColor),
|
|
73
|
+
argColor: Theme::parse_rgb(&theme.colors.argColor),
|
|
74
|
+
descriptionColor: Theme::parse_rgb(&theme.colors.descriptionColor),
|
|
75
|
+
errorPrefixColor: Theme::parse_rgb(&theme.colors.errorPrefixColor),
|
|
76
|
+
highlightColor: Theme::parse_rgb(&theme.colors.highlightColor),
|
|
77
|
+
},
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fn parse_rgb(rgb_str: &Option<String>) -> Option<Color> {
|
|
82
|
+
match rgb_str {
|
|
83
|
+
Some(rgb) => {
|
|
84
|
+
let trimmed = rgb
|
|
85
|
+
.strip_prefix("rgb(")
|
|
86
|
+
.and_then(|s| s.strip_suffix(')'))
|
|
87
|
+
.map(|s| s.trim())?;
|
|
88
|
+
|
|
89
|
+
// 2. Split the remaining string by commas.
|
|
90
|
+
let parts: Vec<&str> = trimmed.split(',').collect();
|
|
91
|
+
|
|
92
|
+
if parts.len() == 3 {
|
|
93
|
+
// 3. Trim whitespace from each part and parse to a u8.
|
|
94
|
+
// `.parse()` returns a `Result`, so we use `.ok()` to convert to an `Option`,
|
|
95
|
+
// and the `?` operator to return early if any parse fails.
|
|
96
|
+
let r = parts[0].trim().parse::<u8>().ok()?;
|
|
97
|
+
let g = parts[1].trim().parse::<u8>().ok()?;
|
|
98
|
+
let b = parts[2].trim().parse::<u8>().ok()?;
|
|
99
|
+
|
|
100
|
+
Some(Color::TrueColor { r, g, b })
|
|
101
|
+
} else {
|
|
102
|
+
None
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
None => None,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
use colored::Color;
|
|
2
|
+
|
|
3
|
+
use crate::themes::theme_inputs::ThemeInputColors;
|
|
4
|
+
|
|
5
|
+
#[derive(Clone)]
|
|
6
|
+
pub struct ThemeColors {
|
|
7
|
+
pub prefixColor: Color,
|
|
8
|
+
pub commandColor: Color,
|
|
9
|
+
pub subcommandColor: Color,
|
|
10
|
+
pub argColor: Color,
|
|
11
|
+
pub descriptionColor: Color,
|
|
12
|
+
pub errorPrefixColor: Color,
|
|
13
|
+
pub highlightColor: Color,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
impl ThemeColors {
|
|
17
|
+
pub fn from_options(input: ThemeInputColors) -> ThemeColors {
|
|
18
|
+
ThemeColors {
|
|
19
|
+
prefixColor: input.prefixColor.unwrap_or(Color::BrightMagenta),
|
|
20
|
+
commandColor: input.commandColor.unwrap_or(Color::BrightBlue),
|
|
21
|
+
subcommandColor: input.subcommandColor.unwrap_or(Color::TrueColor {
|
|
22
|
+
r: 175,
|
|
23
|
+
g: 247,
|
|
24
|
+
b: 7,
|
|
25
|
+
}),
|
|
26
|
+
argColor: input.argColor.unwrap_or(Color::Green),
|
|
27
|
+
descriptionColor: input.descriptionColor.unwrap_or(Color::TrueColor {
|
|
28
|
+
r: 128,
|
|
29
|
+
g: 128,
|
|
30
|
+
b: 128,
|
|
31
|
+
}),
|
|
32
|
+
errorPrefixColor: input.errorPrefixColor.unwrap_or(Color::Red),
|
|
33
|
+
highlightColor: input.highlightColor.unwrap_or(Color::BrightBlue),
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|