mod block_comment;
pub use self::block_comment::BlockComment;
use core::fmt::Write as _;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::{String, ToString};
use crate as genco;
use crate::fmt;
use crate::tokens::ItemStr;
use crate::{quote, quote_in};
pub type Tokens = crate::Tokens<Java>;
impl_lang! {
pub Java {
type Config = Config;
type Format = Format;
type Item = Import;
fn write_quoted(out: &mut fmt::Formatter<'_>, input: &str) -> fmt::Result {
for c in input.chars() {
match c {
'\t' => out.write_str("\\t")?,
'\u{0008}' => out.write_str("\\b")?,
'\n' => out.write_str("\\n")?,
'\r' => out.write_str("\\r")?,
'\u{0014}' => out.write_str("\\f")?,
'\'' => out.write_str("\\'")?,
'"' => out.write_str("\\\"")?,
'\\' => out.write_str("\\\\")?,
' ' => out.write_char(' ')?,
c if c.is_ascii() && !c.is_control() => out.write_char(c)?,
c => {
for c in c.encode_utf16(&mut [0u16; 2]) {
write!(out, "\\u{c:04x}")?;
}
}
}
}
Ok(())
}
fn format_file(
tokens: &Tokens,
out: &mut fmt::Formatter<'_>,
config: &Self::Config,
) -> fmt::Result {
let mut header = Tokens::new();
if let Some(ref package) = config.package {
quote_in!(header => package $package;);
header.line();
}
let mut format = Format::default();
Self::imports(&mut header, tokens, config, &mut format.imported);
header.format(out, config, &format)?;
tokens.format(out, config, &format)?;
Ok(())
}
}
Import(Import) {
fn format(&self, out: &mut fmt::Formatter<'_>, config: &Config, format: &Format) -> fmt::Result {
let file_package = config.package.as_ref().map(|p| p.as_ref());
let imported = format.imported.get(self.name.as_ref()).map(String::as_str);
let pkg = Some(self.package.as_ref());
if &*self.package != JAVA_LANG && imported != pkg && file_package != pkg {
out.write_str(self.package.as_ref())?;
out.write_str(SEP)?;
}
out.write_str(&self.name)?;
Ok(())
}
}
}
const JAVA_LANG: &str = "java.lang";
const SEP: &str = ".";
#[derive(Debug, Default)]
pub struct Format {
imported: BTreeMap<String, String>,
}
#[derive(Debug, Default)]
pub struct Config {
package: Option<ItemStr>,
}
impl Config {
pub fn with_package<P>(self, package: P) -> Self
where
P: Into<ItemStr>,
{
Self {
package: Some(package.into()),
}
}
}
#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
pub struct Import {
package: ItemStr,
name: ItemStr,
}
impl Java {
fn imports(
out: &mut Tokens,
tokens: &Tokens,
config: &Config,
imported: &mut BTreeMap<String, String>,
) {
let mut modules = BTreeSet::new();
let file_package = config.package.as_ref().map(|p| p.as_ref());
for import in tokens.iter_lang() {
modules.insert((import.package.clone(), import.name.clone()));
}
if modules.is_empty() {
return;
}
for (package, name) in modules {
if imported.contains_key(&*name) {
continue;
}
if &*package == JAVA_LANG {
continue;
}
if Some(&*package) == file_package {
continue;
}
out.append(quote!(import $(package.clone())$(SEP)$(name.clone());));
out.push();
imported.insert(name.to_string(), package.to_string());
}
out.line();
}
}
pub fn import<P, N>(package: P, name: N) -> Import
where
P: Into<ItemStr>,
N: Into<ItemStr>,
{
Import {
package: package.into(),
name: name.into(),
}
}
pub fn block_comment<T>(comment: T) -> BlockComment<T>
where
T: IntoIterator,
T::Item: Into<ItemStr>,
{
BlockComment(comment)
}