genco/lang/
mod.rs

1//! Language specialization for genco
2//!
3//! This module contains sub-modules which provide implementations of the [Lang]
4//! trait to configure genco for various programming languages.
5//!
6//! This module also provides a dummy [Lang] implementation for `()`.
7//!
8//! This allows `()` to be used as a quick and dirty way to do formatting,
9//! usually for examples.
10//!
11//! ```rust
12//! use genco::prelude::*;
13//!
14//! let tokens: Tokens = quote!(hello world);
15//! # Ok::<_, genco::fmt::Error>(())
16//! ```
17
18pub mod c;
19pub mod csharp;
20pub mod dart;
21pub mod go;
22pub mod java;
23
24pub mod js;
25pub mod kotlin;
26pub mod nix;
27pub mod python;
28pub mod rust;
29pub mod swift;
30
31pub use self::c::C;
32pub use self::csharp::Csharp;
33pub use self::dart::Dart;
34pub use self::go::Go;
35pub use self::java::Java;
36pub use self::js::JavaScript;
37pub use self::kotlin::Kotlin;
38pub use self::nix::Nix;
39pub use self::python::Python;
40pub use self::rust::Rust;
41pub use self::swift::Swift;
42
43use core::fmt::Write as _;
44
45use crate::fmt;
46use crate::Tokens;
47
48/// Trait to implement for language specialization.
49///
50/// The various language implementations can be found in the [lang][self]
51/// module.
52pub trait Lang: Sized {
53    /// Configuration associated with building a formatting element.
54    type Config;
55    /// State being used during formatting.
56    type Format: Default;
57    /// The type used when resolving imports.
58    type Item: LangItem<Self>;
59
60    /// Provide the default indentation.
61    fn default_indentation() -> fmt::Indentation {
62        fmt::Indentation::Space(4)
63    }
64
65    /// Start a string quote.
66    fn open_quote(
67        out: &mut fmt::Formatter<'_>,
68        _config: &Self::Config,
69        _format: &Self::Format,
70        _has_eval: bool,
71    ) -> fmt::Result {
72        out.write_char('"')?;
73        Ok(())
74    }
75
76    /// End a string quote.
77    fn close_quote(
78        out: &mut fmt::Formatter<'_>,
79        _config: &Self::Config,
80        _format: &Self::Format,
81        _has_eval: bool,
82    ) -> fmt::Result {
83        out.write_char('"')?;
84        Ok(())
85    }
86
87    /// A simple, single-literal string evaluation.
88    fn string_eval_literal(
89        out: &mut fmt::Formatter<'_>,
90        config: &Self::Config,
91        format: &Self::Format,
92        literal: &str,
93    ) -> fmt::Result {
94        Self::start_string_eval(out, config, format)?;
95        out.write_str(literal)?;
96        Self::end_string_eval(out, config, format)?;
97        Ok(())
98    }
99
100    /// Start a string-interpolated eval.
101    fn start_string_eval(
102        _out: &mut fmt::Formatter<'_>,
103        _config: &Self::Config,
104        _format: &Self::Format,
105    ) -> fmt::Result {
106        Ok(())
107    }
108
109    /// End a string interpolated eval.
110    fn end_string_eval(
111        _out: &mut fmt::Formatter<'_>,
112        _config: &Self::Config,
113        _format: &Self::Format,
114    ) -> fmt::Result {
115        Ok(())
116    }
117
118    /// Performing string quoting according to language convention.
119    fn write_quoted(out: &mut fmt::Formatter<'_>, input: &str) -> fmt::Result {
120        out.write_str(input)
121    }
122
123    /// Write a file according to the specified language convention.
124    fn format_file(
125        tokens: &Tokens<Self>,
126        out: &mut fmt::Formatter<'_>,
127        config: &Self::Config,
128    ) -> fmt::Result {
129        let format = Self::Format::default();
130        tokens.format(out, config, &format)
131    }
132}
133
134/// Marker trait indicating that a language supports
135/// [quoted string interpolation].
136///
137/// [quoted string interpolation]: https://docs.rs/genco/0/genco/macro.quote.html#quoted-string-interpolation
138pub trait LangSupportsEval: Lang {}
139
140/// Dummy implementation for a language.
141impl Lang for () {
142    type Config = ();
143    type Format = ();
144    type Item = ();
145}
146
147impl<L> LangItem<L> for ()
148where
149    L: Lang,
150{
151    fn format(&self, _: &mut fmt::Formatter<'_>, _: &L::Config, _: &L::Format) -> fmt::Result {
152        Ok(())
153    }
154}
155
156/// A type-erased holder for language-specific items.
157///
158/// Carries formatting and coercion functions like [LangItem][LangItem::format]
159/// to allow language specific processing to work.
160pub trait LangItem<L>
161where
162    L: Lang,
163{
164    /// Format the language item appropriately.
165    fn format(
166        &self,
167        fmt: &mut fmt::Formatter<'_>,
168        config: &L::Config,
169        format: &L::Format,
170    ) -> fmt::Result;
171}
172
173/// Escape the given string according to a C-family escape sequence.
174///
175/// See <https://en.wikipedia.org/wiki/Escape_sequences_in_C>.
176///
177/// This is one of the more common escape sequences and is provided here so you
178/// can use it if a language you've implemented requires it.
179pub fn c_family_write_quoted(out: &mut fmt::Formatter, input: &str) -> fmt::Result {
180    for c in input.chars() {
181        match c {
182            // alert (bell)
183            '\u{0007}' => out.write_str("\\a")?,
184            // backspace
185            '\u{0008}' => out.write_str("\\b")?,
186            // form feed
187            '\u{0012}' => out.write_str("\\f")?,
188            // new line
189            '\n' => out.write_str("\\n")?,
190            // carriage return
191            '\r' => out.write_str("\\r")?,
192            // horizontal tab
193            '\t' => out.write_str("\\t")?,
194            // vertical tab
195            '\u{0011}' => out.write_str("\\v")?,
196            '\'' => out.write_str("\\'")?,
197            '"' => out.write_str("\\\"")?,
198            '\\' => out.write_str("\\\\")?,
199            ' ' => out.write_char(' ')?,
200            c if c.is_ascii() => {
201                if !c.is_control() {
202                    out.write_char(c)?
203                } else {
204                    write!(out, "\\x{:02x}", c as u32)?;
205                }
206            }
207            c if (c as u32) < 0x10000 => {
208                write!(out, "\\u{:04x}", c as u32)?;
209            }
210            c => {
211                write!(out, "\\U{:08x}", c as u32)?;
212            }
213        };
214    }
215
216    Ok(())
217}