Add docs
This commit is contained in:
parent
14e8a32f35
commit
e18612fd4a
|
|
@ -1,99 +0,0 @@
|
|||
# Code Refactoring Summary
|
||||
|
||||
## Overview
|
||||
|
||||
The `parser.rs` file has been refactored from a single 600+ line file into a more maintainable, modular structure. The refactoring improves code organization, readability, and testability while maintaining all existing functionality.
|
||||
|
||||
## New Module Structure
|
||||
|
||||
### `asn1_types.rs`
|
||||
- **Purpose**: Core ASN.1 type definitions and basic parsing
|
||||
- **Key Types**: `TagClass`, `ParentCtx`, `TlvInfo`
|
||||
- **Key Functions**: `parse_tlv()`, `looks_like_single_der()`, `as_i128()`, `decode_oid()`, `ip_to_string()`
|
||||
- **Tests**: Basic TLV parsing, OID decoding, IP address parsing
|
||||
|
||||
### `encoding_utils.rs`
|
||||
- **Purpose**: Hex encoding/decoding and bit string formatting utilities
|
||||
- **Key Types**: `BitsFormat`, `BitsDisplay`
|
||||
- **Key Functions**: `hex_to_bytes()`, `to_hex_upper()`, `bits_to_string_truncated()`, `format_bitstring_exact()`
|
||||
- **Tests**: Hex conversion, bit string formatting, truncation logic
|
||||
|
||||
### `oid_registry.rs`
|
||||
- **Purpose**: Object Identifier (OID) to human-readable name mappings
|
||||
- **Key Functions**: `oid_friendly()` - maps OIDs to descriptive names
|
||||
- **Content**: Comprehensive registry of X.509, cryptographic, and extension OIDs
|
||||
|
||||
### `time_utils.rs`
|
||||
- **Purpose**: ASN.1 time format parsing and human-readable conversion
|
||||
- **Key Functions**: `human_utctime()`, `human_generalizedtime()`
|
||||
- **Features**: Converts ASN.1 time formats to readable ISO format
|
||||
|
||||
### `certificate_utils.rs`
|
||||
- **Purpose**: X.509 certificate-specific parsing utilities
|
||||
- **Key Functions**: `decode_keyusage_from_octet()`, `try_decode_inner_seq()`
|
||||
- **Features**: Key usage bit field interpretation, sequence heuristics
|
||||
|
||||
### `value_printer.rs`
|
||||
- **Purpose**: ASN.1 value formatting and display logic
|
||||
- **Key Types**: `PrintCtx`
|
||||
- **Key Functions**: `print_primitive_value_with_label()`, individual type printers
|
||||
- **Features**: Colored output, context-aware labeling
|
||||
|
||||
### `pretty_printer.rs`
|
||||
- **Purpose**: Main DER pretty-printing orchestration
|
||||
- **Key Functions**: `pretty_print_der()` - main recursive printer
|
||||
- **Features**: Constructed type handling, label determination, context tracking
|
||||
|
||||
### `parser.rs` (simplified)
|
||||
- **Purpose**: Public API and integration point
|
||||
- **Key Functions**: `parse_and_print()` - main entry point
|
||||
- **Content**: Re-exports and integration tests
|
||||
|
||||
## Key Improvements
|
||||
|
||||
### 1. **Separation of Concerns**
|
||||
- Each module has a single, well-defined responsibility
|
||||
- Clear boundaries between parsing, formatting, and display logic
|
||||
- Certificate-specific code isolated from generic ASN.1 handling
|
||||
|
||||
### 2. **Improved Testability**
|
||||
- Each module can be tested independently
|
||||
- Tests moved to their appropriate modules
|
||||
- Better test coverage through focused unit tests
|
||||
|
||||
### 3. **Enhanced Maintainability**
|
||||
- Smaller, focused files are easier to understand and modify
|
||||
- Related functionality grouped together
|
||||
- Clear module dependencies
|
||||
|
||||
### 4. **Better Code Organization**
|
||||
- Type definitions centralized in `asn1_types`
|
||||
- Utility functions properly categorized
|
||||
- Configuration and display logic separated
|
||||
|
||||
### 5. **Reduced Coupling**
|
||||
- Modules depend on well-defined interfaces
|
||||
- Functionality can be reused across modules
|
||||
- Easier to add new features without touching existing code
|
||||
|
||||
## Backward Compatibility
|
||||
|
||||
- All existing public APIs maintained
|
||||
- All tests continue to pass
|
||||
- No functional changes to the application behavior
|
||||
- Same command-line interface and output format
|
||||
|
||||
## Benefits for Future Development
|
||||
|
||||
1. **Easier Feature Addition**: New ASN.1 types can be added by extending the appropriate modules
|
||||
2. **Improved Debugging**: Issues can be isolated to specific modules
|
||||
3. **Better Documentation**: Each module can be documented independently
|
||||
4. **Code Reuse**: Utility functions are more discoverable and reusable
|
||||
5. **Performance Optimization**: Individual modules can be optimized without affecting others
|
||||
|
||||
## Testing
|
||||
|
||||
All existing tests pass, and new module-specific tests have been added:
|
||||
- `cargo test` runs 14 unit tests across multiple modules
|
||||
- Integration tests verify end-to-end functionality
|
||||
- No regression in functionality or performance
|
||||
60
src/cli.rs
60
src/cli.rs
|
|
@ -1,60 +1,94 @@
|
|||
use clap::{Parser, ValueEnum};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Color output control options
|
||||
#[derive(Copy, Clone, Debug, ValueEnum)]
|
||||
pub enum Color {
|
||||
/// Automatically detect if output supports color
|
||||
Auto,
|
||||
/// Always use colored output
|
||||
Always,
|
||||
/// Never use colored output
|
||||
Never,
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "asn1parser", about = "DER ASN.1 pretty-printer using yasna")]
|
||||
#[command(
|
||||
name = "asn1parse",
|
||||
version,
|
||||
about = "DER ASN.1 pretty-printer using yasna",
|
||||
long_about = "A command-line tool for parsing and pretty-printing DER-encoded ASN.1 data. Supports reading from files, stdin, or hex strings with automatic format detection."
|
||||
)]
|
||||
pub struct CliArgs {
|
||||
#[arg(long, value_enum, default_value_t = Color::Auto)]
|
||||
pub color: Color,
|
||||
|
||||
/// Hex-encoded DER input (read from stdin if omitted)
|
||||
#[arg(help = "Hex-encoded DER input (read from stdin if omitted)")]
|
||||
pub hex: Option<String>,
|
||||
|
||||
/// Color output control
|
||||
#[arg(long, value_enum, default_value_t = Color::Auto, help = "Control colored output")]
|
||||
pub color: Color,
|
||||
|
||||
/// Read input from file (mutually exclusive with HEX argument)
|
||||
#[arg(short, long)]
|
||||
#[arg(short, long, help = "Read input from file (mutually exclusive with HEX argument)")]
|
||||
pub file: Option<PathBuf>,
|
||||
|
||||
/// Input format selection for file/stdin
|
||||
#[arg(long, value_enum, default_value_t = InputFormat::Auto)]
|
||||
#[arg(
|
||||
long,
|
||||
value_enum,
|
||||
default_value_t = InputFormat::Auto,
|
||||
help = "Input format selection for file/stdin"
|
||||
)]
|
||||
pub input_format: InputFormat,
|
||||
|
||||
/// Try to decode OCTET/BitString payloads if they contain DER
|
||||
#[arg(long)]
|
||||
#[arg(long, help = "Try to decode OCTET/BitString payloads if they contain DER")]
|
||||
pub recursive: bool,
|
||||
|
||||
|
||||
/// Truncate displayed BIT STRINGs to at most N bits (omit to show all)
|
||||
#[arg(long, value_name = "N")]
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "N",
|
||||
help = "Truncate displayed BIT STRINGs to at most N bits (omit to show all)"
|
||||
)]
|
||||
pub bits_truncate: Option<usize>,
|
||||
|
||||
/// Truncate displayed BIT STRINGs to at most N bytes (hex mode)
|
||||
#[arg(long, value_name = "N")]
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "N",
|
||||
help = "Truncate displayed BIT STRINGs to at most N bytes (hex mode)"
|
||||
)]
|
||||
pub bits_truncate_bytes: Option<usize>,
|
||||
|
||||
/// BIT STRING display format
|
||||
#[arg(long, value_enum, default_value_t = BitsFormat::Auto)]
|
||||
#[arg(
|
||||
long,
|
||||
value_enum,
|
||||
default_value_t = BitsFormat::Auto,
|
||||
help = "BIT STRING display format"
|
||||
)]
|
||||
pub bits_format: BitsFormat,
|
||||
}
|
||||
|
||||
/// Input format detection and specification
|
||||
#[derive(Copy, Clone, Debug, ValueEnum)]
|
||||
pub enum InputFormat {
|
||||
/// Automatically detect input format (hex text vs binary DER)
|
||||
Auto,
|
||||
/// Force interpretation as hex-encoded text
|
||||
Hex,
|
||||
/// Force interpretation as binary DER data
|
||||
Der,
|
||||
}
|
||||
|
||||
// Removed certificate-specific view; generic pretty-printer is always used.
|
||||
|
||||
/// BIT STRING display format options
|
||||
#[derive(Copy, Clone, Debug, ValueEnum)]
|
||||
pub enum BitsFormat {
|
||||
/// Automatically choose the best format
|
||||
Auto,
|
||||
/// Display as binary bits (0s and 1s)
|
||||
Bits,
|
||||
/// Display as hexadecimal
|
||||
Hex,
|
||||
}
|
||||
|
|
|
|||
63
src/main.rs
63
src/main.rs
|
|
@ -66,19 +66,58 @@ fn main() {
|
|||
parse_hex_or_exit(&hex)
|
||||
} else {
|
||||
// Read from stdin
|
||||
let mut buf = String::new();
|
||||
if io::stdin().read_to_string(&mut buf).is_ok() && !buf.trim().is_empty() {
|
||||
let text = buf;
|
||||
let candidate = strip_ws(&text);
|
||||
if matches!(args.input_format, cli::InputFormat::Der) {
|
||||
// If explicitly der, read raw bytes from stdin (not supported via Read::read_to_string)
|
||||
eprintln!("Reading raw DER from stdin is not supported in text mode. Use --file or provide hex on stdin.");
|
||||
std::process::exit(2);
|
||||
match args.input_format {
|
||||
cli::InputFormat::Der => {
|
||||
// Read raw binary data from stdin
|
||||
let mut buf = Vec::new();
|
||||
if let Err(e) = io::stdin().read_to_end(&mut buf) {
|
||||
eprintln!("Failed to read from stdin: {}", e);
|
||||
std::process::exit(2);
|
||||
}
|
||||
if buf.is_empty() {
|
||||
eprintln!("No input provided. Pass HEX, use --file, or pipe data.");
|
||||
std::process::exit(2);
|
||||
}
|
||||
buf
|
||||
}
|
||||
cli::InputFormat::Hex => {
|
||||
// Read text and parse as hex
|
||||
let mut buf = String::new();
|
||||
if let Err(e) = io::stdin().read_to_string(&mut buf) {
|
||||
eprintln!("Failed to read from stdin: {}", e);
|
||||
std::process::exit(2);
|
||||
}
|
||||
if buf.trim().is_empty() {
|
||||
eprintln!("No input provided. Pass HEX, use --file, or pipe data.");
|
||||
std::process::exit(2);
|
||||
}
|
||||
let hex = strip_ws(&buf);
|
||||
parse_hex_or_exit(&hex)
|
||||
}
|
||||
cli::InputFormat::Auto => {
|
||||
// Try to read as text first (for hex), fall back to binary
|
||||
let mut buf = Vec::new();
|
||||
if let Err(e) = io::stdin().read_to_end(&mut buf) {
|
||||
eprintln!("Failed to read from stdin: {}", e);
|
||||
std::process::exit(2);
|
||||
}
|
||||
if buf.is_empty() {
|
||||
eprintln!("No input provided. Pass HEX, use --file, or pipe data.");
|
||||
std::process::exit(2);
|
||||
}
|
||||
|
||||
// Try to interpret as UTF-8 hex text
|
||||
if let Ok(text) = String::from_utf8(buf.clone()) {
|
||||
let candidate = strip_ws(&text);
|
||||
if candidate.chars().all(|c| c.is_ascii_hexdigit()) && candidate.len() % 2 == 0 && !candidate.is_empty() {
|
||||
parse_hex_or_exit(&candidate)
|
||||
} else {
|
||||
buf // Use as raw binary data
|
||||
}
|
||||
} else {
|
||||
buf // Use as raw binary data
|
||||
}
|
||||
}
|
||||
parse_hex_or_exit(&candidate)
|
||||
} else {
|
||||
eprintln!("No input provided. Pass HEX, use --file, or pipe data.");
|
||||
std::process::exit(2);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue