hledger_fmt/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2// Performance-focused clippy lints to catch common performance issues
3#![warn(clippy::perf)]
4#![warn(clippy::missing_inline_in_public_items)]
5#![warn(clippy::large_types_passed_by_value)]
6#![warn(clippy::inefficient_to_string)]
7#![warn(clippy::clone_on_ref_ptr)]
8
9#[cfg(not(feature = "std"))]
10extern crate alloc;
11
12#[cfg(not(feature = "std"))]
13pub(crate) use alloc::{boxed::Box, format, string::String, vec::Vec};
14
15#[cfg(feature = "std")]
16pub(crate) use std::{boxed::Box, format, string::String, vec::Vec};
17
18mod byte_str;
19#[doc(hidden)]
20#[cfg(feature = "cli")]
21pub mod cli;
22#[cfg(feature = "cli")]
23pub mod file_path;
24mod formatter;
25mod parser;
26#[cfg(any(test, feature = "tracing"))]
27mod tracing;
28
29pub use formatter::FormatJournalOptions;
30pub use parser::errors::SyntaxError;
31
32/// Format an hledger journal string file content as a String.
33#[inline]
34pub fn format_journal(content: &str) -> Result<String, SyntaxError> {
35    let buffer = format_journal_bytes(content.as_bytes())?;
36    // SAFETY: The formatter only outputs valid UTF-8 since it only writes:
37    // 1. Slices from the valid UTF-8 input
38    // 2. ASCII characters (spaces, newlines, comment prefixes)
39    let formatted = String::from_utf8(buffer).expect("formatter should only produce valid UTF-8");
40    Ok(formatted)
41}
42
43/// Format an hledger journal string file content as a String with specified options.
44#[inline]
45pub fn format_journal_with_options(
46    content: &str,
47    options: formatter::FormatJournalOptions,
48) -> Result<String, SyntaxError> {
49    let parsed: Vec<parser::JournalCstNode<'_>> = parser::parse_content(content.as_bytes())?;
50    let merged_options = options.with_estimated_length(content.len());
51    let formatted_bytes = formatter::format_content_with_options(&parsed, &merged_options);
52    // SAFETY: The formatter only outputs valid UTF-8 since it only writes:
53    // 1. Slices from the valid UTF-8 input
54    // 2. ASCII characters (spaces, newlines, comment prefixes)
55    let formatted =
56        String::from_utf8(formatted_bytes).expect("formatter should only produce valid UTF-8");
57    Ok(formatted)
58}
59
60/// Format an hledger journal file content as bytes.
61#[inline]
62pub fn format_journal_bytes(content: &[u8]) -> Result<Vec<u8>, SyntaxError> {
63    let parsed = parser::parse_content(content)?;
64    let opts = formatter::FormatJournalOptions::new().with_estimated_length(content.len());
65    Ok(formatter::format_content_with_options(&parsed, &opts))
66}
67
68/// Format an hledger journal file content as bytes with specified options.
69#[inline]
70pub fn format_journal_bytes_with_options(
71    content: &[u8],
72    options: formatter::FormatJournalOptions,
73) -> Result<Vec<u8>, SyntaxError> {
74    let parsed = parser::parse_content(content)?;
75    let merged_options = options.with_estimated_length(content.len());
76    let formatted = formatter::format_content_with_options(&parsed, &merged_options);
77    Ok(formatted)
78}
79
80#[cfg(feature = "bench")]
81#[inline]
82pub fn format_parsed_journal(parsed: &parser::JournalFile) -> Result<Vec<u8>, SyntaxError> {
83    let format_opts = formatter::FormatJournalOptions::new();
84    let formatted = formatter::format_content_with_options(parsed, &format_opts);
85    Ok(formatted)
86}
87
88#[cfg(feature = "bench")]
89pub use parser::parse_content;