use core::fmt::Write as _;
use crate as genco;
use crate::fmt;
use crate::quote_in;
use crate::tokens::ItemStr;
use alloc::collections::{BTreeMap, BTreeSet};
use alloc::string::{String, ToString};
pub type Tokens = crate::Tokens<Kotlin>;
impl genco::lang::LangSupportsEval for Kotlin {}
impl_lang! {
pub Kotlin {
type Config = Config;
type Format = Format;
type Item = Import;
fn start_string_eval(
out: &mut fmt::Formatter<'_>,
_config: &Self::Config,
_format: &Self::Format,
) -> fmt::Result {
out.write_str("${")?;
Ok(())
}
fn end_string_eval(
out: &mut fmt::Formatter<'_>,
_config: &Self::Config,
_format: &Self::Format,
) -> fmt::Result {
out.write_char('}')?;
Ok(())
}
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")?,
'\'' => out.write_str("\\'")?,
'"' => out.write_str("\\\"")?,
'\\' => out.write_str("\\\\")?,
'$' => out.write_str("\\$")?,
c if c.is_ascii() && !c.is_control() => out.write_char(c)?,
c => {
for unit in c.encode_utf16(&mut [0u16; 2]) {
write!(out, "\\u{unit:04x}")?;
}
}
}
}
Ok(())
}
fn format_file(
tokens: &Tokens,
out: &mut fmt::Formatter<'_>,
config: &Self::Config,
) -> fmt::Result {
let mut header = Tokens::new();
let mut format = Format::default();
if let Some(ref package) = config.package {
quote_in!(header => package $package);
header.line();
}
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 current_package = Some(self.package.as_ref());
if file_package != current_package && imported != current_package {
out.write_str(self.package.as_ref())?;
out.write_str(".")?;
}
out.write_str(&self.name)?;
Ok(())
}
}
}
#[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 Kotlin {
fn imports(
out: &mut Tokens,
tokens: &Tokens,
config: &Config,
imported: &mut BTreeMap<String, String>,
) {
let mut to_import = BTreeSet::new();
let file_package = config.package.as_ref().map(|p| p.as_ref());
for import in tokens.iter_lang() {
if Some(import.package.as_ref()) == file_package {
continue;
}
if let Some(existing_package) = imported.get(import.name.as_ref()) {
if existing_package != import.package.as_ref() {
continue;
}
}
to_import.insert(import.clone());
}
if to_import.is_empty() {
return;
}
for import in to_import {
quote_in!(*out => import $(&import.package).$(&import.name));
out.push();
imported.insert(import.name.to_string(), import.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(),
}
}