Skip to content

Commit cccdf24

Browse files
committed
wip: allow CLI overrides for database layer
1 parent 783c9b7 commit cccdf24

File tree

18 files changed

+238
-106
lines changed

18 files changed

+238
-106
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sandpolis-client/src/cli.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ use clap::Parser;
44
pub struct ClientCommandLine {
55
/// Run client graphical UI (even if invoked from a terminal)
66
#[cfg(all(feature = "client-gui", feature = "client-tui"))]
7-
#[clap(long, conflicts_with = "tui")]
7+
#[clap(long, num_args = 0, conflicts_with = "tui")]
88
pub gui: bool,
99

1010
/// Run client terminal UI
1111
#[cfg(all(feature = "client-gui", feature = "client-tui"))]
12-
#[clap(long, conflicts_with = "gui")]
12+
#[clap(long, num_args = 0, conflicts_with = "gui")]
1313
pub tui: bool,
1414
}

sandpolis-core/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ version = "0.0.1"
77

88
[dependencies]
99
anyhow = { workspace = true }
10+
bevy = { workspace = true, optional = true }
11+
clap = { workspace = true }
1012
colored = { workspace = true }
1113
native_db = { workspace = true }
1214
native_model = { workspace = true }
@@ -15,7 +17,6 @@ serde = { workspace = true }
1517
strum = { workspace = true }
1618
uuid = { workspace = true }
1719
validator = { workspace = true }
18-
bevy = { workspace = true, optional = true }
1920

2021
[features]
2122
server = []

sandpolis-core/src/lib.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use anyhow::Result;
2+
use clap::Parser;
23
use colored::Color;
34
use native_db::ToKey;
45
use regex::Regex;
5-
use serde::{Deserialize, Serialize};
6+
use serde::{Deserialize, Serialize, de::DeserializeOwned};
67
use std::fmt::{Display, Write};
78
use std::ops::Deref;
89
use std::str::FromStr;
@@ -350,3 +351,21 @@ impl ToKey for UserName {
350351
vec!["UserName".to_string()]
351352
}
352353
}
354+
355+
/// A config fragment that can take overrides from the command line or from
356+
/// the process environment.
357+
pub trait LayerConfig<C>
358+
where
359+
C: Parser,
360+
Self: Serialize + DeserializeOwned,
361+
{
362+
/// Override the config with values from the command line
363+
fn override_cli(&mut self, args: &C) {
364+
// Default no-op
365+
}
366+
367+
/// Override the config with values from the environment
368+
fn override_env(&mut self) {
369+
// Default no-op
370+
}
371+
}

sandpolis-database/src/cli.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use clap::Parser;
2+
3+
#[derive(Parser, Debug, Clone, Default)]
4+
pub struct DatabaseCommandLine {
5+
/// Don't persist any data
6+
#[clap(long, num_args = 0)]
7+
pub ephemeral: bool,
8+
}

sandpolis-database/src/config.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
use anyhow::{Result, bail};
2+
use sandpolis_core::LayerConfig;
23
use serde::{Deserialize, Serialize};
34
use std::path::PathBuf;
5+
use tracing::trace;
46
use validator::Validate;
57

8+
use crate::cli::DatabaseCommandLine;
9+
610
#[derive(Serialize, Deserialize, Debug, Clone)]
711
pub struct DatabaseConfig {
812
/// Override default storage directory
@@ -32,6 +36,16 @@ impl Default for DatabaseConfig {
3236
}
3337
}
3438

