commit bf304895261f36cccb6ceddf970fb7fbb414e7c3
parent 1be8e001f8e065164af6b1ff88a65c10eb2c8214
Author: triesap <tyson@radroots.org>
Date: Sun, 9 Aug 2026 19:10:37 +0000
ffi: rename the HarvestCircle cross-language API
- rename exported core, error, and observer types across Rust and Kotlin
- align runtime threads, properties, development paths, and probe variables
- refresh the normalized FFI contract digest
- verify checks and native bindings through both build lanes
Diffstat:
11 files changed, 126 insertions(+), 119 deletions(-)
diff --git a/app/desktop/build.gradle.kts b/app/desktop/build.gradle.kts
@@ -663,7 +663,7 @@ tasks.named("runKtlintFormatOverMainSourceSet") {
tasks.withType<Test>().configureEach {
dependsOn(buildRustCoreDebug)
environment(
- "RADROOTS_STUDIO_DEVELOPMENT_DATA_DIR",
+ "HARVESTCIRCLE_DEVELOPMENT_DATA_DIR",
layout.buildDirectory
.dir("native-test-data")
.get()
@@ -676,7 +676,7 @@ tasks.withType<Test>().configureEach {
}
tasks.withType<JavaExec>().configureEach {
dependsOn(buildRustCoreDebug)
- systemProperty("radroots.studio.development", "true")
+ systemProperty("harvestcircle.development", "true")
systemProperty(
"jna.library.path",
rustDebugLibrary.parentFile.absolutePath,
@@ -713,7 +713,7 @@ compose.desktop {
packageName = applicationName
packageVersion = installableVersion
- description = "Radroots Studio $appVersion"
+ description = "Radroots HarvestCircle $appVersion"
copyright = "Copyright © 2024 Radroots, Inc."
vendor = "Radroots, Inc"
diff --git a/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/HarvestCircleAppStore.kt b/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/HarvestCircleAppStore.kt
@@ -10,7 +10,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.radroots.harvestcircle.ffi.AppLifecycleDto
import org.radroots.harvestcircle.ffi.AppSnapshotDto
-import org.radroots.harvestcircle.ffi.StudioException
+import org.radroots.harvestcircle.ffi.HarvestCircleException
import org.radroots.harvestcircle.ffi.WireErrorCode
import org.radroots.harvestcircle.ffi.WireRecoveryAction
@@ -435,7 +435,7 @@ class HarvestCircleAppStore(
}
private fun acceptFailure(error: Throwable) {
- val native = error as? StudioException.Failure
+ val native = error as? HarvestCircleException.Failure
val gatewayFailure = (error as? HarvestCircleGatewayException)?.failure
mutableState.value =
mutableState.value.copy(
diff --git a/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/HarvestCircleApplication.kt b/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/HarvestCircleApplication.kt
@@ -9,8 +9,8 @@ import org.radroots.harvestcircle.accounts.ui.HarvestCircleScreen
import org.radroots.harvestcircle.accounts.ui.HarvestCircleUiActions
import org.radroots.harvestcircle.accounts.ui.StartupFailureScreen
import org.radroots.harvestcircle.accounts.ui.toUiModel
-import org.radroots.harvestcircle.ffi.StudioAppCore
-import org.radroots.harvestcircle.ffi.StudioException
+import org.radroots.harvestcircle.ffi.HarvestCircleAppCore
+import org.radroots.harvestcircle.ffi.HarvestCircleException
import org.radroots.harvestcircle.ffi.compatibilityDescriptor
internal typealias HarvestCircleStoreFactory = (CoroutineScope) -> HarvestCircleAppStore
@@ -23,7 +23,7 @@ fun HarvestCircleApplication(storeFactory: HarvestCircleStoreFactory = ::createH
if (store == null) {
val error = storeResult.exceptionOrNull()
val message =
- (error as? StudioException.Failure)?.safeMessage
+ (error as? HarvestCircleException.Failure)?.safeMessage
?: "The application could not start."
StartupFailureScreen(message)
return
@@ -65,10 +65,10 @@ fun HarvestCircleApplication(storeFactory: HarvestCircleStoreFactory = ::createH
}
internal fun createHarvestCircleAppStore(scope: CoroutineScope): HarvestCircleAppStore {
- val developmentMode = java.lang.Boolean.getBoolean("radroots.studio.development")
+ val developmentMode = java.lang.Boolean.getBoolean("harvestcircle.development")
val descriptor = compatibilityDescriptor()
val core =
- StudioAppCore.openCompatible(
+ HarvestCircleAppCore.openCompatible(
expectation = verifyNativeCompatibility(descriptor),
developmentMode = developmentMode,
)
diff --git a/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/HarvestCircleCoreGateway.kt b/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/HarvestCircleCoreGateway.kt
@@ -5,13 +5,13 @@ import kotlinx.coroutines.runBlocking
import org.radroots.harvestcircle.ffi.AccountDto
import org.radroots.harvestcircle.ffi.AppSnapshotDto
import org.radroots.harvestcircle.ffi.GeneratedRecoveryRequest
+import org.radroots.harvestcircle.ffi.HarvestCircleAppCore
+import org.radroots.harvestcircle.ffi.HarvestCircleChangeObserver
+import org.radroots.harvestcircle.ffi.HarvestCircleException
import org.radroots.harvestcircle.ffi.ObserverSubscription
import org.radroots.harvestcircle.ffi.RemovalRequest
import org.radroots.harvestcircle.ffi.RequestContextDto
import org.radroots.harvestcircle.ffi.SnapshotChangeDto
-import org.radroots.harvestcircle.ffi.StudioAppCore
-import org.radroots.harvestcircle.ffi.StudioChangeObserver
-import org.radroots.harvestcircle.ffi.StudioException
import org.radroots.harvestcircle.ffi.WireErrorCategory
import org.radroots.harvestcircle.ffi.WireErrorCode
import org.radroots.harvestcircle.ffi.WireRecoveryAction
@@ -107,7 +107,7 @@ interface HarvestCircleCoreGateway : AutoCloseable {
}
class NativeHarvestCircleCoreGateway(
- private val core: StudioAppCore,
+ private val core: HarvestCircleAppCore,
) : HarvestCircleCoreGateway {
private val nextRequest = AtomicLong(1)
private val shutdownLock = Any()
@@ -118,7 +118,7 @@ class NativeHarvestCircleCoreGateway(
override suspend fun subscribeChanges(onChange: (HarvestCircleChange) -> Unit): AutoCloseable {
val subscription =
core.subscribeChangesV2(
- object : StudioChangeObserver {
+ object : HarvestCircleChangeObserver {
override fun onChange(change: SnapshotChangeDto) {
onChange(HarvestCircleChange(change.snapshot, change.previousRevision))
}
@@ -219,7 +219,7 @@ internal fun Throwable.toHarvestCircleCommandFailure(
fallbackCorrelationId: String,
fallbackSafeMessage: String = "The application command failed.",
): HarvestCircleCommandFailure {
- val native = this as? StudioException.Failure
+ val native = this as? HarvestCircleException.Failure
return HarvestCircleCommandFailure(
code = native?.code ?: WireErrorCode.INTERNAL,
category = native?.category ?: WireErrorCategory.INTERNAL,
@@ -256,7 +256,7 @@ private class NativeRemovalTicket(
}
private class NativeGeneratedRecoveryTicket(
- private val core: StudioAppCore,
+ private val core: HarvestCircleAppCore,
private val request: GeneratedRecoveryRequest,
private val requestContext: () -> RequestContextDto,
override val requestId: String,
diff --git a/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/NativeCompatibility.kt b/app/desktop/src/main/kotlin/org/radroots/harvestcircle/application/NativeCompatibility.kt
@@ -5,7 +5,7 @@ import org.radroots.harvestcircle.ffi.CompatibilityExpectation
internal const val EXPECTED_PRODUCT_VERSION = "0.1.0-alpha"
internal const val EXPECTED_CARGO_PACKAGE_VERSION = "0.1.0-alpha"
-internal const val EXPECTED_FFI_CONTRACT_HASH = "249b8f9ec8ce64e33d5b88dd52d7dc646a2d5b6993c738e71253964531d6d210"
+internal const val EXPECTED_FFI_CONTRACT_HASH = "d4e298f0abeaa65aa68e70d7a6e8f69f8182f12f93c12b2dd056d3ed5d83e9c0"
internal val EXPECTED_FFI_CONTRACT_MAJOR: UShort = 3.toUShort()
internal val MINIMUM_FFI_CONTRACT_MINOR: UShort = 0.toUShort()
internal const val MINIMUM_STORAGE_SCHEMA: UInt = 5U
diff --git a/app/desktop/src/test/kotlin/org/radroots/harvestcircle/application/NativeGeneratedRecoveryTest.kt b/app/desktop/src/test/kotlin/org/radroots/harvestcircle/application/NativeGeneratedRecoveryTest.kt
@@ -1,7 +1,7 @@
package org.radroots.harvestcircle.application
import kotlinx.coroutines.test.runTest
-import org.radroots.harvestcircle.ffi.StudioAppCore
+import org.radroots.harvestcircle.ffi.HarvestCircleAppCore
import org.radroots.harvestcircle.ffi.compatibilityDescriptor
import java.nio.file.Files
import java.nio.file.Path
@@ -14,7 +14,7 @@ import kotlin.test.assertTrue
class NativeGeneratedRecoveryTest {
private val dataDirectory =
- Path.of(checkNotNull(System.getenv("RADROOTS_STUDIO_DEVELOPMENT_DATA_DIR")))
+ Path.of(checkNotNull(System.getenv("HARVESTCIRCLE_DEVELOPMENT_DATA_DIR")))
@BeforeTest
fun prepareDataDirectory() {
@@ -31,7 +31,7 @@ class NativeGeneratedRecoveryTest {
fun generatedRecoveryCrossesTheNativeBoundaryAndCancelsWithoutPersistence() =
runTest {
val core =
- StudioAppCore.openCompatible(
+ HarvestCircleAppCore.openCompatible(
expectation = verifyNativeCompatibility(compatibilityDescriptor()),
developmentMode = true,
)
diff --git a/core/crates/harvestcircle_ffi/src/commands.rs b/core/crates/harvestcircle_ffi/src/commands.rs
@@ -31,7 +31,7 @@ const DATABASE_QUALIFIER: &str = "org";
const DATABASE_ORGANIZATION: &str = "radroots";
const DATABASE_APPLICATION: &str = "studio";
const DATABASE_FILENAME: &str = "studio.sqlite3";
-const DEVELOPMENT_DATA_DIR_ENVIRONMENT: &str = "RADROOTS_STUDIO_DEVELOPMENT_DATA_DIR";
+const DEVELOPMENT_DATA_DIR_ENVIRONMENT: &str = "HARVESTCIRCLE_DEVELOPMENT_DATA_DIR";
pub(crate) const ACTOR_MAILBOX_CAPACITY: usize = 64;
const MAX_COMMAND_DEADLINE_MILLIS: u64 = 30_000;
@@ -88,7 +88,7 @@ pub fn compatibility_descriptor() -> CompatibilityDescriptor {
#[derive(Debug)]
#[cfg_attr(not(coverage_nightly), derive(uniffi::Error))]
-pub enum StudioError {
+pub enum HarvestCircleError {
Failure {
code: WireErrorCode,
category: WireErrorCategory,
@@ -99,7 +99,7 @@ pub enum StudioError {
},
}
-impl Display for StudioError {
+impl Display for HarvestCircleError {
fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Failure { safe_message, .. } => formatter.write_str(safe_message),
@@ -107,9 +107,9 @@ impl Display for StudioError {
}
}
-impl std::error::Error for StudioError {}
+impl std::error::Error for HarvestCircleError {}
-impl From<SafeError> for StudioError {
+impl From<SafeError> for HarvestCircleError {
fn from(error: SafeError) -> Self {
let (category, retryable, recovery_action) = error_policy(error.code());
Self::Failure {
@@ -123,7 +123,7 @@ impl From<SafeError> for StudioError {
}
}
-impl StudioError {
+impl HarvestCircleError {
fn correlated(error: SafeError, correlation_id: &str) -> Self {
let (category, retryable, recovery_action) = error_policy(error.code());
Self::Failure {
@@ -158,11 +158,11 @@ impl GeneratedRecoveryRequest {
/// # Errors
///
/// Returns a safe unavailable error after the first read.
- pub fn take_recovery_nsec(&self) -> Result<String, StudioError> {
+ pub fn take_recovery_nsec(&self) -> Result<String, HarvestCircleError> {
self.handle
.take_recovery_nsec()
.map(|nsec| nsec.with_exposed_secret(str::to_owned))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
}
@@ -230,12 +230,12 @@ impl RuntimeCore {
}
#[cfg_attr(not(coverage_nightly), derive(uniffi::Object))]
-pub struct StudioAppCore {
+pub struct HarvestCircleAppCore {
pub(crate) inner: Arc<RuntimeCore>,
}
#[cfg_attr(not(coverage_nightly), uniffi::export)]
-impl StudioAppCore {
+impl HarvestCircleAppCore {
/// Verifies the static contract before touching the application data path.
///
/// # Errors
@@ -246,7 +246,7 @@ impl StudioAppCore {
pub fn open_compatible(
expectation: CompatibilityExpectation,
development_mode: bool,
- ) -> Result<Arc<Self>, StudioError> {
+ ) -> Result<Arc<Self>, HarvestCircleError> {
let path = application_database_path(development_mode)?;
Self::open_path_compatible(&path, &expectation, development_mode)
}
@@ -256,13 +256,13 @@ impl StudioAppCore {
/// # Errors
///
/// Returns a safe storage, recovery, or application-state error.
- pub async fn bootstrap(&self) -> Result<AppSnapshotDto, StudioError> {
+ pub async fn bootstrap(&self) -> Result<AppSnapshotDto, HarvestCircleError> {
self.inner
.actor
.bootstrap()
.await
.map(|snapshot| self.inner.dto_for(&snapshot))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
#[must_use]
@@ -277,7 +277,7 @@ impl StudioAppCore {
/// Returns a safe key-generation, conflict, timeout, or lifecycle error.
pub async fn begin_generated_account_v2(
&self,
- ) -> Result<Arc<GeneratedRecoveryRequest>, StudioError> {
+ ) -> Result<Arc<GeneratedRecoveryRequest>, HarvestCircleError> {
self.inner
.actor
.begin_generated_key_stage()
@@ -288,7 +288,7 @@ impl StudioAppCore {
resolved: AtomicBool::new(false),
})
})
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Acknowledges recovery and commits the generated account once.
@@ -301,12 +301,12 @@ impl StudioAppCore {
&self,
context: RequestContextDto,
request: Arc<GeneratedRecoveryRequest>,
- ) -> Result<AppSnapshotDto, StudioError> {
+ ) -> Result<AppSnapshotDto, HarvestCircleError> {
if request.resolved.swap(true, Ordering::AcqRel) {
return Err(generated_recovery_expired());
}
let request_id = DurableRequestId::parse(context.request_id.clone())
- .map_err(|error| StudioError::correlated(error, &context.request_id))?;
+ .map_err(|error| HarvestCircleError::correlated(error, &context.request_id))?;
let timeout = command_timeout(context.deadline_millis, &context.request_id)?;
self.inner
.actor
@@ -329,7 +329,7 @@ impl StudioAppCore {
pub async fn cancel_generated_account_v2(
&self,
request: Arc<GeneratedRecoveryRequest>,
- ) -> Result<bool, StudioError> {
+ ) -> Result<bool, HarvestCircleError> {
if request.resolved.swap(true, Ordering::AcqRel) {
return Ok(false);
}
@@ -337,7 +337,7 @@ impl StudioAppCore {
.actor
.cancel_generated_key_stage()
.await
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Imports or repairs an account using a caller-owned idempotency key.
@@ -349,12 +349,12 @@ impl StudioAppCore {
&self,
context: RequestContextDto,
secret_key: Vec<u8>,
- ) -> Result<AccountCommandReceiptDto, StudioError> {
+ ) -> Result<AccountCommandReceiptDto, HarvestCircleError> {
let request_id = DurableRequestId::parse(context.request_id.clone())
- .map_err(|error| StudioError::correlated(error, &context.request_id))?;
+ .map_err(|error| HarvestCircleError::correlated(error, &context.request_id))?;
let timeout = command_timeout(context.deadline_millis, &context.request_id)?;
let input = SecretKeyInput::parse_bytes(secret_key)
- .map_err(|error| StudioError::correlated(error, &context.request_id))?;
+ .map_err(|error| HarvestCircleError::correlated(error, &context.request_id))?;
self.inner
.actor
.import_secret_key(
@@ -372,7 +372,7 @@ impl StudioAppCore {
snapshot,
}
})
- .map_err(|error| StudioError::correlated(error, &context.request_id))
+ .map_err(|error| HarvestCircleError::correlated(error, &context.request_id))
}
/// Selects one saved account without activating it.
@@ -383,14 +383,14 @@ impl StudioAppCore {
pub async fn select_account(
&self,
public_key_hex: String,
- ) -> Result<AppSnapshotDto, StudioError> {
+ ) -> Result<AppSnapshotDto, HarvestCircleError> {
let public_key = parse_public_key(&public_key_hex)?;
self.inner
.actor
.select_account(public_key)
.await
.map(|snapshot| self.inner.dto_for(&snapshot))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Activates one saved account after validating its credential.
@@ -401,14 +401,14 @@ impl StudioAppCore {
pub async fn activate_account(
&self,
public_key_hex: String,
- ) -> Result<AppSnapshotDto, StudioError> {
+ ) -> Result<AppSnapshotDto, HarvestCircleError> {
let public_key = parse_public_key(&public_key_hex)?;
self.inner
.actor
.activate_account(public_key)
.await
.map(|snapshot| self.inner.dto_for(&snapshot))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Signs out while retaining accounts and credentials.
@@ -416,13 +416,13 @@ impl StudioAppCore {
/// # Errors
///
/// Returns a safe application-state error.
- pub async fn sign_out(&self) -> Result<AppSnapshotDto, StudioError> {
+ pub async fn sign_out(&self) -> Result<AppSnapshotDto, HarvestCircleError> {
self.inner
.actor
.sign_out()
.await
.map(|snapshot| self.inner.dto_for(&snapshot))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Refreshes the active Nostr profile from configured relays.
@@ -430,13 +430,13 @@ impl StudioAppCore {
/// # Errors
///
/// Returns a safe storage or application-state error.
- pub async fn refresh_active_profile(&self) -> Result<AppSnapshotDto, StudioError> {
+ pub async fn refresh_active_profile(&self) -> Result<AppSnapshotDto, HarvestCircleError> {
self.inner
.actor
.refresh_active_profile()
.await
.map(|snapshot| self.inner.dto_for(&snapshot))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Issues a revision-bound removal confirmation object.
@@ -447,7 +447,7 @@ impl StudioAppCore {
pub async fn request_account_removal(
&self,
public_key_hex: String,
- ) -> Result<Arc<RemovalRequest>, StudioError> {
+ ) -> Result<Arc<RemovalRequest>, HarvestCircleError> {
let public_key = parse_public_key(&public_key_hex)?;
self.inner
.actor
@@ -463,7 +463,7 @@ impl StudioAppCore {
token: Mutex::new(Some(token)),
})
})
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
/// Permanently removes the account represented by a one-time request.
@@ -475,7 +475,7 @@ impl StudioAppCore {
&self,
context: RequestContextDto,
request: Arc<RemovalRequest>,
- ) -> Result<AppSnapshotDto, StudioError> {
+ ) -> Result<AppSnapshotDto, HarvestCircleError> {
let token = request
.token
.lock()
@@ -483,7 +483,7 @@ impl StudioAppCore {
.take()
.ok_or_else(confirmation_expired)?;
let request_id = DurableRequestId::parse(context.request_id.clone())
- .map_err(|error| StudioError::correlated(error, &context.request_id))?;
+ .map_err(|error| HarvestCircleError::correlated(error, &context.request_id))?;
let timeout = command_timeout(context.deadline_millis, &context.request_id)?;
self.inner
.actor
@@ -495,11 +495,11 @@ impl StudioAppCore {
)
.await
.map(|snapshot| self.inner.dto_for(&snapshot))
- .map_err(StudioError::from)
+ .map_err(HarvestCircleError::from)
}
}
-fn verify_compatibility(expectation: &CompatibilityExpectation) -> Result<(), StudioError> {
+fn verify_compatibility(expectation: &CompatibilityExpectation) -> Result<(), HarvestCircleError> {
let actual = compatibility_descriptor();
if expectation.contract_major != actual.contract_major
|| expectation.minimum_contract_minor > actual.contract_minor
@@ -512,12 +512,12 @@ fn verify_compatibility(expectation: &CompatibilityExpectation) -> Result<(), St
Ok(())
}
-impl StudioAppCore {
+impl HarvestCircleAppCore {
fn open_path_compatible(
path: &Path,
expectation: &CompatibilityExpectation,
development_mode: bool,
- ) -> Result<Arc<Self>, StudioError> {
+ ) -> Result<Arc<Self>, HarvestCircleError> {
verify_compatibility(expectation)?;
std::fs::create_dir_all(path.parent().ok_or_else(path_unavailable)?)
.map_err(|_| path_unavailable())?;
@@ -528,7 +528,7 @@ impl StudioAppCore {
// SQLite ownership. Platform installation lanes exercise this adapter;
// deterministic coverage owns the compatibility and runtime policies.
#[cfg_attr(coverage_nightly, coverage(off))]
- fn open_path(path: &Path, development_mode: bool) -> Result<Arc<Self>, StudioError> {
+ fn open_path(path: &Path, development_mode: bool) -> Result<Arc<Self>, HarvestCircleError> {
let mode = if development_mode {
RelayRuntimeMode::Development
} else {
@@ -585,7 +585,7 @@ impl Clock for SystemClock {
// ProjectDirs and the process environment are host integration boundaries.
#[cfg_attr(coverage_nightly, coverage(off))]
-fn application_database_path(development_mode: bool) -> Result<PathBuf, StudioError> {
+fn application_database_path(development_mode: bool) -> Result<PathBuf, HarvestCircleError> {
if development_mode && let Some(directory) = std::env::var_os(DEVELOPMENT_DATA_DIR_ENVIRONMENT)
{
return Ok(PathBuf::from(directory).join(DATABASE_FILENAME));
@@ -599,13 +599,13 @@ fn application_database_path(development_mode: bool) -> Result<PathBuf, StudioEr
.ok_or_else(path_unavailable)
}
-fn parse_public_key(value: &str) -> Result<PublicKey, StudioError> {
- PublicKey::from_hex(value).map_err(StudioError::from)
+fn parse_public_key(value: &str) -> Result<PublicKey, HarvestCircleError> {
+ PublicKey::from_hex(value).map_err(HarvestCircleError::from)
}
-fn command_timeout(millis: u64, correlation_id: &str) -> Result<Duration, StudioError> {
+fn command_timeout(millis: u64, correlation_id: &str) -> Result<Duration, HarvestCircleError> {
if millis == 0 || millis > MAX_COMMAND_DEADLINE_MILLIS {
- return Err(StudioError::Failure {
+ return Err(HarvestCircleError::Failure {
code: WireErrorCode::InvalidApplicationState,
category: WireErrorCategory::Input,
retryable: false,
@@ -617,13 +617,13 @@ fn command_timeout(millis: u64, correlation_id: &str) -> Result<Duration, Studio
Ok(Duration::from_millis(millis))
}
-pub(crate) fn runtime() -> Result<&'static tokio::runtime::Runtime, StudioError> {
+pub(crate) fn runtime() -> Result<&'static tokio::runtime::Runtime, HarvestCircleError> {
static RUNTIME: OnceLock<Result<tokio::runtime::Runtime, ()>> = OnceLock::new();
RUNTIME
.get_or_init(|| {
tokio::runtime::Builder::new_multi_thread()
.enable_all()
- .thread_name("radroots-studio-core")
+ .thread_name("harvestcircle-core")
.build()
.map_err(|_| ())
})
@@ -631,12 +631,12 @@ pub(crate) fn runtime() -> Result<&'static tokio::runtime::Runtime, StudioError>
.map_err(|()| runtime_unavailable())
}
-fn actor_mailbox_capacity() -> Result<NonZeroUsize, StudioError> {
+fn actor_mailbox_capacity() -> Result<NonZeroUsize, HarvestCircleError> {
NonZeroUsize::new(ACTOR_MAILBOX_CAPACITY).ok_or_else(runtime_unavailable)
}
-fn runtime_unavailable() -> StudioError {
- StudioError::Failure {
+fn runtime_unavailable() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: WireErrorCode::InvalidApplicationState,
category: WireErrorCategory::Lifecycle,
retryable: true,
@@ -646,8 +646,8 @@ fn runtime_unavailable() -> StudioError {
}
}
-fn path_unavailable() -> StudioError {
- StudioError::Failure {
+fn path_unavailable() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: WireErrorCode::StorageUnavailable,
category: WireErrorCategory::Storage,
retryable: true,
@@ -657,8 +657,8 @@ fn path_unavailable() -> StudioError {
}
}
-fn confirmation_expired() -> StudioError {
- StudioError::Failure {
+fn confirmation_expired() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: WireErrorCode::InvalidApplicationState,
category: WireErrorCategory::Lifecycle,
retryable: false,
@@ -668,8 +668,8 @@ fn confirmation_expired() -> StudioError {
}
}
-fn generated_recovery_expired() -> StudioError {
- StudioError::Failure {
+fn generated_recovery_expired() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: WireErrorCode::InvalidApplicationState,
category: WireErrorCategory::Lifecycle,
retryable: false,
@@ -679,9 +679,9 @@ fn generated_recovery_expired() -> StudioError {
}
}
-fn generated_commit_failed(error: SafeError) -> StudioError {
+fn generated_commit_failed(error: SafeError) -> HarvestCircleError {
let (category, _, _) = error_policy(error.code());
- StudioError::Failure {
+ HarvestCircleError::Failure {
code: error.code().into(),
category,
retryable: false,
@@ -693,8 +693,8 @@ fn generated_commit_failed(error: SafeError) -> StudioError {
}
}
-fn compatibility_mismatch() -> StudioError {
- StudioError::Failure {
+fn compatibility_mismatch() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: WireErrorCode::CompatibilityMismatch,
category: WireErrorCategory::Compatibility,
retryable: false,
@@ -722,14 +722,14 @@ mod tests {
use super::{
ACTOR_MAILBOX_CAPACITY, CompatibilityExpectation, DATABASE_APPLICATION, DATABASE_FILENAME,
DATABASE_ORGANIZATION, DATABASE_QUALIFIER, FFI_CONTRACT_HASH, FFI_CONTRACT_MAJOR,
- FFI_CONTRACT_MINOR, RequestContextDto, RuntimeCore, StudioAppCore, StudioError,
- SystemClock, WireErrorCategory, WireErrorCode, WireRecoveryAction, actor_mailbox_capacity,
- compatibility_descriptor, confirmation_expired, generated_commit_failed,
- local_first_relay_configuration, path_unavailable, runtime, runtime_unavailable,
- verify_compatibility,
+ FFI_CONTRACT_MINOR, HarvestCircleAppCore, HarvestCircleError, RequestContextDto,
+ RuntimeCore, SystemClock, WireErrorCategory, WireErrorCode, WireRecoveryAction,
+ actor_mailbox_capacity, compatibility_descriptor, confirmation_expired,
+ generated_commit_failed, local_first_relay_configuration, path_unavailable, runtime,
+ runtime_unavailable, verify_compatibility,
};
- async fn in_memory_core() -> Arc<StudioAppCore> {
+ async fn in_memory_core() -> Arc<HarvestCircleAppCore> {
let actor = RuntimeActorHandle::in_memory(
RelayConfiguration::default(),
RuntimeDependencies::new(
@@ -743,7 +743,7 @@ mod tests {
)
.await
.expect("in-memory actor");
- Arc::new(StudioAppCore {
+ Arc::new(HarvestCircleAppCore {
inner: Arc::new(RuntimeCore {
actor,
observers: std::sync::Mutex::new(std::collections::BTreeMap::new()),
@@ -816,7 +816,7 @@ mod tests {
.expect_err("repeated acknowledgement");
assert!(matches!(
repeated,
- StudioError::Failure { safe_message, .. }
+ HarvestCircleError::Failure { safe_message, .. }
if safe_message == "The generated-key recovery step is no longer valid."
));
}
@@ -991,7 +991,7 @@ mod tests {
assert_eq!(error.to_string(), message);
assert!(matches!(
error,
- StudioError::Failure {
+ HarvestCircleError::Failure {
code: actual_code,
category: actual_category,
retryable: actual_retryable,
@@ -1050,7 +1050,9 @@ mod tests {
contract_major: FFI_CONTRACT_MAJOR + 1,
..compatible
};
- assert!(StudioAppCore::open_path_compatible(&rejected, &incompatible, true).is_err());
+ assert!(
+ HarvestCircleAppCore::open_path_compatible(&rejected, &incompatible, true).is_err()
+ );
assert!(!rejected.parent().expect("parent").exists());
}
diff --git a/core/crates/harvestcircle_ffi/src/lib.rs b/core/crates/harvestcircle_ffi/src/lib.rs
@@ -7,8 +7,8 @@ mod dto;
mod observer;
pub use commands::{
- AccountCommandReceiptDto, GeneratedRecoveryRequest, RemovalRequest, RequestContextDto,
- StudioAppCore, StudioError,
+ AccountCommandReceiptDto, GeneratedRecoveryRequest, HarvestCircleAppCore, HarvestCircleError,
+ RemovalRequest, RequestContextDto,
};
pub use contract::{
FFI_CONTRACT_HASH, FFI_CONTRACT_MAJOR, FFI_CONTRACT_MINOR, MINIMUM_SCHEMA_VERSION,
@@ -20,7 +20,7 @@ pub use dto::{
WireErrorCategory, WireErrorCode, WireRecoveryAction,
};
pub use observer::{
- ObserverSubscription, ShutdownReceiptDto, SnapshotChangeDto, StudioChangeObserver,
+ HarvestCircleChangeObserver, ObserverSubscription, ShutdownReceiptDto, SnapshotChangeDto,
};
uniffi::setup_scaffolding!();
diff --git a/core/crates/harvestcircle_ffi/src/observer.rs b/core/crates/harvestcircle_ffi/src/observer.rs
@@ -6,7 +6,7 @@ use std::sync::{Arc, Mutex, Weak};
use harvestcircle_application::ChangeSubscriptionId;
use crate::commands::RuntimeCore;
-use crate::{AppSnapshotDto, StudioAppCore, StudioError};
+use crate::{AppSnapshotDto, HarvestCircleAppCore, HarvestCircleError};
const OBSERVER_CHANGE_CAPACITY: NonZeroUsize = NonZeroUsize::MIN.saturating_add(63);
const MAX_OBSERVERS: usize = 32;
@@ -26,7 +26,7 @@ pub struct ShutdownReceiptDto {
}
#[cfg_attr(not(coverage_nightly), uniffi::export(callback_interface))]
-pub trait StudioChangeObserver: Send + Sync {
+pub trait HarvestCircleChangeObserver: Send + Sync {
fn on_change(&self, change: SnapshotChangeDto);
}
@@ -62,7 +62,7 @@ impl ObserverSubscription {
}
#[cfg_attr(not(coverage_nightly), uniffi::export)]
-impl StudioAppCore {
+impl HarvestCircleAppCore {
/// Subscribes to ordered revision changes including predecessor metadata.
///
/// # Errors
@@ -70,8 +70,8 @@ impl StudioAppCore {
/// Returns a safe observer or lifecycle error.
pub async fn subscribe_changes_v2(
&self,
- observer: Box<dyn StudioChangeObserver>,
- ) -> Result<Arc<ObserverSubscription>, StudioError> {
+ observer: Box<dyn HarvestCircleChangeObserver>,
+ ) -> Result<Arc<ObserverSubscription>, HarvestCircleError> {
if self.inner.closed.load(Ordering::Acquire) {
return Err(closed_error());
}
@@ -80,9 +80,9 @@ impl StudioAppCore {
.actor
.subscribe_changes(OBSERVER_CHANGE_CAPACITY)
.await
- .map_err(StudioError::from)?;
+ .map_err(HarvestCircleError::from)?;
let id = subscription.id();
- let observer: Arc<dyn StudioChangeObserver> = Arc::from(observer);
+ let observer: Arc<dyn HarvestCircleChangeObserver> = Arc::from(observer);
let runtime_core = Arc::downgrade(&self.inner);
let admitted = {
let mut observers = self
@@ -102,7 +102,7 @@ impl StudioAppCore {
.actor
.unsubscribe_changes(id)
.await
- .map_err(StudioError::from)?;
+ .map_err(HarvestCircleError::from)?;
return Err(observer_registration_error());
}
let task = crate::commands::runtime()?.spawn(async move {
@@ -161,7 +161,7 @@ impl StudioAppCore {
/// # Errors
///
/// Returns a safe closed or timeout error when shutdown cannot complete.
- pub async fn shutdown_v2(&self) -> Result<ShutdownReceiptDto, StudioError> {
+ pub async fn shutdown_v2(&self) -> Result<ShutdownReceiptDto, HarvestCircleError> {
if self.inner.closed.swap(true, Ordering::AcqRel) {
return Err(closed_error());
}
@@ -178,7 +178,11 @@ impl StudioAppCore {
let _ = task.await;
}
}
- self.inner.actor.close().await.map_err(StudioError::from)?;
+ self.inner
+ .actor
+ .close()
+ .await
+ .map_err(HarvestCircleError::from)?;
Ok(ShutdownReceiptDto {
final_revision: self.inner.actor.snapshot().revision().value(),
closed: true,
@@ -186,8 +190,8 @@ impl StudioAppCore {
}
}
-fn closed_error() -> StudioError {
- StudioError::Failure {
+fn closed_error() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: crate::WireErrorCode::InvalidApplicationState,
category: crate::WireErrorCategory::Lifecycle,
retryable: false,
@@ -197,8 +201,8 @@ fn closed_error() -> StudioError {
}
}
-fn observer_registration_error() -> StudioError {
- StudioError::Failure {
+fn observer_registration_error() -> HarvestCircleError {
+ HarvestCircleError::Failure {
code: crate::WireErrorCode::ObserverRegistrationFailed,
category: crate::WireErrorCategory::Lifecycle,
retryable: true,
@@ -227,7 +231,8 @@ mod tests {
use crate::commands::{ACTOR_MAILBOX_CAPACITY, RuntimeCore, SystemClock, runtime};
use crate::{
- AppSnapshotDto, ProfileLoadStateDto, SnapshotChangeDto, StudioAppCore, StudioChangeObserver,
+ AppSnapshotDto, HarvestCircleAppCore, HarvestCircleChangeObserver, ProfileLoadStateDto,
+ SnapshotChangeDto,
};
const SECRET_HEX: &str = "7e7e9c42a91bfef19fa7ea99d52d8afdb67d893a8fefba1f5cb9793f2107f6d7";
@@ -235,18 +240,18 @@ mod tests {
#[derive(Default)]
struct RecordingObserver {
snapshots: Mutex<Vec<AppSnapshotDto>>,
- core: Mutex<Option<Arc<StudioAppCore>>>,
+ core: Mutex<Option<Arc<HarvestCircleAppCore>>>,
}
struct PanickingObserver;
- impl StudioChangeObserver for PanickingObserver {
+ impl HarvestCircleChangeObserver for PanickingObserver {
fn on_change(&self, _change: SnapshotChangeDto) {
panic!("injected host callback failure");
}
}
- impl StudioChangeObserver for RecordingObserver {
+ impl HarvestCircleChangeObserver for RecordingObserver {
fn on_change(&self, change: SnapshotChangeDto) {
let snapshot = change.snapshot;
if let Some(core) = self.core.lock().expect("core").as_ref() {
@@ -256,11 +261,11 @@ mod tests {
}
}
- async fn core() -> Arc<StudioAppCore> {
+ async fn core() -> Arc<HarvestCircleAppCore> {
core_with_relays(RelayConfiguration::default()).await
}
- async fn core_with_relays(relays: RelayConfiguration) -> Arc<StudioAppCore> {
+ async fn core_with_relays(relays: RelayConfiguration) -> Arc<HarvestCircleAppCore> {
let actor = RuntimeActorHandle::in_memory(
relays,
RuntimeDependencies::new(
@@ -274,7 +279,7 @@ mod tests {
)
.await
.expect("actor");
- Arc::new(StudioAppCore {
+ Arc::new(HarvestCircleAppCore {
inner: Arc::new(RuntimeCore {
actor,
observers: Mutex::new(std::collections::BTreeMap::new()),
@@ -471,7 +476,7 @@ mod tests {
struct ArcObserver(Arc<RecordingObserver>);
- impl StudioChangeObserver for ArcObserver {
+ impl HarvestCircleChangeObserver for ArcObserver {
fn on_change(&self, change: SnapshotChangeDto) {
self.0.on_change(change);
}
diff --git a/core/crates/harvestcircle_nostr/src/client.rs b/core/crates/harvestcircle_nostr/src/client.rs
@@ -75,7 +75,7 @@ impl NostrClient for SdkNostrClient {
.collect::<Result<Vec<_>, _>>()
.map_err(|_| invalid_relay_configuration())?;
let request = FetchRequest::new(
- format!("studio-profile-{policy:?}"),
+ format!("harvestcircle-profile-{policy:?}"),
TargetSet::new(targets).map_err(|_| invalid_relay_configuration())?,
FetchBounds::new(
MAX_PROFILE_EVENTS_PER_RELAY as u16,
diff --git a/core/crates/harvestcircle_storage/src/db.rs b/core/crates/harvestcircle_storage/src/db.rs
@@ -775,7 +775,7 @@ mod tests {
.arg("--exact")
.arg("db::tests::writable_ownership_child_probe")
.arg("--nocapture")
- .env("RADROOTS_STUDIO_LOCK_PROBE_PATH", &path)
+ .env("HARVESTCIRCLE_LOCK_PROBE_PATH", &path)
.status()
.expect("child process");
assert!(status.success());
@@ -783,7 +783,7 @@ mod tests {
#[test]
fn writable_ownership_child_probe() {
- let Ok(path) = std::env::var("RADROOTS_STUDIO_LOCK_PROBE_PATH") else {
+ let Ok(path) = std::env::var("HARVESTCIRCLE_LOCK_PROBE_PATH") else {
return;
};
assert!(Database::open(Path::new(&path)).is_err());