Skip to content

Commit

Permalink
chore: add tailcall-prettier (#1731)
Browse files Browse the repository at this point in the history
Co-authored-by: Tushar Mathur <[email protected]>
  • Loading branch information
ssddOnTop and tusharmath authored Apr 16, 2024
1 parent 66eb1bc commit 69f22a6
Show file tree
Hide file tree
Showing 8 changed files with 159 additions and 1 deletion.
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ jobs:
- uses: actions/checkout@v4
- uses: taiki-e/install-action@cargo-llvm-cov

- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: "20.11.0"
- name: Install Prettier
run: npm i -g prettier

- name: Install Stable Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
Expand Down
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ tonic-types = "0.11.0"


[dev-dependencies]
tailcall-prettier = {path = "tailcall-prettier"}
criterion = "0.5.1"
httpmock = "0.7.0"
pretty_assertions = "1.4.0"
Expand Down Expand Up @@ -196,6 +197,7 @@ members = [
"tailcall-autogen",
"tailcall-aws-lambda",
"tailcall-cloudflare",
"tailcall-prettier",
"tailcall-query-plan",
]

Expand Down
12 changes: 12 additions & 0 deletions tailcall-prettier/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "tailcall-prettier"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
anyhow = "1.0.82"
lazy_static = "1.4.0"
strum_macros = "0.26.2"
tokio.workspace = true
26 changes: 26 additions & 0 deletions tailcall-prettier/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::sync::Arc;
mod parser;
mod prettier;
use anyhow::Result;
pub use parser::Parser;
use prettier::Prettier;

lazy_static::lazy_static! {
static ref PRETTIER: Arc<Prettier> = Arc::new(Prettier::new());
}

pub async fn format<T: AsRef<str>>(source: T, parser: Parser) -> Result<String> {
PRETTIER.format(source.as_ref().to_string(), parser).await
}

#[cfg(test)]
mod tests {
use crate::{format, Parser};

#[tokio::test]
async fn test_js() -> anyhow::Result<()> {
let prettier = format("const x={a:3};", Parser::Js).await?;
assert_eq!("const x = {a: 3}\n", prettier);
Ok(())
}
}
30 changes: 30 additions & 0 deletions tailcall-prettier/src/parser.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use anyhow::{anyhow, Result};

#[derive(strum_macros::Display)]
pub enum Parser {
Gql,
Yml,
Json,
Md,
Ts,
Js,
}

impl Parser {
pub fn detect(path: &str) -> Result<Self> {
let ext = path
.split('.')
.last()
.ok_or(anyhow!("No file extension found"))?
.to_lowercase();
match ext.as_str() {
"gql" | "graphql" => Ok(Parser::Gql),
"yml" | "yaml" => Ok(Parser::Yml),
"json" => Ok(Parser::Json),
"md" => Ok(Parser::Md),
"ts" => Ok(Parser::Ts),
"js" => Ok(Parser::Js),
_ => Err(anyhow!("Unsupported file type")),
}
}
}
54 changes: 54 additions & 0 deletions tailcall-prettier/src/prettier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use std::io::Write;
use std::process::{Command, Stdio};

use anyhow::{anyhow, Result};

pub use super::Parser;

pub struct Prettier {
runtime: tokio::runtime::Runtime,
}

impl Prettier {
pub fn new() -> Prettier {
let runtime = tokio::runtime::Builder::new_multi_thread()
.max_blocking_threads(1024)
.build()
.unwrap();

Self { runtime }
}

pub async fn format(&self, source: String, parser: Parser) -> Result<String> {
self.runtime
.spawn_blocking(move || {
let mut command = command();
let mut child = command
.arg("--stdin-filepath")
.arg(format!("file.{}", parser))
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()?;

if let Some(ref mut stdin) = child.stdin {
stdin.write_all(source.as_bytes())?;
}

let output = child.wait_with_output()?;
if output.status.success() {
Ok(String::from_utf8(output.stdout)?)
} else {
Err(anyhow!("Prettier formatting failed"))
}
})
.await?
}
}

fn command() -> Command {
if cfg!(target_os = "windows") {
Command::new("prettier.cmd")
} else {
Command::new("prettier")
}
}
18 changes: 17 additions & 1 deletion tests/execution_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,9 +889,25 @@ async fn assert_spec(spec: ExecutionSpec, opentelemetry: &InMemoryTelemetry) {
// \r is added automatically in windows, it's safe to replace it with \n
let content = content.replace("\r\n", "\n");

let path_str = spec.path.display().to_string();

let identity = tailcall_prettier::format(
identity,
tailcall_prettier::Parser::detect(path_str.as_str()).unwrap(),
)
.await
.unwrap();

let content = tailcall_prettier::format(
content,
tailcall_prettier::Parser::detect(path_str.as_str()).unwrap(),
)
.await
.unwrap();

pretty_assertions::assert_eq!(
identity,
content.as_ref(),
content,
"Identity check failed for {:#?}",
spec.path,
);
Expand Down

1 comment on commit 69f22a6

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running 30s test @ http://localhost:8000/graphql

4 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 7.27ms 3.30ms 102.83ms 71.74%
Req/Sec 3.48k 113.27 4.24k 87.17%

415183 requests in 30.02s, 2.08GB read

Requests/sec: 13832.17

Transfer/sec: 71.00MB

Please sign in to comment.