mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-07 02:21:23 +00:00
cleanup and code cleanliness instructions
This commit is contained in:
1 parent
094a3e9a12
commit
71af9422d6
3 files changed
+78
-37
No files matched your search
@@ -11,6 +11,30 @@
|
||||
- Fields that are optional use the `maybe_` prefix: `maybe_creator_user_token`, `maybe_prompt_token`
|
||||
- When implementing `fmt::Display` for error types, use the pattern: `write!(f, "{:?}", self)`
|
||||
|
||||
## File Layout (Top-to-Bottom Reading Order)
|
||||
|
||||
Organize code so a human reading top-to-bottom encounters the most important things first and
|
||||
can drill down into details only when curious. Think of it like a newspaper: headline first,
|
||||
then the story, then the fine print.
|
||||
|
||||
### Ordering within a file
|
||||
|
||||
1. **Constants** at the top, right after imports.
|
||||
2. **Structs, enums, and type definitions** next. If a struct contains another struct, place
|
||||
the outer/larger struct above the inner/smaller one.
|
||||
3. **Type ordering for API types**: Request, then Response (success), then Error.
|
||||
4. **`impl` blocks** after their type definitions.
|
||||
|
||||
### Ordering within an `impl` block
|
||||
|
||||
1. **Constructors** (`new`, `from_*`, `builder`) at the top.
|
||||
2. **Public methods** next — these are the main entry points a reader cares about.
|
||||
3. **Private helper methods** below public methods.
|
||||
- Among helpers: place "meaty" helpers (substantial logic, direct continuations of the
|
||||
public entry point) above "leaf" helpers (small formatters, simple lookups).
|
||||
- A method that calls another method in the same file should appear *above* the method
|
||||
it calls. This lets readers follow the call chain downward.
|
||||
|
||||
## Naming
|
||||
|
||||
- Handler functions: `{verb}_{noun}_handler` (e.g. `get_health_check_handler`, `login_handler`)
|
||||
|
||||
@@ -62,3 +62,14 @@ artcraft/
|
||||
- A mix of wreq and reqwest for Rust HTTP clients
|
||||
- TypeScript with Nx, React, Vite, Zustand, and Three.js
|
||||
- Use two spaces for indentation
|
||||
|
||||
### File Layout
|
||||
|
||||
Organize for top-to-bottom reading. Important things first, details later.
|
||||
|
||||
- **Constants** at the top (after imports)
|
||||
- **Structs/enums** next; outer structs above inner sub-structs
|
||||
- **API types** in order: Request, Response, Error
|
||||
- **In impl blocks**: constructors first, then public methods, then private helpers
|
||||
- Private helpers go *below* the methods that call them
|
||||
- Among helpers: meatier logic above leaf-level formatters
|
||||
@@ -66,7 +66,11 @@ pub struct PageSentResult {
|
||||
pub short_id: Option<String>,
|
||||
}
|
||||
|
||||
// =============== Implementation ===============
|
||||
|
||||
impl PagerClient {
|
||||
// --- Constructor ---
|
||||
|
||||
pub fn new(
|
||||
client_config: PagerClientConfig,
|
||||
application_name: Option<String>,
|
||||
@@ -77,45 +81,12 @@ impl PagerClient {
|
||||
Self { client_config, application_name, environment, hostname, service_id }
|
||||
}
|
||||
|
||||
// --- Public interface ---
|
||||
|
||||
pub fn is_noop(&self) -> bool {
|
||||
matches!(self.client_config, PagerClientConfig::NoOp)
|
||||
}
|
||||
|
||||
/// Build structured labels from the client config and notification fields.
|
||||
fn build_labels(&self, notification: &NotificationDetails) -> Option<Vec<(String, String)>> {
|
||||
let mut labels: Vec<(String, String)> = Vec::new();
|
||||
|
||||
if let Some(name) = &self.application_name {
|
||||
labels.push(("application_name".to_string(), name.clone()));
|
||||
}
|
||||
|
||||
if let Some(id) = &self.service_id {
|
||||
labels.push(("service_id".to_string(), id.clone()));
|
||||
}
|
||||
|
||||
if let Some(env) = &self.environment {
|
||||
labels.push(("environment".to_string(), env.clone()));
|
||||
}
|
||||
|
||||
if let Some(h) = &self.hostname {
|
||||
labels.push(("hostname".to_string(), h.clone()));
|
||||
}
|
||||
|
||||
if let Some(method) = ¬ification.http_method {
|
||||
labels.push(("http_method".to_string(), method.clone()));
|
||||
}
|
||||
|
||||
if let Some(path) = ¬ification.http_path {
|
||||
labels.push(("http_path".to_string(), path.clone()));
|
||||
}
|
||||
|
||||
if let Some(status_code) = notification.http_status_code {
|
||||
labels.push(("http_status_code".to_string(), status_code.to_string()));
|
||||
}
|
||||
|
||||
if labels.is_empty() { None } else { Some(labels) }
|
||||
}
|
||||
|
||||
/// Send a page immediately. Returns `Ok(None)` for NoOp.
|
||||
pub async fn send_page(&self, notification: &NotificationDetails) -> Result<Option<PageSentResult>, PagerError> {
|
||||
match &self.client_config {
|
||||
@@ -129,6 +100,8 @@ impl PagerClient {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Private helpers (called by send_page) ---
|
||||
|
||||
async fn send_page_via_rootly(
|
||||
&self,
|
||||
notification: &NotificationDetails,
|
||||
@@ -150,10 +123,8 @@ impl PagerClient {
|
||||
|
||||
debug!("Sending page via Rootly (source={}): {}", source, notification.title);
|
||||
|
||||
// Build labels for Rootly's structured metadata.
|
||||
let labels = self.build_labels(notification);
|
||||
|
||||
// Build the enriched description from the notification's atomic sections.
|
||||
let description = notification.build_enriched_description(
|
||||
self.application_name.as_deref(),
|
||||
self.service_id.as_deref(),
|
||||
@@ -199,4 +170,39 @@ impl PagerClient {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Build structured labels from the client config and notification fields.
|
||||
fn build_labels(&self, notification: &NotificationDetails) -> Option<Vec<(String, String)>> {
|
||||
let mut labels: Vec<(String, String)> = Vec::new();
|
||||
|
||||
if let Some(name) = &self.application_name {
|
||||
labels.push(("application_name".to_string(), name.clone()));
|
||||
}
|
||||
|
||||
if let Some(id) = &self.service_id {
|
||||
labels.push(("service_id".to_string(), id.clone()));
|
||||
}
|
||||
|
||||
if let Some(env) = &self.environment {
|
||||
labels.push(("environment".to_string(), env.clone()));
|
||||
}
|
||||
|
||||
if let Some(h) = &self.hostname {
|
||||
labels.push(("hostname".to_string(), h.clone()));
|
||||
}
|
||||
|
||||
if let Some(method) = ¬ification.http_method {
|
||||
labels.push(("http_method".to_string(), method.clone()));
|
||||
}
|
||||
|
||||
if let Some(path) = ¬ification.http_path {
|
||||
labels.push(("http_path".to_string(), path.clone()));
|
||||
}
|
||||
|
||||
if let Some(status_code) = notification.http_status_code {
|
||||
labels.push(("http_status_code".to_string(), status_code.to_string()));
|
||||
}
|
||||
|
||||
if labels.is_empty() { None } else { Some(labels) }
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user