include_preprocessor/lib.rs
1//! # Include preprocessor
2//!
3//! Inlines UTF-8 encoded files into other UTF-8 encoded files using C-preprocessor style `#include`
4//! directives.
5//!
6//! ## Basic Example
7//!
8//! Given file `hello.txt`:
9//!
10//! ```txt
11//! Hello
12//! #include "world.txt"
13//! ```
14//!
15//! ...and file `world.txt` (in the same directory):
16//!
17//! ```txt
18//! world!
19//! ```
20//!
21//! ...then the following code:
22//!
23//! ```no_run
24//! use std::path::Path;
25//! use include_preprocessor::{preprocess, SearchPaths};
26//!
27//! let entry_point = Path::new("./hello.txt");
28//! let search_paths = SearchPaths::new();
29//! let output_sink = String::new();
30//! let result = preprocess(&entry_point, search_paths, output_sink).unwrap();
31//! ```
32//!
33//! ... will produce the following `result` string:
34//!
35//! ```txt
36//! Hello
37//! world!
38//! ```
39//!
40//! ## The `#include` directive
41//!
42//! The `#include` directive must begin with the `#include` string. The `#include` directive takes a
43//! single argument. This argument may either be:
44//!
45//! - A file path wrapped in double-quotes:
46//! ```txt
47//! #include "path/to/file"
48//! ```
49//!
50//! - A file path wrapped in angle brackets:
51//! ```txt
52//! #include <path/to/file>
53//! ```
54//!
55//! The `#include` directive must be on its own line, as demarcated by ASCII line-endings (either
56//! newlines `\n` or carriage returns `\r`). The `#` must not be preceded by any other characters,
57//! including whitespace. There must be at least one ASCII whitespace character between `#include`
58//! and the file path. It may only be succeeded by whitespace. There must not be any line-endings
59//! inside the file path.
60//!
61//! ```txt
62//! // Invalid, other characters precede the directive
63//! Hello #include "path/to/file"
64//!
65//! // Invalid, preceded by whitespace
66//! #include "path/to/file"
67//!
68//! // Invalid, other characters succeed the directive
69//! #include "path/to/file" world!
70//!
71//! // Invalid, no whitespace between `include` and the file path
72//! #include"path/to/file"
73//!
74//! // Invalid, a line-ending inside the file path
75//! #include "path/to
76//! /file"
77//! ```
78//!
79//! ## File Resolution
80//!
81//! File resolution is configured by passing a [SearchPaths] instance as the second argument to
82//! the [preprocess] function. A [SearchPaths] instance is constructed by adding "base paths" with
83//! [SearchPaths::push_base_path] and/or "quoted paths" with [SearchPaths::push_quoted_path]:
84//!
85//! ```rust
86//! use include_preprocessor::SearchPaths;
87//!
88//! let mut search_paths = SearchPaths::new();
89//!
90//! search_paths.push_base_path("/some/path");
91//! search_paths.push_quoted_path("/some/other/path");
92//! ```
93//!
94//! You may add any number of base paths and quote paths.
95//!
96//! An absolute path will always resolve to itself. For relative paths, resolution further depends
97//! on whether the included path was wrapped in angle brackets (`<some/path>`) or in quotes
98//! (`"some/path"`).
99//!
100//! For a relative path wrapped in angle brackets, resolution will attempt to join (see
101//! [Path::join](std::path::Path::join)) the relative path with each of the "base paths" added to the
102//! [SearchPaths] instance, in the order in which the base paths were added. The resolved path will
103//! be the first such path that resolves to a file (see [Path::is_file](std::path::Path::is_file)).
104//! If none of these paths produce a valid file path, then [preprocess] will return an error.
105//!
106//! For a relative path wrapped in quotes, resolution will go through the following steps (in
107//! order):
108//!
109//! 1. It will attempt to join the relative path with the path of the parent directory of the
110//! including file.
111//! 2. It will attempt to join the relative path with each of the "quoted paths" added to the
112//! [SearchPaths] instance, in the order in which they were added.
113//! 3. It will attempt to join the relative path with each of the "base paths" added to the
114//! [SearchPaths] instance, in the order in which they were added.
115//!
116//! The resolved path will be the first such path that resolves to a file. If none of these paths
117//! produce a valid file path, then [preprocess] will return an error.
118//!
119//! ## The `#pragma once` Directive
120//!
121//! This `#include` pre-processor also supports the "pragma once" directive, a non-standard, but
122//! commonly supported directive in C/C++ preprocessors:
123//!
124//! ```txt
125//! #pragma once
126//! ```
127//!
128//! The `#pragma once` directive must be on its own line, as demarcated by ASCII line-endings
129//! (either newlines `\n` or carriage returns `\r`). The `#` must not be preceded by any other
130//! characters, including whitespace. There must be at least one ASCII whitespace character between
131//! `#pragma` and `once`. It may only be succeeded by whitespace.
132//!
133//! If a `#pragma once` directive occurs anywhere in a file, then if that file is included into
134//! the final output through `#include` directives, only the first time that any `#include`
135//! directive resolves to this file will the file's contents be inlined; all subsequent `#include`
136//! directives that resolve to the file will simply cause the `#include` directive's line to be
137//! elided from the final output. To determine if two different `#include` directives resolve to the
138//! same file, the pre-processor compares the "canonicalized" paths (see
139//! [Path::canonicalize](std::path::Path::canonicalize)) after file resolution (see the
140//! [File Resolution](crate#file-resolution) section above).
141//!
142//! For example, given the file `grandparent`:
143//!
144//! ```pseudocode
145//! #pragma once
146//!
147//! struct MyStruct {
148//! a: u32
149//! }
150//! ```
151//!
152//! ...and the file `parent`:
153//!
154//! ```pseudocode
155//! #include "grandparent"
156//! ```
157//!
158//! ...and the file `child`:
159//!
160//! ```pseudocode
161//! #include "grandparent"
162//! #include "parent"
163//! ```
164//!
165//! ...then include pre-processing will produce the following result:
166//!
167//! ```pseudocode
168//!
169//! struct MyStruct {
170//! a: u32
171//! }
172//! ```
173//!
174//! Most programming languages do not allow multiple struct definitions with the same name, so
175//! without the `#pragma once` directive, the above example would probably have resulted in code
176//! that fails to compile.
177//!
178//! ## The `include_str_ipp` Macro
179//!
180//! This crate has a companion crate called `include-preprocessor-macro` that defines the
181//! `include_str_ipp` macro, which covers basic use-cases. It behaves similarly to the [include_str]
182//! macro and will inline pre-processed file contents into your code as a `&'static str`. For
183//! details on the macro's use, refer to the documentation of the `include-preprocessor-macro`
184//! crate.
185//!
186//! ## Custom [OutputSink]
187//!
188//! The third argument to the [preprocess] function must implement the [OutputSink] trait. In the
189//! basic example above, we use a [String] as the output-sink ([String] implements the [OutputSink]
190//! trait), but you may also provide a custom output-sink. The output-sink will receive chunks of
191//! text via [OutputSink::sink] and [OutputSink::sink_source_mapped]. Concatenating all chunk text
192//! strings in the order in which they are received will produce the intended result string. Most
193//! chunks will be delivered via [OutputSink::sink_source_mapped], which receives
194//! [SourceMappedChunk]s, which in addition to the chunk text, also contain information about the
195//! original text span that produced the chunk; this information may be useful for the construction
196//! of a source-map. The pre-processor sometimes inserts blank lines for correctness; such lines
197//! will not map to a source span and will instead be delivered via [OutputSink::sink].
198//!
199//! ## Source Tracking
200//!
201//! When using the [preprocess_with_source_tracker] function rather than the regular [preprocess]
202//! function, the function takes an additional fourth argument that must implement the
203//! [SourceTracker] trait. This source-tracker will be notified for each file that is included
204//! through an `#include` directive via [SourceTracker::track] with the path of the file.
205//!
206//! This is intended to be used when building proc-macros. It can be used to make the Rust compiler
207//! track files for changes that require recompilation when using Rust with incremental compilation.
208//! For example, the `include-preprocessor-macro` crate defines the following [SourceTracker]:
209//!
210//! ```ignore
211//! use proc_macro::tracked_path;
212//!
213//! struct ProcMacroPathTracker;
214//!
215//! impl SourceTracker for ProcMacroPathTracker {
216//! fn track(&mut self, path: &Path, _source: &str) {
217//! tracked_path::path(path.to_str().expect("cannot track non-unicode path"));
218//! }
219//! }
220//! ```
221
222mod include_preprocessor;
223mod line_parser;
224
225pub use self::include_preprocessor::{
226 Error, FileNotFoundError, OutputSink, ParseError, SearchPaths, SourceMappedChunk,
227 SourceTracker, preprocess, preprocess_with_source_tracker,
228};