mirror of
https://github.com/martinvonz/jj.git
synced 2024-12-30 16:19:35 +00:00
Put an RNG in user settings with configurable seed
Some hijinx are utilized to make it possible to generate random values using an immutable `&UserSettings` reference.
This commit is contained in:
parent
e8b21c5ce0
commit
79aaf117ea
4 changed files with 70 additions and 4 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -846,6 +846,7 @@ dependencies = [
|
|||
"pest_derive",
|
||||
"prost",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"regex",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
|
|
|
@ -31,6 +31,8 @@ maplit = "1.0.2"
|
|||
once_cell = "1.17.0"
|
||||
pest = "2.5.2"
|
||||
pest_derive = "2.5.2"
|
||||
rand = "0.8.5"
|
||||
rand_chacha = "0.3.1"
|
||||
regex = "1.7.0"
|
||||
serde_json = "1.0.91"
|
||||
tempfile = "3.3.0"
|
||||
|
@ -46,7 +48,6 @@ prost = "0.11.5"
|
|||
assert_matches = "1.5.0"
|
||||
insta = "1.23.0"
|
||||
num_cpus = "1.15.0"
|
||||
rand = "0.8.5"
|
||||
test-case = "2.2.2"
|
||||
testutils = { path = "testutils" }
|
||||
|
||||
|
|
|
@ -13,8 +13,11 @@
|
|||
// limitations under the License.
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use chrono::DateTime;
|
||||
use rand::prelude::*;
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
|
||||
use crate::backend::{Signature, Timestamp};
|
||||
|
||||
|
@ -22,6 +25,7 @@ use crate::backend::{Signature, Timestamp};
|
|||
pub struct UserSettings {
|
||||
config: config::Config,
|
||||
timestamp: Option<Timestamp>,
|
||||
rng: Arc<JJRng>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -39,10 +43,22 @@ fn get_timestamp_config(config: &config::Config, key: &str) -> Option<Timestamp>
|
|||
}
|
||||
}
|
||||
|
||||
fn get_rng_seed_config(config: &config::Config) -> Option<u64> {
|
||||
config
|
||||
.get_string("debug.randomness-seed")
|
||||
.ok()
|
||||
.and_then(|str| str.parse().ok())
|
||||
}
|
||||
|
||||
impl UserSettings {
|
||||
pub fn from_config(config: config::Config) -> Self {
|
||||
let timestamp = get_timestamp_config(&config, "user.timestamp");
|
||||
UserSettings { config, timestamp }
|
||||
let rng_seed = get_rng_seed_config(&config);
|
||||
UserSettings {
|
||||
config,
|
||||
timestamp,
|
||||
rng: Arc::new(JJRng::new(rng_seed)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn incorporate_toml_strings(
|
||||
|
@ -54,8 +70,13 @@ impl UserSettings {
|
|||
config_builder =
|
||||
config_builder.add_source(config::File::from_str(s, config::FileFormat::Toml));
|
||||
}
|
||||
self.config = config_builder.build()?;
|
||||
self.timestamp = get_timestamp_config(&self.config, "user.timestamp");
|
||||
let new_config = config_builder.build()?;
|
||||
let new_rng_seed = get_rng_seed_config(&new_config);
|
||||
if new_rng_seed != get_rng_seed_config(&self.config) {
|
||||
self.rng.reset(new_rng_seed);
|
||||
}
|
||||
self.timestamp = get_timestamp_config(&new_config, "user.timestamp");
|
||||
self.config = new_config;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -71,6 +92,10 @@ impl UserSettings {
|
|||
Ok(RepoSettings { _config: config })
|
||||
}
|
||||
|
||||
pub fn get_rng(&self) -> Arc<JJRng> {
|
||||
self.rng.clone()
|
||||
}
|
||||
|
||||
pub fn user_name(&self) -> String {
|
||||
self.config
|
||||
.get_string("user.name")
|
||||
|
@ -146,3 +171,37 @@ impl UserSettings {
|
|||
&self.config
|
||||
}
|
||||
}
|
||||
|
||||
/// This Rng uses interior mutability to allow generating random values using an
|
||||
/// immutable reference. It also fixes a specific seedable RNG for
|
||||
/// reproducibility.
|
||||
#[derive(Debug)]
|
||||
pub struct JJRng(Mutex<ChaCha20Rng>);
|
||||
impl JJRng {
|
||||
/// Wraps Rng::gen but only requires an immutable reference.
|
||||
pub fn gen<T>(&self) -> T
|
||||
where
|
||||
rand::distributions::Standard: rand::distributions::Distribution<T>,
|
||||
{
|
||||
let mut rng = self.0.lock().unwrap();
|
||||
rng.gen()
|
||||
}
|
||||
|
||||
/// Creates a new RNGs. Could be made public, but we'd like to encourage all
|
||||
/// RNGs references to point to the same RNG.
|
||||
fn new(seed: Option<u64>) -> Self {
|
||||
Self(Mutex::new(JJRng::internal_rng_from_seed(seed)))
|
||||
}
|
||||
|
||||
fn reset(&self, seed: Option<u64>) {
|
||||
let mut rng = self.0.lock().unwrap();
|
||||
*rng = JJRng::internal_rng_from_seed(seed)
|
||||
}
|
||||
|
||||
fn internal_rng_from_seed(seed: Option<u64>) -> ChaCha20Rng {
|
||||
match seed {
|
||||
Some(seed) => ChaCha20Rng::seed_from_u64(seed),
|
||||
None => ChaCha20Rng::from_entropy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,6 +100,11 @@ fn env_overrides() -> config::Config {
|
|||
if let Ok(value) = env::var("JJ_TIMESTAMP") {
|
||||
builder = builder.set_override("user.timestamp", value).unwrap();
|
||||
}
|
||||
if let Ok(value) = env::var("JJ_RANDOMNESS_SEED") {
|
||||
builder = builder
|
||||
.set_override("debug.randomness-seed", value)
|
||||
.unwrap();
|
||||
}
|
||||
if let Ok(value) = env::var("JJ_OP_TIMESTAMP") {
|
||||
builder = builder.set_override("operation.timestamp", value).unwrap();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue