Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pretty-print output code #25

Merged
merged 3 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 81 additions & 31 deletions src/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::type_expr::{
TypeArray, TypeDefinition, TypeExpr, TypeInfo, TypeIntersection, TypeName,
TypeObject, TypeString, TypeTuple, TypeUnion,
};
use std::io;
use std::{borrow::Cow, io};

/// A Rust type that has a corresponding TypeScript type definition.
///
Expand Down Expand Up @@ -55,9 +55,12 @@ use std::io;
/// r#"// AUTO-GENERATED by typescript-type-def
///
/// export default types;
/// export namespace types{
/// export type Uuid=string;
/// export type User={"id":types.Uuid;"name":string;};
/// export namespace types {
/// export type Uuid = string;
/// export type User = {
/// "id": types.Uuid;
/// "name": string;
/// };
/// }
/// "#
/// );
Expand Down Expand Up @@ -87,8 +90,11 @@ use std::io;
/// r#"// AUTO-GENERATED by typescript-type-def
///
/// export default types;
/// export namespace types{
/// export type User={"id":string;"name":string;};
/// export namespace types {
/// export type User = {
/// "id": string;
/// "name": string;
/// };
/// }
/// "#
/// );
Expand Down Expand Up @@ -146,9 +152,35 @@ pub trait TypeDef: 'static {
pub(crate) struct EmitCtx<'ctx> {
w: &'ctx mut dyn io::Write,
root_namespace: Option<&'ctx str>,
indent: usize,
stats: Stats,
}

impl EmitCtx<'_> {
fn indent(&mut self) {
self.indent += 1;
}

fn deindent(&mut self) {
debug_assert!(
self.indent > 0,
"indentation must be > 0 when deindenting"
);
self.indent -= 1;
}

fn current_indentation(&self) -> Cow<'static, str> {
// hard-code common values to avoid frequent string construction
match self.indent {
0 => "".into(),
1 => " ".into(),
2 => " ".into(),
3 => " ".into(),
n => " ".repeat(n).into(),
}
}
}

