chore: rename default to ephemeral

This commit is contained in:
kaichaosun 2026-06-25 18:28:26 +08:00
parent c6b56ef2e7
commit 9b4c8c49a0
No known key found for this signature in database
GPG Key ID: 223E0F992F4F03BF
6 changed files with 37 additions and 124 deletions

View File

@ -9,8 +9,7 @@ use anyhow::{Context, Result};
use clap::{Parser, ValueEnum}; use clap::{Parser, ValueEnum};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use logos_chat::{ use logos_chat::{
ChatClient, ChatClientBuilder, ChatStore, Event, HttpRegistry, IdentityProvider, ChatClient, ChatStore, Event, IdentityProvider, LogosChatClient, RegistrationService, Transport,
RegistrationService, StorageConfig, Transport,
}; };
use app::ChatApp; use app::ChatApp;
@ -59,9 +58,9 @@ struct Cli {
#[arg(long)] #[arg(long)]
smoketest: bool, smoketest: bool,
/// Optional KeyPackage registry base URL. When set, uses the HTTP-backed /// Override the Logos registry endpoint (account + keypackage store). When
/// registry instead of the in-memory `EphemeralRegistry`. /// omitted, the preconfigured endpoint is used.
/// Example: `--registry-url http://localhost:8080`. /// Example: `--registry-url http://127.0.0.1:18080`.
#[arg(long)] #[arg(long)]
registry_url: Option<String>, registry_url: Option<String>,
} }
@ -108,33 +107,12 @@ fn run<T: Transport>(transport: T, cli: &Cli) -> Result<()> {
.to_str() .to_str()
.context("db path contains non-UTF-8 characters")? .context("db path contains non-UTF-8 characters")?
.to_string(); .to_string();
let storage = StorageConfig::Encrypted {
path: db_str,
key: "chat-cli".to_string(),
};
match cli.registry_url.as_deref() { let (client, events) =
Some(url) => { LogosChatClient::open(transport, db_str, "chat-cli", cli.registry_url.as_deref())
let registry = HttpRegistry::new(url); .map_err(|e| anyhow::anyhow!("{e:?}"))
let (client, events) = ChatClientBuilder::default() .context("failed to open chat client")?;
.transport(transport) launch_tui(client, events, cli)
.storage_config(storage)
.registration(registry)
.build()
.map_err(|e| anyhow::anyhow!("{e:?}"))
.context("failed to open chat client with HTTP registry")?;
launch_tui(client, events, cli)
}
None => {
let (client, events) = ChatClientBuilder::default()
.transport(transport)
.storage_config(storage)
.build()
.map_err(|e| anyhow::anyhow!("{e:?}"))
.context("failed to open chat client")?;
launch_tui(client, events, cli)
}
}
} }
fn launch_tui<I, T, R, S>( fn launch_tui<I, T, R, S>(
@ -160,74 +138,6 @@ where
result result
} }
#[cfg_attr(not(logos_delivery), allow(dead_code, unused_variables))]
fn run_logos_delivery(cli: Cli) -> Result<()> {
#[cfg(logos_delivery)]
{
use transport::logos_delivery::{Config, Service};
eprintln!("Starting logos-delivery node (preset={})...", cli.preset);
eprintln!("This may take a few seconds while connecting to the network.");
let logos_cfg = Config {
preset: cli.preset.clone(),
tcp_port: cli.port,
..Default::default()
};
let delivery = Service::start(logos_cfg).context("failed to start logos-delivery")?;
eprintln!("Node connected. Initializing chat client...");
let data_dir = cli
.db
.as_ref()
.and_then(|p| p.parent())
.map(|p| p.to_path_buf())
.unwrap_or_else(|| cli.data.clone());
let (client, events) = match cli.db {
Some(ref path) => {
let db_str = path
.to_str()
.context("db path contains non-UTF-8 characters")?
.to_string();
logos_chat::ChatClientBuilder::default()
.storage_config(logos_chat::StorageConfig::Encrypted {
path: db_str,
key: "chat-cli".to_string(),
})
.transport(delivery)
.build()
.map_err(|e| anyhow::anyhow!("{e:?}"))
.context("failed to open persistent client")?
}
None => logos_chat::ChatClientBuilder::default()
.transport(delivery)
.build()
.map_err(|e| anyhow::anyhow!("{e:?}"))
.context("failed to open chat client")?,
};
let mut app = ChatApp::new(client, events, &cli.name, &data_dir)?;
if cli.smoketest {
return Ok(());
}
let mut terminal = ui::init().context("failed to initialize terminal")?;
let result = run_app(&mut terminal, &mut app);
ui::restore().context("failed to restore terminal")?;
return result;
}
#[cfg(not(logos_delivery))]
anyhow::bail!(
"logos-delivery transport is not available in this build.\n\
Build with LOGOS_DELIVERY_LIB_DIR set to enable it."
)
}
fn run_app<I, T, R, S>(terminal: &mut ui::Tui, app: &mut ChatApp<I, T, R, S>) -> Result<()> fn run_app<I, T, R, S>(terminal: &mut ui::Tui, app: &mut ChatApp<I, T, R, S>) -> Result<()>
where where
I: IdentityProvider + Send, I: IdentityProvider + Send,

