Skip to content

Commit

Permalink
Merge #5
Browse files Browse the repository at this point in the history
5: Use `jq` for `open` r=qryxip a=qryxip



Co-authored-by: Ryo Yamashita <qryxip@gmail.com>
  • Loading branch information
bors[bot] and qryxip authored Aug 4, 2020
2 parents 86e3263 + 08acfc1 commit e4440bb
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 35 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Splitted the `download` command into `new`, `open`, and `download`. ([#3](https://github.com/qryxip/cargo-compete/pull/3))
- Changed the format of `workspace-metadata.toml`.
- Now `cargo-compete.test-suite`s are [Liquid](https://shopify.github.io/liquid/) templates. ([#4](https://github.com/qryxip/cargo-compete/pull/4))
- Now `cargo-compete.open`s are [jq](https://github.com/stedolan/jq) commands. ([#5](https://github.com/qryxip/cargo-compete/pull/5))

### Fixed

Expand Down
2 changes: 1 addition & 1 deletion README-ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ AtCoderを選択に入れる場合、

## 設定

**v0.2.0で`workspace-metadata.toml`のフォーマットを変更する予定です。** ([#4](https://github.com/qryxip/cargo-compete/pull/4))
**v0.2.0で`workspace-metadata.toml`のフォーマットを変更する予定です。** ([#4](https://github.com/qryxip/cargo-compete/pull/4), [#5](https://github.com/qryxip/cargo-compete/pull/5))

設定は各ワークスペース下にある`workspace-metadata.toml`にあります。
バイナリ提出関連の設定もこちらです。
Expand Down
11 changes: 9 additions & 2 deletions src/commands/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,16 @@ fn write_workspace_metadata_toml(
) -> anyhow::Result<()> {
let content = format!(
r#"[cargo-compete]
new-workspace-member = "include" # "include", "focus"
# How to manage new workspace members ("include", "focus")
new-workspace-member = "include"
# Path to the test file (Liquid template)
test-suite = "./testcases/{{{{ contest }}}}/{{{{ problem | kebabcase }}}}.yml"
#open = "vscode" # "vscode", "emacsclient"
# Open files with the command (`jq` command)
#
# VSCode:
#open = '["code"] + (.paths | map([.src, .test_suite]) | flatten) + ["-a", .manifest_dir]'
# Emacs:
#open = '["emacsclient", "-n"] + (.paths | map([.src, .test_suite]) | flatten)'
[cargo-compete.template]
code = "./cargo-compete-template/src/main.rs"
Expand Down
58 changes: 39 additions & 19 deletions src/open.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use crate::{project::Open, shell::Shell};
use crate::shell::Shell;
use anyhow::{ensure, Context as _};
use serde_json::json;
use std::{borrow::Borrow, path::Path};
use url::Url;

pub(crate) fn open(
urls: &[impl Borrow<Url>],
open: Option<Open>,
open: Option<impl AsRef<str>>,
paths: &[(impl AsRef<Path>, impl AsRef<Path>)],
pkg_manifest_dir: &Path,
cwd: &Path,
Expand All @@ -17,26 +19,44 @@ pub(crate) fn open(
}

if let Some(open) = open {
let mut cmd = match open {
Open::Vscode => crate::process::with_which("code", cwd)?,
Open::Emacsclient => {
let mut cmd = crate::process::with_which("emacsclient", cwd)?;
cmd.arg("-n");
cmd
}
};

for (src_path, test_suite_path) in paths {
cmd.arg(src_path.as_ref());
cmd.arg(test_suite_path.as_ref());
fn ensure_utf8(path: &Path) -> anyhow::Result<&str> {
path.to_str()
.with_context(|| format!("must be UTF-8: {:?}", path.display()))
}

if open == Open::Vscode {
cmd.arg("-a");
cmd.arg(pkg_manifest_dir);
}
let input = json!({
"manifest_dir": ensure_utf8(pkg_manifest_dir)?,
"paths": paths
.iter()
.map(|(src_path, test_suite_path)| {
let src_path = ensure_utf8(src_path.as_ref())?;
let test_suite_path = ensure_utf8(test_suite_path.as_ref())?;
Ok(json!({
"src": src_path,
"test_suite": test_suite_path
}))
})
.collect::<anyhow::Result<Vec<_>>>()?
})
.to_string();

let jq = crate::process::which("jq", cwd).with_context(|| {
"`jq` not found. install `jq` from https://github.com/stedolan/jq/releases"
})?;

let output = crate::process::process(jq, &cwd)
.args(&["-c", open.as_ref()])
.pipe_input(Some(input))
.read_with_shell_status(shell)?;

let args = serde_json::from_str::<Vec<String>>(&output)
.with_context(|| "expected string array")?;

ensure!(!args.is_empty(), "empty command");

cmd.exec_with_shell_status(shell)?;
crate::process::with_which(&args[0], cwd)?
.args(&args[1..])
.exec_with_shell_status(shell)?;
}
Ok(())
}
50 changes: 45 additions & 5 deletions src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,17 @@ use std::{
env,
ffi::{OsStr, OsString},
fmt,
io::Write as _,
path::{Path, PathBuf},
process::Stdio,
};

#[derive(Debug)]
pub(crate) struct ProcessBuilder {
program: OsString,
args: Vec<OsString>,
cwd: PathBuf,
pipe_input: Option<Vec<u8>>,
}

impl ProcessBuilder {
Expand All @@ -26,12 +29,13 @@ impl ProcessBuilder {
self
}

pub(crate) fn exec(&self) -> anyhow::Result<()> {
let status = std::process::Command::new(&self.program)
.args(&self.args)
.current_dir(&self.cwd)
.status()?;
pub(crate) fn pipe_input(&mut self, pipe_input: Option<impl Into<Vec<u8>>>) -> &mut Self {
self.pipe_input = pipe_input.map(Into::into);
self
}

pub(crate) fn exec(&self) -> anyhow::Result<()> {
let status = self.spawn(Stdio::inherit())?.wait()?;
if !status.success() {
bail!("{} didn't exit successfully: {}", self, status);
}
Expand All @@ -42,6 +46,41 @@ impl ProcessBuilder {
shell.status("Running", self)?;
self.exec()
}

fn read(&self) -> anyhow::Result<String> {
let std::process::Output { status, stdout, .. } =
self.spawn(Stdio::piped())?.wait_with_output()?;
if !status.success() {
bail!("{} didn't exit successfully: {}", self, status);
}
String::from_utf8(stdout).with_context(|| "non UTF-8 output")
}

pub(crate) fn read_with_shell_status(&self, shell: &mut Shell) -> anyhow::Result<String> {
shell.status("Running", self)?;
self.read()
}

fn spawn(&self, stdout: Stdio) -> anyhow::Result<std::process::Child> {
let mut child = std::process::Command::new(&self.program)
.args(&self.args)
.current_dir(&self.cwd)
.stdin(if self.pipe_input.is_some() {
Stdio::piped()
} else {
Stdio::inherit()
})
.stdout(stdout)
.spawn()?;

if let (Some(mut stdin), Some(pipe_input)) = (child.stdin.take(), self.pipe_input.as_ref())
{
stdin.write_all(pipe_input)?;
stdin.flush()?;
}

Ok(child)
}
}

impl fmt::Display for ProcessBuilder {
Expand All @@ -64,6 +103,7 @@ pub(crate) fn process(program: impl AsRef<Path>, cwd: impl AsRef<Path>) -> Proce
program: program.as_ref().into(),
args: vec![],
cwd: cwd.as_ref().into(),
pipe_input: None,
}
}

Expand Down
9 changes: 1 addition & 8 deletions src/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub(crate) struct WorkspaceMetadataCargoCompete {
#[derivative(Debug = "ignore")]
#[serde(deserialize_with = "deserialize_liquid_template_with_custom_filter")]
pub(crate) test_suite: liquid::Template,
pub(crate) open: Option<Open>,
pub(crate) open: Option<String>,
pub(crate) template: WorkspaceMetadataCargoCompeteTemplate,
pub(crate) platform: WorkspaceMetadataCargoCompetePlatform,
}
Expand Down Expand Up @@ -75,13 +75,6 @@ fn liquid_template_with_custom_filter(text: &str) -> Result<liquid::Template, St
}
}

#[derive(Deserialize, Clone, Copy, PartialEq, Debug)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum Open {
Vscode,
Emacsclient,
}

#[derive(Deserialize, Clone, Copy, Debug)]
#[serde(rename_all = "kebab-case")]
pub(crate) enum NewWorkspaceMember {
Expand Down

0 comments on commit e4440bb

Please sign in to comment.