pub(crate) trait Emit {
fn emit(&self, ctx: &mut EmitCtx<'_>) -> io::Result<()>;
}
Expand Down Expand Up @@ -208,6 +240,7 @@ impl<'ctx> EmitCtx<'ctx> {
Self {
w,
root_namespace,
indent: 0,
stats,
}
}
Expand Down Expand Up @@ -243,7 +276,7 @@ where
let Self(args) = self;
if !args.is_empty() {
write!(ctx.w, "<")?;
SepList(args, ",").emit(ctx)?;
SepList(args, ", ").emit(ctx)?;
write!(ctx.w, ">")?;
}
Ok(())
Expand Down Expand Up @@ -298,7 +331,7 @@ impl Emit for TypeTuple {
let Self { docs, elements } = self;
docs.emit(ctx)?;
write!(ctx.w, "[")?;
SepList(elements, ",").emit(ctx)?;
SepList(elements, ", ").emit(ctx)?;
write!(ctx.w, "]")?;
Ok(())
}
Expand All @@ -311,15 +344,20 @@ impl Emit for TypeObject {
index_signature,
fields,
} = self;
docs.emit(ctx)?;
write!(ctx.w, "{{")?;
if let Some(docs) = docs {
docs.emit(ctx)?;
write!(ctx.w, "{}", ctx.current_indentation())?;
}
writeln!(ctx.w, "{{")?;
ctx.indent();
if let Some(IndexSignature { docs, name, value }) = index_signature {
docs.emit(ctx)?;
write!(ctx.w, "[")?;
write!(ctx.w, "{}[", ctx.current_indentation())?;
name.emit(ctx)?;
write!(ctx.w, ":string]:")?;
value.emit(ctx)?;
write!(ctx.w, ";")?;
writeln!(ctx.w)?;
}
for ObjectField {
docs,
Expand All @@ -329,15 +367,17 @@ impl Emit for TypeObject {
} in *fields
{
docs.emit(ctx)?;
write!(ctx.w, "{}", ctx.current_indentation())?;
name.emit(ctx)?;
if *optional {
write!(ctx.w, "?")?;
}
write!(ctx.w, ":")?;
write!(ctx.w, ": ")?;
r#type.emit(ctx)?;
write!(ctx.w, ";")?;
writeln!(ctx.w, ";")?;
}
write!(ctx.w, "}}")?;
ctx.deindent();
write!(ctx.w, "{}}}", ctx.current_indentation())?;
Ok(())
}
}
Expand All @@ -361,7 +401,7 @@ impl Emit for TypeUnion {
write!(ctx.w, "never")?;
} else {
write!(ctx.w, "(")?;
SepList(members, "|").emit(ctx)?;
SepList(members, " | ").emit(ctx)?;
write!(ctx.w, ")")?;
}
Ok(())
Expand All @@ -376,7 +416,7 @@ impl Emit for TypeIntersection {
write!(ctx.w, "unknown")?;
} else {
write!(ctx.w, "(")?;
SepList(members, "&").emit(ctx)?;
SepList(members, " & ").emit(ctx)?;
write!(ctx.w, ")")?;
}
Ok(())
Expand All @@ -395,11 +435,11 @@ impl Emit for Docs {
fn emit(&self, ctx: &mut EmitCtx<'_>) -> io::Result<()> {
let Self(docs) = self;
writeln!(ctx.w)?;
writeln!(ctx.w, "/**")?;
writeln!(ctx.w, "{}/**", ctx.current_indentation())?;
for line in docs.lines() {
writeln!(ctx.w, " * {}", line)?;
writeln!(ctx.w, "{} * {}", ctx.current_indentation(), line)?;
}
writeln!(ctx.w, " */")?;
writeln!(ctx.w, "{} */", ctx.current_indentation())?;
Ok(())
}
}
Expand Down Expand Up @@ -438,19 +478,26 @@ impl EmitCtx<'_> {
{
self.stats.type_definitions += 1;
if !path.is_empty() {
write!(self.w, "export namespace ")?;
write!(
self.w,
"{}export namespace ",
self.current_indentation()
)?;
SepList(path, ".").emit(self)?;
writeln!(self.w, "{{")?;
writeln!(self.w, " {{")?;
self.indent();
}
docs.emit(self)?;
write!(self.w, "export type ")?;
write!(self.w, "{}export type ", self.current_indentation())?;
name.emit(self)?;
Generics(generic_vars).emit(self)?;
write!(self.w, "=")?;
write!(self.w, " = ")?;
def.emit(self)?;
write!(self.w, ";")?;
if !path.is_empty() {
write!(self.w, "}}")?;
writeln!(self.w)?;
self.deindent();
write!(self.w, "{}}}", self.current_indentation())?;
}
writeln!(self.w)?;
}
Expand Down Expand Up @@ -560,20 +607,22 @@ pub fn write_definition_file_from_type_infos<W>(
where
W: io::Write,
{
let mut ctx = EmitCtx::new(&mut writer, options.root_namespace);
if let Some(header) = options.header {
writeln!(&mut writer, "{}", header)?;
writeln!(&mut ctx.w, "{}", header)?;
}
if let Some(root_namespace) = options.root_namespace {
writeln!(&mut writer, "export default {};", root_namespace)?;
writeln!(&mut writer, "export namespace {}{{", root_namespace)?;
writeln!(&mut ctx.w, "export default {};", root_namespace)?;
writeln!(&mut ctx.w, "export namespace {} {{", root_namespace)?;
ctx.indent();
}
let mut ctx = EmitCtx::new(&mut writer, options.root_namespace);
ctx.emit_type_def(type_infos)?;
let stats = ctx.stats;
if options.root_namespace.is_some() {
writeln!(&mut writer, "}}")?;
ctx.deindent();
writeln!(&mut ctx.w, "}}")?;
}
Ok(stats)
debug_assert_eq!(ctx.indent, 0, "indentation must be 0 after printing");
Ok(ctx.stats)
}

impl TypeInfo {
Expand Down Expand Up @@ -625,6 +674,7 @@ impl TypeInfo {
{
let mut ctx = EmitCtx::new(&mut writer, root_namespace);
ctx.emit_type_ref(self)?;
debug_assert_eq!(ctx.indent, 0, "indentation must be 0 after printing");
Ok(())
}
}
45 changes: 32 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@
//! r#"// AUTO-GENERATED by typescript-type-def
//!
//! export default types;
//! export namespace types{
//! export type Usize=number;
//! export type Foo={"a":types.Usize;"b":string;};
//! export namespace types {
//! export type Usize = number;
//! export type Foo = {
//! "a": types.Usize;
//! "b": string;
//! };
//! }
//! "#
//! );
Expand Down Expand Up @@ -116,11 +119,19 @@
//! r#"// AUTO-GENERATED by typescript-type-def
//!
//! export default types;
//! export namespace types{
//! export type Foo={"a":string;};
//! export type Bar={"a":string;};
//! export type Qux={"a":string;};
//! export type Baz={"a":types.Qux;};
//! export namespace types {
//! export type Foo = {
//! "a": string;
//! };
//! export type Bar = {
//! "a": string;
//! };
//! export type Qux = {
//! "a": string;
//! };
//! export type Baz = {
//! "a": types.Qux;
//! };
//! }
//! "#
//! );
Expand Down Expand Up @@ -179,11 +190,19 @@
//! r#"// AUTO-GENERATED by typescript-type-def
//!
//! export default types;
//! export namespace types{
//! export type Foo={"a":string;};
//! export type Bar={"a":string;};
//! export type Qux={"a":string;};
//! export type Baz={"a":types.Qux;};
//! export namespace types {
//! export type Foo = {
//! "a": string;
//! };
//! export type Bar = {
//! "a": string;
//! };
//! export type Qux = {
//! "a": string;
//! };
//! export type Baz = {
//! "a": types.Qux;
//! };
//! }
//! "#
//! );
Expand Down
Loading