Conventions
Code Style
Patterns and anti-patterns for Rust and TypeScript
Rust
Module Structure
mod.rs should only contain pub mod declarations:
// Good
pub mod analyzer;
pub mod baseline;
// Bad - no logic in mod.rs
pub mod analyzer;
pub fn some_function() { }Error Handling
thiserrorfor library errors (engine)anyhowfor application errors (api, cli)- Always use
?- neverunwrap()in production
// Library
#[derive(Debug, thiserror::Error)]
pub enum ExfilError {
#[error("Capture error: {0}")]
Capture(String),
}
// Application
fn load_config(path: &str) -> anyhow::Result<AppConfig> {
let content = std::fs::read_to_string(path)
.context("Failed to read config")?;
Ok(serde_yaml::from_str(&content)?)
}Import Order
// 1. Standard library
use std::sync::Arc;
// 2. External crates
use serde::{Deserialize, Serialize};
// 3. Workspace crates
use exfil_engine::common::types::ThreatEvent;
// 4. Current crate
use crate::handlers::threats;Functions
- Max 40 lines per function
- Return
Result<T>over panicking - Document public APIs with
///
Protobuf
Style
- One
.protoper service/domain (exfil.proto) - Messages:
PascalCase, fields:snake_case - Always specify field numbers explicitly
- Use
bytesfor raw payloads,stringfor text
service ExfilService {
rpc StreamPackets(stream NetworkPacket) returns (StreamResponse);
}
message NetworkPacket {
uint32 protocol = 1;
uint32 local_ip = 2;
uint32 remote_ip = 3;
bytes payload = 7;
}TypeScript
Component Structure
// 1. Imports
import { useState } from "react";
// 2. Types
interface ThreatListProps {
filter?: string;
}
// 3. Named export
export function ThreatList({ filter }: ThreatListProps) {
return <div>{/* ... */}</div>;
}Rules
- Server Components by default -
"use client"only for interactivity - Named exports - no
export defaultexceptpage.tsx/layout.tsx - No barrel files - no
index.tsre-exporting - Biome for linting:
bun run check,bun run format