1use std::path::Path;
16use std::sync::{Arc, Mutex};
17
18use chrono::DateTime;
19use rand::prelude::*;
20use rand_chacha::ChaCha20Rng;
21
22use crate::backend::{ChangeId, ObjectId, Signature, Timestamp};
23
24#[derive(Debug, Clone)]
25pub struct UserSettings {
26 config: config::Config,
27 timestamp: Option<Timestamp>,
28 rng: Arc<JJRng>,
29}
30
31#[derive(Debug, Clone)]
32pub struct RepoSettings {
33 _config: config::Config,
34}
35
36#[derive(Debug, Clone)]
37pub struct GitSettings {
38 pub auto_local_branch: bool,
39}
40
41impl GitSettings {
42 pub fn from_config(config: &config::Config) -> Self {
43 GitSettings {
44 auto_local_branch: config.get_bool("git.auto-local-branch").unwrap_or(true),
45 }
46 }
47}
48
49impl Default for GitSettings {
50 fn default() -> Self {
51 GitSettings {
52 auto_local_branch: true,
53 }
54 }
55}
56
57fn get_timestamp_config(config: &config::Config, key: &str) -> Option<Timestamp> {
58 match config.get_string(key) {
59 Ok(timestamp_str) => match DateTime::parse_from_rfc3339(×tamp_str) {
60 Ok(datetime) => Some(Timestamp::from_datetime(datetime)),
61 Err(_) => None,
62 },
63 Err(_) => None,
64 }
65}
66
67fn get_rng_seed_config(config: &config::Config) -> Option<u64> {
68 config
69 .get_string("debug.randomness-seed")
70 .ok()
71 .and_then(|str| str.parse().ok())
72}
73
74impl UserSettings {
75 pub fn from_config(config: config::Config) -> Self {
76 let timestamp = get_timestamp_config(&config, "debug.commit-timestamp");
77 let rng_seed = get_rng_seed_config(&config);
78 UserSettings {
79 config,
80 timestamp,
81 rng: Arc::new(JJRng::new(rng_seed)),
82 }
83 }
84
85 pub fn with_repo(&self, _repo_path: &Path) -> Result<RepoSettings, config::ConfigError> {
88 let config = self.config.clone();
89 Ok(RepoSettings { _config: config })
90 }
91
92 pub fn get_rng(&self) -> Arc<JJRng> {
93 self.rng.clone()
94 }
95
96 pub fn user_name(&self) -> String {
97 self.config
98 .get_string("user.name")
99 .unwrap_or_else(|_| Self::user_name_placeholder().to_string())
100 }
101
102 pub fn user_name_placeholder() -> &'static str {
103 "(no name configured)"
104 }
105
106 pub fn user_email(&self) -> String {
107 self.config
108 .get_string("user.email")
109 .unwrap_or_else(|_| Self::user_email_placeholder().to_string())
110 }
111
112 pub fn user_email_placeholder() -> &'static str {
113 "(no email configured)"
114 }
115
116 pub fn operation_timestamp(&self) -> Option<Timestamp> {
117 get_timestamp_config(&self.config, "debug.operation-timestamp")
118 }
119
120 pub fn operation_hostname(&self) -> String {
121 self.config
122 .get_string("operation.hostname")
123 .unwrap_or_else(|_| whoami::hostname())
124 }
125
126 pub fn operation_username(&self) -> String {
127 self.config
128 .get_string("operation.username")
129 .unwrap_or_else(|_| whoami::username())
130 }
131
132 pub fn push_branch_prefix(&self) -> String {
133 self.config
134 .get_string("push.branch-prefix")
135 .unwrap_or_else(|_| "push-".to_string())
136 }
137
138 pub fn default_revset(&self) -> String {
139 self.config
140 .get_string("ui.default-revset")
141 .unwrap_or_else(|_| {
142 "@ | (remote_branches() | tags()).. | ((remote_branches() | tags())..)-".to_string()
143 })
144 }
145
146 pub fn signature(&self) -> Signature {
147 let timestamp = self.timestamp.clone().unwrap_or_else(Timestamp::now);
148 Signature {
149 name: self.user_name(),
150 email: self.user_email(),
151 timestamp,
152 }
153 }
154
155 pub fn allow_native_backend(&self) -> bool {
156 self.config
157 .get_bool("ui.allow-init-native")
158 .unwrap_or(false)
159 }
160
161 pub fn oplog_relative_timestamps(&self) -> bool {
162 self.config
163 .get_bool("ui.oplog-relative-timestamps")
164 .unwrap_or(true)
165 }
166
167 pub fn config(&self) -> &config::Config {
168 &self.config
169 }
170
171 pub fn git_settings(&self) -> GitSettings {
172 GitSettings::from_config(&self.config)
173 }
174
175 pub fn graph_style(&self) -> String {
176 self.config
177 .get_string("ui.graph.style")
178 .unwrap_or_else(|_| "curved".to_string())
179 }
180}
181
182#[derive(Debug)]
186pub struct JJRng(Mutex<ChaCha20Rng>);
187impl JJRng {
188 pub fn new_change_id(&self, length: usize) -> ChangeId {
189 let mut rng = self.0.lock().unwrap();
190 let random_bytes = (0..length).map(|_| rng.gen::<u8>()).collect();
191 ChangeId::new(random_bytes)
192 }
193
194 fn new(seed: Option<u64>) -> Self {
197 Self(Mutex::new(JJRng::internal_rng_from_seed(seed)))
198 }
199
200 fn internal_rng_from_seed(seed: Option<u64>) -> ChaCha20Rng {
201 match seed {
202 Some(seed) => ChaCha20Rng::seed_from_u64(seed),
203 None => ChaCha20Rng::from_entropy(),
204 }
205 }
206}