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

Use jq for open #5

Merged
merged 2 commits into from
Aug 4, 2020
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
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