Skip to content

Commit 7f27875

Browse files
Rename repository -> database
1 parent 14a39cd commit 7f27875

File tree

15 files changed

+97
-111
lines changed

15 files changed

+97
-111
lines changed

src/cli.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{
22
collection::{ProfileId, RequestCollection, RequestRecipeId},
3-
http::{HttpEngine, Repository, RequestBuilder},
3+
db::Database,
4+
http::{HttpEngine, RequestBuilder},
45
template::{Prompt, Prompter, TemplateContext},
56
util::{Directory, ResultExt},
67
};
@@ -91,15 +92,15 @@ impl Subcommand {
9192
)?;
9293

9394
// Build the request
94-
let repository = Repository::load(&collection.id)?;
95+
let database = Database::load(&collection.id)?;
9596
let overrides: IndexMap<_, _> = overrides.into_iter().collect();
9697
let request = RequestBuilder::new(
9798
recipe,
9899
TemplateContext {
99100
profile: profile_data,
100101
overrides,
101102
chains: collection.chains,
102-
repository: repository.clone(),
103+
database: database.clone(),
103104
prompter: Box::new(CliPrompter),
104105
},
105106
)
@@ -110,7 +111,7 @@ impl Subcommand {
110111
println!("{:#?}", request);
111112
} else {
112113
// Run the request
113-
let http_engine = HttpEngine::new(repository);
114+
let http_engine = HttpEngine::new(database);
114115
let record = http_engine.send(request).await?;
115116

116117
// Print response

src/collection.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ pub struct RequestCollection<S = PathBuf> {
5555
pub recipes: IndexMap<RequestRecipeId, RequestRecipe>,
5656
}
5757

58-
/// A unique ID for a collection. This is necessary to differentiate between
59-
/// responses from different collections in the repository.
58+
/// A unique ID for a collection. This is necessary to give each collection its
59+
/// own database.
6060
#[derive(
6161
Clone, Debug, Default, Deref, Display, From, Serialize, Deserialize,
6262
)]

src/http/repository.rs renamed to src/db.rs

Lines changed: 41 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
//! The repository persists all requests and responses. It consists of a SQLite
2-
//! database to persist all request history and an in-memory layer to provide
3-
//! caching and other ephemeral data (e.g. prettified content).
1+
//! The database is responsible for persisting data, including requests and
2+
//! responses.
43
54
use crate::{
65
collection::{CollectionId, RequestRecipeId},
@@ -18,78 +17,74 @@ use tokio::sync::Mutex;
1817
use tracing::debug;
1918
use uuid::Uuid;
2019

21-
/// A record of all HTTP history, which is persisted on disk. This is also used
22-
/// to populate chained values. This uses a sqlite DB underneath, which means
23-
/// all operations are internally fallible. Generally speaking, any error that
20+
/// A SQLite database for persisting data. Generally speaking, any error that
2421
/// occurs *after* opening the DB connection should be an internal bug, but
25-
/// should be shown to the user whenever possible. All operations are also
26-
/// async because of the database and the locking nature of the interior cache.
27-
/// They generally will be fast though, so it's safe to block on them in the
28-
/// main loop. Do not call this from the draw phase though; instead, cache the
29-
/// results in UI state for as long as they're needed.
22+
/// should be shown to the user whenever possible. All operations are async
23+
/// to enable concurrent accesses to yield. Do not call block on thisfrom the
24+
/// draw phase; instead, cache the results in UI state for as long as they're
25+
/// needed.
3026
///
31-
/// Only requests that received a valid HTTP response should be stored.
32-
/// In-flight requests, invalid requests, and requests that failed to complete
33-
/// (e.g. because of a network error) should not (and cannot) be stored.
27+
/// This uses an `Arc` internally, so it's safe and cheap to clone.
3428
///
3529
/// Note: Despite all the operations being async, the actual database isn't
3630
/// async. Each operation will asynchronously wait for the connection mutex,
3731
/// then block while performing the operation. This is just a shortcut, if it
3832
/// becomes a bottleneck we can change that.
3933
#[derive(Clone, Debug)]
40-
pub struct Repository {
41-
/// History is stored in a sqlite DB. Mutex is needed for multi-threaded
34+
pub struct Database {
35+
/// Data is stored in a sqlite DB. Mutex is needed for multi-threaded
4236
/// access. This is a bottleneck but the access rate should be so low that
4337
/// it doesn't matter.
4438
connection: Arc<Mutex<Connection>>,
4539
}
4640

47-
impl Repository {
48-
/// Load the repository database. This will perform first-time setup, so
49-
/// this should only be called at the main session entrypoint.
41+
impl Database {
42+
/// Load the database. This will perform first-time setup, so this should
43+
/// only be called at the main session entrypoint.
5044
pub fn load(collection_id: &CollectionId) -> anyhow::Result<Self> {
5145
let mut connection = Connection::open(Self::path(collection_id)?)?;
5246
// Use WAL for concurrency
5347
connection.pragma_update(None, "journal_mode", "WAL")?;
54-
Self::setup(&mut connection)?;
48+
Self::migrate(&mut connection)?;
5549
Ok(Self {
5650
connection: Arc::new(Mutex::new(connection)),
5751
})
5852
}
5953

60-
/// Path to the repository database file. This will create the directory if
61-
/// it doesn't exist
54+
/// Path to the database file. This will create the directory if it doesn't
55+
/// exist
6256
fn path(collection_id: &CollectionId) -> anyhow::Result<PathBuf> {
6357
Ok(Directory::data(collection_id)
6458
.create()?
6559
.join("state.sqlite"))
6660
}
6761

68-
/// Apply first-time setup
69-
fn setup(connection: &mut Connection) -> anyhow::Result<()> {
62+
/// Apply database migrations
63+
fn migrate(connection: &mut Connection) -> anyhow::Result<()> {
7064
let migrations = Migrations::new(vec![M::up(
7165
// The request state kind is a bit hard to map to tabular data.
72-
// Everything that we need to query on (success/error kind, HTTP
73-
// status code, end_time, etc.) is in its own column. The
74-
// request/repsonse and response will be serialized
75-
// into messagepack bytes
66+
// Everything that we need to query on (HTTP status code,
67+
// end_time, etc.) is in its own column. The
68+
// request/repsonse and response will be serialized into
69+
// msgpack bytes
7670
"CREATE TABLE requests (
77-
id UUID PRIMARY KEY,
78-
recipe_id TEXT,
79-
start_time TEXT,
80-
end_time TEXT,
81-
request BLOB,
82-
response BLOB,
83-
status_code INTEGER
84-
)",
85-
)]);
71+
id UUID PRIMARY KEY,
72+
recipe_id TEXT,
73+
start_time TEXT,
74+
end_time TEXT,
75+
request BLOB,
76+
response BLOB,
77+
status_code INTEGER
78+
)",
79+
)
80+
.down("DROP TABLE requests")]);
8681
migrations.to_latest(connection)?;
8782
Ok(())
8883
}
8984

