docs: tambah doc comment, logging, dan inline comments di semua 255 file
Meliputi: - File-level //! doc comment: tujuan file, alur kerja, komponen utama - Function-level /// doc comment: apa, parameter, return, flow, edge cases - Struct/enum/trait /// doc comment: peran, field docs - Tracing logging (tracing::info!/debug!/trace!/warn!/error!) di setiap fungsi - Inline comments untuk variable dan branching logic penting - Seluruh 8 crates di workspace: zesdex-backend, zesdex-cms, zesdex-entities, zesdex-iam, zesdex-infra, zesdex-ipc, zesdex-middleware, zesdex-utils - Build: 0 errors, 242/242 tests passed
This commit is contained in:
@@ -8,6 +8,7 @@ use std::io::Write;
|
||||
use std::path::Path;
|
||||
|
||||
use serde::Serialize;
|
||||
use tracing;
|
||||
|
||||
/// Atomically write serializable `data` to `path`.
|
||||
///
|
||||
@@ -18,8 +19,8 @@ use serde::Serialize;
|
||||
/// the existing extension -- correct for `foo.json` -> `foo.tmp`. For paths
|
||||
/// without an extension (unlikely in this codebase), appends `.tmp`.
|
||||
pub fn write_json_atomic<T: Serialize>(path: &Path, data: &T, mode: Option<u32>) -> anyhow::Result<()> {
|
||||
let tmp = path.with_extension("tmp");
|
||||
let bytes = serde_json::to_vec_pretty(data)?;
|
||||
let tmp = path.with_extension("tmp"); // temporary sibling for atomic rename
|
||||
let bytes = serde_json::to_vec_pretty(data)?; // pretty-printed JSON
|
||||
{
|
||||
let mut f = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
@@ -27,7 +28,7 @@ pub fn write_json_atomic<T: Serialize>(path: &Path, data: &T, mode: Option<u32>)
|
||||
.write(true)
|
||||
.open(&tmp)?;
|
||||
f.write_all(&bytes)?;
|
||||
f.sync_all()?;
|
||||
f.sync_all()?; // flush kernel buffers to disk
|
||||
}
|
||||
if let Some(m) = mode {
|
||||
#[cfg(unix)]
|
||||
@@ -38,9 +39,10 @@ pub fn write_json_atomic<T: Serialize>(path: &Path, data: &T, mode: Option<u32>)
|
||||
#[cfg(not(unix))]
|
||||
{ let _ = m; }
|
||||
}
|
||||
std::fs::rename(&tmp, path)?;
|
||||
std::fs::rename(&tmp, path)?; // atomic move (POSIX guarantees it is atomic within the same fs)
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = std::fs::File::open(parent).and_then(|d| d.sync_all());
|
||||
}
|
||||
tracing::debug!("atomically wrote {} bytes to {:?}", bytes.len(), path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
//! Terminal clipboard access via the OSC-52 escape sequence.
|
||||
//!
|
||||
//! Provides a single function, [`write_osc52`], that writes `text` to the
|
||||
//! system clipboard by emitting the OSC-52 control sequence (`ESC ] 52 ; c ;
|
||||
//! <base64> ESC \`). This works in iTerm2, Kitty, tmux, and most modern
|
||||
//! terminal emulators without external binaries.
|
||||
|
||||
use std::io::{self, Write};
|
||||
use tracing;
|
||||
|
||||
/// Write `text` to the terminal's clipboard using the OSC-52 escape sequence.
|
||||
///
|
||||
@@ -20,7 +28,9 @@ pub fn write_osc52(output: &mut impl Write, text: &str) -> io::Result<()> {
|
||||
// OSC-52: ESC ] 52 ; c ; <base64> ST
|
||||
// Where c = "c" for clipboard, ST = ESC \
|
||||
write!(output, "\x1b]52;c;{encoded}\x1b\\")?;
|
||||
output.flush()
|
||||
output.flush()?;
|
||||
tracing::debug!("wrote {} bytes via OSC-52 clipboard escape", encoded.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//! Shared error types for the zesdex codebase.
|
||||
//!
|
||||
//! Defines [`Error`], a unified error enum covering I/O, JSON, parse,
|
||||
//! not-found, and invalid-input cases, plus a [`Result`] type alias.
|
||||
//! Conversions from `std::io::Error` and `serde_json::Error` are provided
|
||||
//! via `From` impls.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Unified error type for the zesdex codebase.
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
#![allow(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cast_precision_loss,
|
||||
clippy::cast_possible_wrap
|
||||
)]
|
||||
//! # zesdex-utils
|
||||
//!
|
||||
//! General-purpose utilities used across the zesdex codebase.
|
||||
//!
|
||||
//! ## Components
|
||||
//!
|
||||
//! - **`atomic_write`** — Atomic JSON file writing via a temp-file + rename strategy.
|
||||
//! - **`clipboard`** — System clipboard access (copy, paste) for the TUI.
|
||||
//! - **`error`** — Shared `Error` enum and `Result` type for the crate.
|
||||
//! - **`logger`** — `tracing` / `tracing-subscriber` initialisation for the daemon.
|
||||
//! - **`pagination`** — Generic offset/limit pagination helper.
|
||||
//! - **`sanitize`** — Input sanitisation: HTML escaping, filename cleaning, path traversal
|
||||
//! prevention, session-id validation, string truncation.
|
||||
//! - **`slug`** — String slugification (URL-safe, lowercased, hyphen-separated).
|
||||
|
||||
pub mod atomic_write;
|
||||
pub use atomic_write::write_json_atomic;
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
//! Tracing initialisation for the zesdex daemon.
|
||||
//!
|
||||
//! Writes structured logs to a timestamped file under
|
||||
//! `$DATA_DIR/zesdex/logs/zesdex-<timestamp>.log`, with env-filter support
|
||||
//! via `RUST_LOG` or `ZESDEX_LOG`. Falls back to `/dev/null` if the log
|
||||
//! directory or file cannot be created, ensuring the daemon never panics
|
||||
//! at startup due to logging failures.
|
||||
|
||||
use std::fs::{self, OpenOptions};
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//! Generic pagination utilities for list endpoints.
|
||||
//!
|
||||
//! Provides [`Paginated<T>`], a serde-compatible response wrapper with
|
||||
//! computed metadata (total pages, prev/next), and two helper functions:
|
||||
//! [`paginate`] for in-memory slicing and [`page_params`] for SQL offset/limit
|
||||
//! computation.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A generic paginated response.
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
//! Input sanitisation utilities.
|
||||
//!
|
||||
//! Functions for cleaning filenames, paths, HTML content, and session IDs.
|
||||
//! Each function is pure (no I/O or allocations beyond the return value).
|
||||
|
||||
use tracing;
|
||||
|
||||
/// Characters that are invalid in filenames on most operating systems.
|
||||
const INVALID_FILENAME_CHARS: &[char] = &[
|
||||
'/', '\0', '<', '>', ':', '"', '\\', '|', '?', '*', '\x01', '\x02', '\x03', '\x04', '\x05',
|
||||
@@ -16,7 +23,7 @@ pub fn sanitize_filename(s: &str) -> String {
|
||||
.chars()
|
||||
.map(|c| {
|
||||
if INVALID_FILENAME_CHARS.contains(&c) {
|
||||
'_'
|
||||
'_' // replace invalid char with underscore
|
||||
} else {
|
||||
c
|
||||
}
|
||||
@@ -27,9 +34,11 @@ pub fn sanitize_filename(s: &str) -> String {
|
||||
let trimmed = sanitized.trim_matches(|c: char| c == '.' || c.is_whitespace());
|
||||
|
||||
if trimmed.is_empty() {
|
||||
tracing::warn!("filename became empty after sanitization, using fallback 'unnamed'");
|
||||
return "unnamed".to_string();
|
||||
}
|
||||
|
||||
tracing::trace!("sanitized filename: '{s}' -> '{trimmed}'");
|
||||
trimmed.to_string()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,11 @@
|
||||
//! String slugification utilities.
|
||||
//!
|
||||
//! Provides [`slugify`] for turning arbitrary strings into URL-safe,
|
||||
//! lowercased, hyphen-separated slugs, and [`slug_path`] for joining a
|
||||
//! base directory with a slugified name.
|
||||
|
||||
use std::path::{Path, PathBuf};
|
||||
use tracing;
|
||||
|
||||
const MAX_SLUG_LENGTH: usize = 80;
|
||||
|
||||
@@ -16,21 +23,22 @@ const MAX_SLUG_LENGTH: usize = 80;
|
||||
#[must_use]
|
||||
pub fn slugify(s: &str) -> Option<String> {
|
||||
if s.is_empty() {
|
||||
tracing::trace!("slugify: empty input");
|
||||
return None;
|
||||
}
|
||||
|
||||
let lower = s.to_lowercase();
|
||||
let lower = s.to_lowercase(); // step 1: lowercase
|
||||
|
||||
// Replace non-alphanumeric (except dash/underscore) sequences with '-'
|
||||
let mut slug = String::with_capacity(lower.len());
|
||||
let mut prev_was_sep = false;
|
||||
let mut prev_was_sep = false; // track consecutive separators
|
||||
|
||||
for c in lower.chars() {
|
||||
if c.is_alphanumeric() {
|
||||
slug.push(c);
|
||||
prev_was_sep = false;
|
||||
} else if !prev_was_sep {
|
||||
slug.push('-');
|
||||
slug.push('-'); // separator hyphen
|
||||
prev_was_sep = true;
|
||||
}
|
||||
// else skip consecutive separators
|
||||
@@ -40,6 +48,7 @@ pub fn slugify(s: &str) -> Option<String> {
|
||||
let slug = slug.trim_matches('-').to_string();
|
||||
|
||||
if slug.is_empty() {
|
||||
tracing::trace!("slugify: no slug characters remaining");
|
||||
return None;
|
||||
}
|
||||
|
||||
@@ -62,6 +71,7 @@ pub fn slugify(s: &str) -> Option<String> {
|
||||
slug
|
||||
};
|
||||
|
||||
tracing::trace!("slugify: '{s}' -> '{slug}'");
|
||||
Some(slug)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user