39+
impl LayerConfig<DatabaseCommandLine> for DatabaseConfig {
40+
fn override_cli(&mut self, args: &DatabaseCommandLine) {
41+
if args.ephemeral {
42+
trace!("Overriding ephemeral flag from CLI");
43+
self.ephemeral = true;
44+
self.storage = None;
45+
}
46+
}
47+
}
48+
3549
impl DatabaseConfig {
3650
/// Create the storage directory if needed.
3751
pub fn get_storage_dir(&self) -> Result<Option<PathBuf>> {

sandpolis-database/src/lib.rs

Lines changed: 56 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::sync::{RwLock, RwLockReadGuard};
2020
use tokio_util::sync::CancellationToken;
2121
use tracing::{debug, trace, warn};
2222

23+
pub mod cli;
2324
pub mod config;
2425

2526
/// This layer manages separate databases for each realm.
@@ -37,6 +38,8 @@ pub struct DatabaseLayer {
3738
impl DatabaseLayer {
3839
/// Create a new `DatabaseLayer` initialized with the default realm.
3940
pub fn new(config: DatabaseConfig, models: &'static Models) -> Result<Self> {
41+
debug!("Initializing database layer");
42+
4043
let default = if let Some(path) = config.get_storage_dir()? {
4144
let path = path.join("default.db");
4245

@@ -149,11 +152,10 @@ impl RealmDatabase {
149152
} else {
150153
// Otherwise, watch the upper half of the primary ID which is the same for all
151154
// revisions
152-
self.0
153-
.watch()
154-
.scan()
155-
.primary()
156-
.range::<T, _>(item.id() & 0x0000_0000..=item.id() | 0xFFFF_FFFF)?
155+
self.0.watch().scan().primary().range::<T, _>(
156+
DataIdentifier((item.id().revision_id() as u64) << 32)
157+
..=DataIdentifier(((item.id().revision_id() as u64) << 32) | 0xFFFF_FFFF),
158+
)?
157159
};
158160

159161
// Safe to end the transaction once the watcher is registered
@@ -410,7 +412,36 @@ impl ToKey for DataRevision {
410412

411413
/// Uniquely identifies a record in the database. All revisions of the same
412414
/// `Data` share the upper 4 bytes of this value.
413-
pub type DataIdentifier = u64; // TODO tuple struct
415+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone, PartialOrd, Ord)]
416+
pub struct DataIdentifier(u64);
417+
418+
impl Default for DataIdentifier {
419+
fn default() -> Self {
420+
Self(rand::rng().next_u64())
421+
}
422+
}
423+
424+
impl ToKey for DataIdentifier {
425+
fn to_key(&self) -> native_db::Key {
426+
self.0.to_key()
427+
}
428+
429+
fn key_names() -> Vec<String> {
430+
vec!["DataIdentifier".to_string()]
431+
}
432+
}
433+
434+
impl DataIdentifier {
435+
/// Create a new `DataIdentifier` for a revision based on this one.
436+
fn new_revision(&self) -> Self {
437+
Self((self.0 & 0x0000_0000) | (0xFFFF_FFFF & rand::rng().next_u64()))
438+
}
439+
440+
/// Get the portion of the `DataIdentifier` shared across all revisions
441+
fn revision_id(&self) -> u32 {
442+
((self.0 >> 32) & 0xFFFF_FFFF) as u32
443+
}
444+
}
414445

415446
/// When some `Data` will expire and no longer be returnable by queries.
416447
/// Eventually it will be removed from the database altogether.
@@ -534,9 +565,7 @@ impl<T: Data> Resident<T> {
534565

535566
if previous.expiration().is_some() {
536567
// Derive new id from the previous
537-
next.set_id(
538-
(previous.id() & 0x0000_0000) | (0xFFFF_FFFF & rand::rng().random::<u64>()),
539-
);
568+
next.set_id(previous.id().new_revision());
540569

541570
rw.insert(next.clone())?;
542571
rw.upsert(previous.clone())?;
@@ -551,7 +580,7 @@ impl<T: Data> Resident<T> {
551580
}
552581

553582
pub fn history(&self, range: impl RangeBounds<DataCreation>) -> Result<Vec<T>> {
554-
let revision_id = self.read().id() & 0x0000_0000;
583+
let revision_id = self.read().id().revision_id();
555584

556585
let r = self.db.r_transaction()?;
557586

@@ -566,7 +595,7 @@ impl<T: Data> Resident<T> {
566595

567596
Ok(items
568597
.into_iter()
569-
.filter(|item| (item.id() & 0x0000_0000) == revision_id)
598+
.filter(|item| item.id().revision_id() == revision_id)
570599
.collect())
571600
}
572601
}
@@ -601,16 +630,15 @@ mod test_resident {
601630
async fn test_data() -> Result<()> {
602631
let database = test_db!(TestData);
603632

604-
let db = database.realm(RealmName::default()).await?;
633+
let db = database.realm(RealmName::default())?;
605634
let res: Resident<TestData> = db.resident(DataCondition::equal(TestDataKey::a, "A"))?;
606635

607636
// Update data a bunch of times
608637
for i in 1..10 {
609638
res.update(|data| {
610639
data.a = format!("test {i}");
611640
Ok(())
612-
})
613-
.await?;
641+
})?;
614642
}
615643

616644
// Resident should reflect "test 9"
@@ -649,16 +677,15 @@ mod test_resident {
649677
async fn test_temporal_data() -> Result<()> {
650678
let database = test_db!(TestHistoryData);
651679

652-
let db = database.realm(RealmName::default()).await?;
680+
let db = database.realm(RealmName::default())?;
653681
let res: Resident<TestHistoryData> = db.resident(())?;
654682

655683
// Update data a bunch of times
656684
for i in 1..10 {
657685
res.update(|data| {
658686
data.b = format!("test {i}");
659687
Ok(())
660-
})
661-
.await?;
688+
})?;
662689
}
663690

664691
// Database should have 10 items
@@ -670,7 +697,7 @@ mod test_resident {
670697

671698
// Check history
672699
{
673-
assert_eq!(res.history(DataCreation::all()).await?.len(), 10);
700+
assert_eq!(res.history(DataCreation::all())?.len(), 10);
674701
}
675702

676703
Ok(())
@@ -707,8 +734,8 @@ impl<T: Data> ResidentVec<T> {
707734
assert!(
708735
self.conditions
709736
.first()
710-
.expect("There must be at least one condition")
711-
.check(&d)
737+
.map(|condition| condition.check(&d))
738+
.unwrap_or(true)
712739
);
713740

714741
// Make sure the remaining conditions are satisfied
@@ -920,30 +947,27 @@ mod test_resident_vec {
920947
async fn test_nonhistorical() -> Result<()> {
921948
let database = test_db!(TestData);
922949

923-
let db = database.realm(RealmName::default()).await?;
950+
let db = database.realm(RealmName::default())?;
924951
let test_data: ResidentVec<TestData> =
925952
db.resident_vec(DataCondition::equal(TestDataKey::b, "B"))?;
926953

927-
assert_eq!(test_data.len().await, 0);
954+
assert_eq!(test_data.len(), 0);
928955

929956
// Add item
930-
let data: Resident<TestData> = test_data
931-
.push(TestData {
932-
a: "A".to_string(),
933-
b: "B".to_string(),
934-
..Default::default()
935-
})
936-
.await?;
957+
let data: Resident<TestData> = test_data.push(TestData {
958+
a: "A".to_string(),
959+
b: "B".to_string(),
960+
..Default::default()
961+
})?;
937962

938-
assert_eq!(test_data.len().await, 1);
963+
assert_eq!(test_data.len(), 1);
939964

940965
// Update a bunch of times
941966
for i in 1..10 {
942967
data.update(|d| {
943968
d.a = format!("test {i}");
944969
Ok(())
945-
})
946-
.await?;
970+
})?;
947971
}
948972

949973
// Database should reflect "test 9"

sandpolis-instance/src/lib.rs

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use anyhow::Result;
2-
use clap::Parser;
32
use native_db::ToKey;
43
use native_model::Model;
54
use sandpolis_core::{ClusterId, InstanceId, RealmName};
@@ -74,21 +73,3 @@ impl PartialOrd for LayerVersion {
7473
}
7574
}
7675
}
77-
78-
/// A config fragment that can take overrides from the command line or from
79-
/// the process environment.
80-
pub trait OverridableConfig<C>
81-
where
82-
C: Parser,
83-
Self: Serialize + DeserializeOwned,
84-
{
85-
/// Override the config with values from the command line
86-
fn override_cli(&mut self, args: &C) {
87-
// Default no-op
88-
}
89-
90-
/// Override the config with values from the environment
91-
fn override_env(&mut self) {
92-
// Default no-op
93-
}
94-
}

sandpolis-mobile/android/app/src/main/java/org/sandpolis/mobile/AgentService.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,12 @@ private void startForeground() {
4242
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
4343
type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
4444
}
45-
ServiceCompat.startForeground(
46-
/* service = */ this,
47-
/* id = */ 100, // Cannot be 0
48-
/* notification = */ notification,
49-
/* foregroundServiceType = */ type
50-
);
45+
// ServiceCompat.startForeground(
46+
// /* service = */ this,
47+
// /* id = */ 100, // Cannot be 0
48+
// /* notification = */ notification,
49+
// /* foregroundServiceType = */ type
50+
// );
5151
} catch (Exception e) {
5252
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
5353
e instanceof ForegroundServiceStartNotAllowedException

sandpolis-network/src/config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{ServerAddress, cli::NetworkCommandLine};
2-
use sandpolis_instance::OverridableConfig;
2+
use sandpolis_core::LayerConfig;
33
use serde::{Deserialize, Serialize};
44
use tracing::debug;
55

@@ -17,7 +17,7 @@ pub struct NetworkLayerConfig {
1717
pub poll: Option<String>,
1818
}
1919

20-
impl OverridableConfig<NetworkCommandLine> for NetworkLayerConfig {
20+
impl LayerConfig<NetworkCommandLine> for NetworkLayerConfig {
2121
fn override_env(&mut self) {
2222
match std::env::var("S7S_SERVER") {
2323
Ok(server) => {

0 commit comments

Comments
 (0)