9085
/// Get the most recent request+response for a recipe, or `None` if there
9186
/// has never been one received.
92-
pub async fn get_last(
87+
pub async fn get_last_request(
9388
&self,
9489
recipe_id: &RequestRecipeId,
9590
) -> anyhow::Result<Option<RequestRecord>> {
@@ -107,13 +102,12 @@ impl Repository {
107102
.traced()
108103
}
109104

110-
/// Add a new request to history. This should be called immediately before
111-
/// or after the request is sent, so the generated start_time timestamp
112-
/// is accurate.
113-
///
114-
/// The HTTP engine is responsible for inserting its requests, so this isn't
115-
/// exposed outside the `http` module.
116-
pub(super) async fn insert(
105+
/// Add a new request to history. The HTTP engine is responsible for
106+
/// inserting its own requests. Only requests that received a valid HTTP
107+
/// response should be stored. In-flight requests, invalid requests, and
108+
/// requests that failed to complete (e.g. because of a network error)
109+
/// should not (and cannot) be stored.
110+
pub async fn insert_request(
117111
&self,
118112
record: &RequestRecord,
119113
) -> anyhow::Result<()> {
@@ -155,23 +149,15 @@ impl Repository {
155149

156150
/// Test-only helpers
157151
#[cfg(test)]
158-
impl Repository {
152+
impl Database {
159153
/// Create an in-memory DB, only for testing
160154
pub fn testing() -> Self {
161155
let mut connection = Connection::open_in_memory().unwrap();
162-
Self::setup(&mut connection).unwrap();
156+
Self::migrate(&mut connection).unwrap();
163157
Self {
164158
connection: Arc::new(Mutex::new(connection)),
165159
}
166160
}
167-
168-
/// Public insert function, only for tests
169-
pub async fn insert_test(
170-
&self,
171-
record: &RequestRecord,
172-
) -> anyhow::Result<()> {
173-
self.insert(record).await
174-
}
175161
}
176162

177163
impl ToSql for RequestId {

src/factory.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{
22
collection::{Chain, ChainSource, RequestRecipeId},
3-
http::{Body, Repository, Request, RequestId, RequestRecord, Response},
3+
db::Database,
4+
http::{Body, Request, RequestId, RequestRecord, Response},
45
template::{Prompt, Prompter, Template, TemplateContext},
56
};
67
use chrono::Utc;
@@ -61,7 +62,7 @@ factori!(TemplateContext, {
6162
profile = Default::default()
6263
chains = Default::default()
6364
prompter = Box::<TestPrompter>::default(),
64-
repository = Repository::testing()
65+
database = Database::testing()
6566
overrides = Default::default()
6667
}
6768
});

src/http.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,13 @@
3535
3636
mod parse;
3737
mod record;
38-
mod repository;
3938

4039
pub use parse::*;
4140
pub use record::*;
42-
pub use repository::*;
4341

4442
use crate::{
4543
collection::RequestRecipe,
44+
db::Database,
4645
template::{Template, TemplateContext},
4746
util::ResultExt,
4847
};
@@ -70,24 +69,24 @@ const USER_AGENT: &str =
7069
#[derive(Clone, Debug)]
7170
pub struct HttpEngine {
7271
client: Client,
73-
repository: Repository,
72+
database: Database,
7473
}
7574

7675
impl HttpEngine {
7776
/// Build a new HTTP engine, which can be used for the entire program life
78-
pub fn new(repository: Repository) -> Self {
77+
pub fn new(database: Database) -> Self {
7978
Self {
8079
client: Client::builder()
8180
.user_agent(USER_AGENT)
8281
.build()
8382
// This should be infallible
8483
.expect("Error building reqwest client"),
85-
repository,
84+
database,
8685
}
8786
}
8887

8988
/// Launch an HTTP request. Upon completion, it will automatically be
90-
/// registered in the repository for posterity.
89+
/// registered in the database for posterity.
9190
///
9291
/// This consumes the HTTP engine so that the future can outlive the scope
9392
/// that created the future. This allows the future to be created outside
@@ -131,7 +130,7 @@ impl HttpEngine {
131130
};
132131

133132
// Error here should *not* kill the request
134-
let _ = self.repository.insert(&record).await;
133+
let _ = self.database.insert_request(&record).await;
135134
Ok(record)
136135
}
137136
Err(error) => Err(RequestError {

src/http/record.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,7 @@ pub struct RequestError {
5858
Serialize,
5959
Deserialize,
6060
)]
61-
pub struct RequestId(
62-
/// Private to enforce construction via deserialization or [Self::new]
63-
pub(super) Uuid,
64-
);
61+
pub struct RequestId(pub Uuid);
6562

6663
impl RequestId {
6764
pub fn new() -> Self {

src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
mod cli;
55
mod collection;
6+
mod db;
67
#[cfg(test)]
78
mod factory;
89
mod http;

src/template.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub use prompt::{Prompt, Prompter};
99

1010
use crate::{
1111
collection::{Chain, ChainId, ProfileValue},
12-
http::Repository,
12+
db::Database,
1313
template::{
1414
error::TemplateParseError,
1515
parse::{TemplateInputChunk, CHAIN_PREFIX, ENV_PREFIX},
@@ -30,7 +30,7 @@ pub struct TemplateContext {
3030
/// Chained values from dynamic sources
3131
pub chains: IndexMap<ChainId, Chain>,
3232
/// Needed for accessing response bodies for chaining
33-
pub repository: Repository,
33+
pub database: Database,
3434
/// Additional key=value overrides passed directly from the user
3535
pub overrides: IndexMap<String, String>,
3636
/// A conduit to ask the user questions
@@ -266,7 +266,7 @@ mod tests {
266266
#[case] expected_value: &str,
267267
) {
268268
let recipe_id: RequestRecipeId = "recipe1".into();
269-
let repository = Repository::testing();
269+
let database = Database::testing();
270270
let response_body = json!({
271271
"string": "Hello World!",
272272
"number": 6,
@@ -277,8 +277,8 @@ mod tests {
277277
let request = create!(Request, recipe_id: recipe_id.clone());
278278
let response =
279279
create!(Response, body: response_body.to_string().into());
280-
repository
281-
.insert_test(
280+
database
281+
.insert_request(
282282
&create!(RequestRecord, request: request, response: response),
283283
)
284284
.await
@@ -290,7 +290,7 @@ mod tests {
290290
selector: selector,
291291
)};
292292
let context = create!(
293-
TemplateContext, repository: repository, chains: chains,
293+
TemplateContext, database: database, chains: chains,
294294
);
295295

296296
assert_eq!(
@@ -339,21 +339,21 @@ mod tests {
339339
async fn test_chain_request_error(
340340
#[case] chain_id: impl Into<ChainId>,
341341
#[case] chain: Chain,
342-
// Optional request data to store in the repository
342+
// Optional request data to store in the database
343343
#[case] request_response: Option<(Request, Response)>,
344344
#[case] expected_error: &str,
345345
) {
346-
let repository = Repository::testing();
346+
let database = Database::testing();
347347
if let Some((request, response)) = request_response {
348-
repository
349-
.insert_test(&create!(
348+
database
349+
.insert_request(&create!(
350350
RequestRecord, request: request, response: response))
351351
.await
352352
.unwrap();
353353
}
354354
let chains = indexmap! {chain_id.into() => chain};
355355
let context = create!(
356-
TemplateContext, repository: repository, chains: chains
356+
TemplateContext, database: database, chains: chains
357357
);
358358

359359
assert_err!(render!("{{chains.chain1}}", context), expected_error);

src/template/error.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,10 @@ pub enum ChainError {
6363
/// Reference to a chain that doesn't exist
6464
#[error("Unknown chain")]
6565
Unknown,
66-
/// An error occurred accessing the request repository. This error is
66+
/// An error occurred accessing the persistence database. This error is
6767
/// generated by our code so we don't need any extra context.
6868
#[error(transparent)]
69-
Repository(anyhow::Error),
69+
Database(anyhow::Error),
7070
/// The chain ID is valid, but the corresponding recipe has no successful
7171
/// response
7272
#[error("No response available")]

0 commit comments

Comments
 (0)