View File

@ -6,13 +6,13 @@ fn main() {
let bus = MessageBus::default(); let bus = MessageBus::default();
let reg = EphemeralRegistry::new(); let reg = EphemeralRegistry::new();
let (mut saro, saro_events) = ChatClientBuilder::default() let (mut saro, saro_events) = ChatClientBuilder::ephemeral()
.transport(InProcessDelivery::new(bus.clone())) .transport(InProcessDelivery::new(bus.clone()))
.registration(reg.clone()) .registration(reg.clone())
.build() .build()
.unwrap(); .unwrap();
let (mut raya, raya_events) = ChatClientBuilder::default() let (mut raya, raya_events) = ChatClientBuilder::ephemeral()
.transport(InProcessDelivery::new(bus)) .transport(InProcessDelivery::new(bus))
.registration(reg) .registration(reg)
.build() .build()

View File

@ -11,8 +11,8 @@ use crate::event::Event;
/// Marker for a builder field that has not been configured. A field left `Unset` /// Marker for a builder field that has not been configured. A field left `Unset`
/// at `build()` is a compile error: `build()` requires every component to have a /// at `build()` is a compile error: `build()` requires every component to have a
/// concrete type. Use [`ChatClientBuilder::default`] to start from filled-in /// concrete type. Use [`ChatClientBuilder::ephemeral`] to start from filled-in
/// defaults instead. /// throwaway components instead.
pub struct Unset; pub struct Unset;
pub struct ChatClientBuilder<I = Unset, T = Unset, R = Unset, S = Unset> { pub struct ChatClientBuilder<I = Unset, T = Unset, R = Unset, S = Unset> {
@ -22,15 +22,15 @@ pub struct ChatClientBuilder<I = Unset, T = Unset, R = Unset, S = Unset> {
storage: S, storage: S,
} }
impl Default for ChatClientBuilder {
fn default() -> Self {
Self::new()
}
}
impl ChatClientBuilder { impl ChatClientBuilder {
/// An empty builder: every component is `Unset` and must be supplied before /// An empty builder: every component is `Unset` and must be supplied before
/// [`build`](ChatClientBuilder::build). For the common case, prefer /// [`build`](ChatClientBuilder::build).
/// [`default`](ChatClientBuilder::default), which pre-fills the components.
//
// `Default` is intentionally not implemented: `default()` below is a distinct
// constructor that returns a *different*, pre-filled builder type, which the
// `Default` trait (`fn() -> Self`) cannot express.
#[allow(clippy::new_without_default)]
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
ident: Unset, ident: Unset,
@ -40,12 +40,12 @@ impl ChatClientBuilder {
} }
} }
/// A builder pre-filled with the default identity, registration, and storage. /// A builder pre-filled with throwaway components — a random identity, an
/// Only the transport is left to set; override any default with the matching /// ephemeral (in-memory) registry, and in-memory storage. Intended for tests,
/// setter. A complete entry point on its own — there is no need to call /// examples, and local runs that need no persistence or network services;
/// [`new`](ChatClientBuilder::new) first. /// override any slot with the matching setter. A complete entry point on its
#[allow(clippy::should_implement_trait)] /// own — only the transport is left to set before [`build`](ChatClientBuilder::build).
pub fn default() -> ChatClientBuilder<DelegateSigner, Unset, EphemeralRegistry, ChatStorage> { pub fn ephemeral() -> ChatClientBuilder<DelegateSigner, Unset, EphemeralRegistry, ChatStorage> {
Self::new() Self::new()
.ident(DelegateSigner::random()) .ident(DelegateSigner::random())
.registration(EphemeralRegistry::new()) .registration(EphemeralRegistry::new())
@ -109,7 +109,7 @@ type Built<I, T, R, S> = Result<(ChatClient<I, T, R, S>, Receiver<Event>), Clien
/// `build()` exists only once every component has a concrete type. Any field /// `build()` exists only once every component has a concrete type. Any field
/// still `Unset` (always at least the transport, which has no default) fails the /// still `Unset` (always at least the transport, which has no default) fails the
/// bounds below, so an incomplete builder is a compile error rather than a /// bounds below, so an incomplete builder is a compile error rather than a
/// runtime one. Start from [`default`](ChatClientBuilder::default) to fill the /// runtime one. Start from [`ephemeral`](ChatClientBuilder::ephemeral) to fill the
/// identity/registration/storage slots, then set the transport. /// identity/registration/storage slots, then set the transport.
impl<I, T, R, S> ChatClientBuilder<I, T, R, S> impl<I, T, R, S> ChatClientBuilder<I, T, R, S>
where where

View File

@ -9,10 +9,10 @@ mod logos;
pub use builder::{ChatClientBuilder, Unset}; pub use builder::{ChatClientBuilder, Unset};
pub use client::{ChatClient, Transport}; pub use client::{ChatClient, Transport};
pub use delegate::DelegateSigner; pub use delegate::DelegateSigner;
pub use logos::LogosChatClient;
pub use delivery_in_process::{InProcessDelivery, MessageBus}; pub use delivery_in_process::{InProcessDelivery, MessageBus};
pub use errors::ClientError; pub use errors::ClientError;
pub use event::{Event, MessageSender}; pub use event::{Event, MessageSender};
pub use logos::LogosChatClient;
// Re-export types callers need to interact with ChatClient. // Re-export types callers need to interact with ChatClient.
pub use libchat::{ pub use libchat::{

View File

@ -35,8 +35,9 @@ where
T: Transport + Send + 'static, T: Transport + Send + 'static,
{ {
/// Open a client on the Logos stack over `transport`, persisting to the /// Open a client on the Logos stack over `transport`, persisting to the
/// encrypted database at `db_path` unlocked with `db_key`. The identity and /// encrypted database at `db_path` unlocked with `db_key`. When `registry_url`
/// registry are preconfigured; the registry endpoint is a placeholder for now. /// is `Some`, it overrides the preconfigured registry endpoint (e.g. a local
/// deployment); otherwise the baked-in endpoint is used.
/// ///
/// `db_path` is a per-client location and `db_key` is a secret, so both are /// `db_path` is a per-client location and `db_key` is a secret, so both are
/// caller-supplied — never baked into the library. /// caller-supplied — never baked into the library.
@ -44,11 +45,13 @@ where
transport: T, transport: T,
db_path: impl Into<String>, db_path: impl Into<String>,
db_key: impl Into<String>, db_key: impl Into<String>,
registry_url: Option<&str>,
) -> Result<(Self, Receiver<Event>), ClientError> { ) -> Result<(Self, Receiver<Event>), ClientError> {
let endpoint = registry_url.unwrap_or(REGISTRY_ENDPOINT);
ChatClientBuilder::new() ChatClientBuilder::new()
.ident(DelegateSigner::random()) .ident(DelegateSigner::random())
.transport(transport) .transport(transport)
.registration(HttpRegistry::new(REGISTRY_ENDPOINT)) .registration(HttpRegistry::new(endpoint))
.storage_config(StorageConfig::Encrypted { .storage_config(StorageConfig::Encrypted {
path: db_path.into(), path: db_path.into(),
key: db_key.into(), key: db_key.into(),

View File

@ -39,7 +39,7 @@ fn create_test_client(
logos_chat::ClientError, logos_chat::ClientError,
> { > {
let d = InProcessDelivery::new(message_bus); let d = InProcessDelivery::new(message_bus);
ChatClientBuilder::default() ChatClientBuilder::ephemeral()
.transport(d) .transport(d)
.registration(reg) .registration(reg)
.build() .build()
@ -108,7 +108,7 @@ fn direct_v1_standalone_integration() {
// Build saro's client with its associated delegate so its outbound messages // Build saro's client with its associated delegate so its outbound messages
// carry a credential the receiver can verify against the published bundle. // carry a credential the receiver can verify against the published bundle.
let (mut saro, _saro_events) = ChatClientBuilder::default() let (mut saro, _saro_events) = ChatClientBuilder::ephemeral()
.ident(saro_delegate) .ident(saro_delegate)
.transport(InProcessDelivery::new(bus.clone())) .transport(InProcessDelivery::new(bus.clone()))
.registration(reg_service.clone()) .registration(reg_service.clone())
@ -293,7 +293,7 @@ fn malformed_inbound_surfaces_as_error_event() {
let delivery = FailingDelivery::new(); let delivery = FailingDelivery::new();
let inbound_tx = delivery.inbound_sender(); let inbound_tx = delivery.inbound_sender();
let (_client, events) = ChatClientBuilder::default() let (_client, events) = ChatClientBuilder::ephemeral()
.transport(delivery) .transport(delivery)
.build() .build()
.expect("client create"); .expect("client create");