From 9b99260254b6e9a98949ba8022309ed97ff0a082 Mon Sep 17 00:00:00 2001 From: maciektr Date: Thu, 13 Jun 2024 13:05:47 +0200 Subject: [PATCH] Add option to emit expand result to stdout commit-id:bdf0cf3c --- scarb/src/bin/scarb/args.rs | 12 +++- scarb/src/bin/scarb/commands/expand.rs | 11 ++- scarb/src/bin/scarb/commands/fmt.rs | 12 +++- scarb/src/ops/expand.rs | 97 ++++++++++++++++++++++---- scarb/src/ops/fmt.rs | 9 ++- scarb/tests/expand.rs | 58 +++++++++++++++ 6 files changed, 177 insertions(+), 22 deletions(-) diff --git a/scarb/src/bin/scarb/args.rs b/scarb/src/bin/scarb/args.rs index 40dbe944e..ae44a8dfd 100644 --- a/scarb/src/bin/scarb/args.rs +++ b/scarb/src/bin/scarb/args.rs @@ -7,8 +7,7 @@ use std::ffi::OsString; use anyhow::Result; use camino::Utf8PathBuf; -use clap::{CommandFactory, Parser, Subcommand}; -use scarb::ops::EmitTarget; +use clap::{CommandFactory, Parser, Subcommand, ValueEnum}; use smol_str::SmolStr; use url::Url; @@ -203,6 +202,11 @@ pub enum Command { External(Vec), } +#[derive(ValueEnum, Clone, Debug)] +pub enum EmitTarget { + Stdout, +} + /// Arguments accepted by the `build` command. #[derive(Parser, Clone, Debug)] pub struct BuildArgs { @@ -239,6 +243,10 @@ pub struct ExpandArgs { /// Do not attempt formatting. #[arg(long, default_value_t = false)] pub ugly: bool, + + /// Emit the expanded file to stdout + #[arg(short, long)] + pub emit: Option, } /// Arguments accepted by the `run` command. diff --git a/scarb/src/bin/scarb/commands/expand.rs b/scarb/src/bin/scarb/commands/expand.rs index e6c018962..4a5c4baaf 100644 --- a/scarb/src/bin/scarb/commands/expand.rs +++ b/scarb/src/bin/scarb/commands/expand.rs @@ -1,7 +1,7 @@ use anyhow::Result; use smol_str::ToSmolStr; -use crate::args::ExpandArgs; +use crate::args::{EmitTarget, ExpandArgs}; use scarb::core::{Config, TargetKind}; use scarb::ops; use scarb::ops::ExpandOpts; @@ -15,6 +15,15 @@ pub fn run(args: ExpandArgs, config: &Config) -> Result<()> { ugly: args.ugly, target_name: args.target_name.map(|n| n.to_smolstr()), target_kind: args.target_kind.map(TargetKind::try_new).transpose()?, + emit: args.emit.map(|e| e.into()), }; ops::expand(package, opts, &ws) } + +impl From for ops::ExpandEmitTarget { + fn from(target: EmitTarget) -> Self { + match target { + EmitTarget::Stdout => ops::ExpandEmitTarget::Stdout, + } + } +} diff --git a/scarb/src/bin/scarb/commands/fmt.rs b/scarb/src/bin/scarb/commands/fmt.rs index 8f907c291..c06b9cb41 100644 --- a/scarb/src/bin/scarb/commands/fmt.rs +++ b/scarb/src/bin/scarb/commands/fmt.rs @@ -1,6 +1,6 @@ use anyhow::Result; -use crate::args::FmtArgs; +use crate::args::{EmitTarget, FmtArgs}; use crate::errors::error_with_exit_code; use scarb::core::Config; use scarb::ops::{self, FmtAction}; @@ -12,7 +12,7 @@ pub fn run(args: FmtArgs, config: &Config) -> Result<()> { let action = if args.check { FmtAction::Check } else if let Some(emit_target) = args.emit { - FmtAction::Emit(emit_target) + FmtAction::Emit(emit_target.into()) } else { // Format in place is the default option FmtAction::Fix @@ -37,3 +37,11 @@ pub fn run(args: FmtArgs, config: &Config) -> Result<()> { error_with_exit_code(1) } } + +impl From for ops::FmtEmitTarget { + fn from(target: EmitTarget) -> Self { + match target { + EmitTarget::Stdout => ops::FmtEmitTarget::Stdout, + } + } +} diff --git a/scarb/src/ops/expand.rs b/scarb/src/ops/expand.rs index 62281c60b..dad0c228e 100644 --- a/scarb/src/ops/expand.rs +++ b/scarb/src/ops/expand.rs @@ -1,7 +1,7 @@ use crate::compiler::db::{build_scarb_root_database, ScarbDatabase}; use crate::compiler::helpers::{build_compiler_config, write_string}; use crate::compiler::{CairoCompilationUnit, CompilationUnit, CompilationUnitAttributes}; -use crate::core::{Package, TargetKind, Workspace}; +use crate::core::{Package, PackageId, TargetKind, Workspace}; use crate::ops; use crate::ops::{get_test_package_ids, FeaturesOpts}; use anyhow::{bail, Context, Result}; @@ -16,8 +16,17 @@ use cairo_lang_parser::db::ParserGroup; use cairo_lang_syntax::node::helpers::UsePathEx; use cairo_lang_syntax::node::{ast, TypedStablePtr, TypedSyntaxNode}; use cairo_lang_utils::Upcast; +use scarb_ui::Message; +use serde::{Serialize, Serializer}; use smol_str::SmolStr; -use std::collections::HashSet; +use std::collections::{BTreeMap, HashSet}; + +#[derive(Debug, Clone, Default)] +pub enum ExpandEmitTarget { + Stdout, + #[default] + File, +} #[derive(Clone, Debug)] pub struct ExpandOpts { @@ -25,6 +34,7 @@ pub struct ExpandOpts { pub target_kind: Option, pub target_name: Option, pub ugly: bool, + pub emit: Option, } pub fn expand(package: Package, opts: ExpandOpts, ws: &Workspace<'_>) -> Result<()> { @@ -209,19 +219,82 @@ fn do_expand( format_cairo(content.clone()).unwrap_or(content) }; - let file_name = format!( - "{}.expanded.cairo", - compilation_unit - .main_component() - .first_target() - .name - .clone() - ); - let target_dir = compilation_unit.target_dir(ws); - write_string(file_name.as_str(), "output file", &target_dir, ws, content)?; + opts.emit + .unwrap_or_default() + .emit(content, compilation_unit, ws)?; + Ok(()) } +impl ExpandEmitTarget { + fn emit( + &self, + content: String, + compilation_unit: &CairoCompilationUnit, + ws: &Workspace<'_>, + ) -> Result<()> { + match self { + Self::Stdout => { + ws.config() + .ui() + .force_print(EmittedText::new(content, compilation_unit)); + } + Self::File => { + let file_name = format!( + "{}.expanded.cairo", + compilation_unit + .main_component() + .first_target() + .name + .clone() + ); + let target_dir = compilation_unit.target_dir(ws); + write_string(file_name.as_str(), "output file", &target_dir, ws, content)?; + } + } + Ok(()) + } +} + +struct EmittedText { + expanded: String, + package_id: PackageId, + target: String, +} + +impl EmittedText { + pub fn new(expanded: String, compilation_unit: &CairoCompilationUnit) -> Self { + Self { + expanded, + package_id: compilation_unit.main_package_id(), + target: compilation_unit + .main_component() + .first_target() + .name + .clone() + .to_string(), + } + } +} + +impl Message for EmittedText { + fn text(self) -> String { + self.expanded + } + + fn structured(self, ser: S) -> std::result::Result + where + Self: Sized, + { + BTreeMap::from_iter(vec![ + ("package_id".to_string(), self.package_id.to_string()), + ("target_name".to_string(), self.target), + ("expanded".to_string(), self.expanded), + ]) + .serialize(ser) + } +} + fn format_cairo(content: String) -> Option { let formatter = CairoFormatter::new(FormatterConfig::default()); let content = formatter.format_to_string(&content).ok()?; diff --git a/scarb/src/ops/fmt.rs b/scarb/src/ops/fmt.rs index 85085e5cf..f3123f541 100644 --- a/scarb/src/ops/fmt.rs +++ b/scarb/src/ops/fmt.rs @@ -6,7 +6,6 @@ use anyhow::Result; use cairo_lang_diagnostics::Severity; use cairo_lang_formatter::cairo_formatter::FormattingError; use cairo_lang_formatter::{CairoFormatter, FormatOutcome, FormatterConfig}; -use clap::ValueEnum; use ignore::WalkState::{Continue, Skip}; use ignore::{DirEntry, Error, ParallelVisitor, ParallelVisitorBuilder, WalkState}; use tracing::{info, warn}; @@ -15,8 +14,8 @@ use crate::core::workspace::Workspace; use crate::core::PackageId; use crate::internal::serdex::toml_merge; -#[derive(Debug, Clone, ValueEnum)] -pub enum EmitTarget { +#[derive(Debug, Clone)] +pub enum FmtEmitTarget { Stdout, } @@ -27,7 +26,7 @@ pub enum FmtAction { #[default] Fix, Check, - Emit(EmitTarget), + Emit(FmtEmitTarget), } #[derive(Debug)] @@ -159,7 +158,7 @@ pub trait Emittable { fn emit(&self, ws: &Workspace<'_>, path: &Path, formatted: &str); } -impl Emittable for EmitTarget { +impl Emittable for FmtEmitTarget { fn emit(&self, ws: &Workspace<'_>, path: &Path, formatted: &str) { match self { Self::Stdout => ws diff --git a/scarb/tests/expand.rs b/scarb/tests/expand.rs index 636be2137..3620bd356 100644 --- a/scarb/tests/expand.rs +++ b/scarb/tests/expand.rs @@ -40,6 +40,64 @@ fn expand_package_simple() { ); } +#[test] +fn can_expand_to_stdout() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .lib_cairo(indoc! {r#" + fn hello() -> felt252 { + 0 + + "#}) + .build(&t); + Scarb::quick_snapbox() + .arg("expand") + .arg("--emit=stdout") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + error: Missing token TerminalRBrace. + --> [..]lib.cairo:2:6 + 0 + ^ + + + mod hello { + fn hello() -> felt252 { + 0 + } + + "#}); + assert!(!t.child("target").exists()); +} + +#[test] +fn can_expand_to_stdout_json() { + let t = TempDir::new().unwrap(); + ProjectBuilder::start() + .name("hello") + .lib_cairo(indoc! {r#" + fn hello() -> felt252 { + 0 + + "#}) + .build(&t); + Scarb::quick_snapbox() + .arg("--json") + .arg("expand") + .arg("--emit=stdout") + .current_dir(&t) + .assert() + .success() + .stdout_matches(indoc! {r#" + {"type":"error","message":"Missing token TerminalRBrace./n --> [..]lib.cairo:2:6/n 0/n ^/n"} + {"expanded":"/nmod hello {/nfn hello() -> felt252 {/n 0/n}/n","package_id":"hello v1.0.0 ([..]Scarb.toml)","target_name":"hello"} + "#}); + assert!(!t.child("target").exists()); +} + #[test] fn expand_integration_test() { let t = TempDir::new().unwrap();