Skip to content

Commit 26844a3

Browse files
committed
feat(toml): Allow adding/removing from cargo scripts
1 parent 89d1faf commit 26844a3

File tree

17 files changed

+139
-119
lines changed

17 files changed

+139
-119
lines changed

src/cargo/util/toml_mut/dependency.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,6 +1252,8 @@ mod tests {
12521252
let mut local = LocalManifest {
12531253
path: crate_root.clone(),
12541254
manifest,
1255+
embedded: None,
1256+
raw: toml.to_owned(),
12551257
};
12561258
assert_eq!(local.manifest.to_string(), toml);
12571259
let gctx = GlobalContext::default().unwrap();

src/cargo/util/toml_mut/manifest.rs

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use crate::core::dependency::DepKind;
1111
use crate::core::{FeatureValue, Features, Workspace};
1212
use crate::util::closest;
1313
use crate::util::interning::InternedString;
14+
use crate::util::toml::{is_embedded, ScriptSource};
1415
use crate::{CargoResult, GlobalContext};
1516

1617
/// Dependency table to add deps to.
@@ -245,6 +246,10 @@ pub struct LocalManifest {
245246
pub path: PathBuf,
246247
/// Manifest contents.
247248
pub manifest: Manifest,
249+
/// The raw, unparsed package file
250+
pub raw: String,
251+
/// Edit location for an embedded manifest, if relevant
252+
pub embedded: Option<Embedded>,
248253
}
249254

250255
impl Deref for LocalManifest {
@@ -267,18 +272,56 @@ impl LocalManifest {
267272
if !path.is_absolute() {
268273
anyhow::bail!("can only edit absolute paths, got {}", path.display());
269274
}
270-
let data = cargo_util::paths::read(&path)?;
275+
let raw = cargo_util::paths::read(&path)?;
276+
let mut data = raw.clone();
277+
let mut embedded = None;
278+
if is_embedded(path) {
279+
let source = ScriptSource::parse(&data)?;
280+
if let Some(frontmatter) = source.frontmatter() {
281+
embedded = Some(Embedded::exists(&data, frontmatter));
282+
data = frontmatter.to_owned();
283+
} else if let Some(shebang) = source.shebang() {
284+
embedded = Some(Embedded::after(&data, shebang));
285+
data = String::new();
286+
} else {
287+
embedded = Some(Embedded::start());
288+
data = String::new();
289+
}
290+
}
271291
let manifest = data.parse().context("Unable to parse Cargo.toml")?;
272292
Ok(LocalManifest {
273293
manifest,
274294
path: path.to_owned(),
295+
raw,
296+
embedded,
275297
})
276298
}
277299

278300
/// Write changes back to the file.
279301
pub fn write(&self) -> CargoResult<()> {
280-
let s = self.manifest.data.to_string();
281-
let new_contents_bytes = s.as_bytes();
302+
let mut manifest = self.manifest.data.to_string();
303+
let raw = match self.embedded.as_ref() {
304+
Some(Embedded::Implicit(start)) => {
305+
if !manifest.ends_with("\n") {
306+
manifest.push_str("\n");
307+
}
308+
let fence = "---\n";
309+
let prefix = &self.raw[0..*start];
310+
let suffix = &self.raw[*start..];
311+
let empty_line = if prefix.is_empty() { "\n" } else { "" };
312+
format!("{prefix}{fence}{manifest}{fence}{empty_line}{suffix}")
313+
}
314+
Some(Embedded::Explicit(span)) => {
315+
if !manifest.ends_with("\n") {
316+
manifest.push_str("\n");
317+
}
318+
let prefix = &self.raw[0..span.start];
319+
let suffix = &self.raw[span.end..];
320+
format!("{prefix}{manifest}{suffix}")
321+
}
322+
None => manifest,
323+
};
324+
let new_contents_bytes = raw.as_bytes();
282325

283326
cargo_util::paths::write_atomic(&self.path, new_contents_bytes)
284327
}
@@ -531,6 +574,51 @@ impl std::fmt::Display for LocalManifest {
531574
}
532575
}
533576

577+
/// Edit location for an embedded manifest
578+
#[derive(Clone, Debug)]
579+
pub enum Embedded {
580+
/// Manifest is implicit
581+
///
582+
/// This is the insert location for a frontmatter
583+
Implicit(usize),
584+
/// Manifest is explicit in a frontmatter
585+
///
586+
/// This is the span of the frontmatter body
587+
Explicit(std::ops::Range<usize>),
588+
}
589+
590+
impl Embedded {
591+
fn start() -> Self {
592+
Self::Implicit(0)
593+
}
594+
595+
fn after(input: &str, after: &str) -> Self {
596+
let span = substr_span(input, after);
597+
let end = span.end;
598+
Self::Implicit(end)
599+
}
600+
601+
fn exists(input: &str, exists: &str) -> Self {
602+
let span = substr_span(input, exists);
603+
Self::Explicit(span)
604+
}
605+
}
606+
607+
fn substr_span(haystack: &str, needle: &str) -> std::ops::Range<usize> {
608+
let haystack_start_ptr = haystack.as_ptr();
609+
let haystack_end_ptr = haystack[haystack.len()..haystack.len()].as_ptr();
610+
611+
let needle_start_ptr = needle.as_ptr();
612+
let needle_end_ptr = needle[needle.len()..needle.len()].as_ptr();
613+
614+
assert!(needle_end_ptr < haystack_end_ptr);
615+
assert!(haystack_start_ptr <= needle_start_ptr);
616+
let start = needle_start_ptr as usize - haystack_start_ptr as usize;
617+
let end = start + needle.len();
618+
619+
start..end
620+
}
621+
534622
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
535623
enum DependencyStatus {
536624
None,

tests/testsuite/cargo_add/script_bare/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn case() {
3131
.arg_line("--manifest-path cargo-test-fixture.rs my-package")
3232
.current_dir(cwd)
3333
.assert()
34-
.failure()
34+
.success()
3535
.stdout_eq(str![""])
3636
.stderr_eq(file!["stderr.term.svg"]);
3737

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
1+
---
2+
[dependencies]
3+
my-package = "99999.0.0"
4+
---
5+
16
fn main() {}

tests/testsuite/cargo_add/script_bare/stderr.term.svg

Lines changed: 6 additions & 20 deletions
Loading

tests/testsuite/cargo_add/script_frontmatter/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn case() {
3131
.arg_line("--manifest-path cargo-test-fixture.rs my-package")
3232
.current_dir(cwd)
3333
.assert()
34-
.failure()
34+
.success()
3535
.stdout_eq(str![""])
3636
.stderr_eq(file!["stderr.term.svg"]);
3737

tests/testsuite/cargo_add/script_frontmatter/out/cargo-test-fixture.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
---
22
[package]
33
edition = "2015"
4+
5+
[dependencies]
6+
my-package = "99999.0.0"
47
---
58

69
fn main() {

tests/testsuite/cargo_add/script_frontmatter/stderr.term.svg

Lines changed: 6 additions & 22 deletions
Loading

tests/testsuite/cargo_add/script_shebang/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ fn case() {
3131
.arg_line("--manifest-path cargo-test-fixture.rs my-package")
3232
.current_dir(cwd)
3333
.assert()
34-
.failure()
34+
.success()
3535
.stdout_eq(str![""])
3636
.stderr_eq(file!["stderr.term.svg"]);
3737

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
#!/usr/bin/env cargo
2+
---
3+
[dependencies]
4+
my-package = "99999.0.0"
5+
---
26

37
fn main() {}

tests/testsuite/cargo_add/script_shebang/stderr.term.svg

Lines changed: 6 additions & 20 deletions
Loading

tests/testsuite/cargo_remove/script/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn case() {
3232
.arg_line("--manifest-path cargo-remove-test-fixture.rs docopt")
3333
.current_dir(cwd)
3434
.assert()
35-
.failure()
35+
.success()
3636
.stdout_eq(str![""])
3737
.stderr_eq(file!["stderr.term.svg"]);
3838

tests/testsuite/cargo_remove/script/out/cargo-remove-test-fixture.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ edition = "2015"
55
semver = "0.1.0"
66

77
[dependencies]
8-
docopt = "0.6"
98
rustc-serialize = "0.4"
109
semver = "0.1"
1110
toml = "0.1"

tests/testsuite/cargo_remove/script/stderr.term.svg

Lines changed: 5 additions & 23 deletions
Loading

tests/testsuite/cargo_remove/script_last/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ fn case() {
2222
.arg_line("--manifest-path cargo-remove-test-fixture.rs docopt")
2323
.current_dir(cwd)
2424
.assert()
25-
.failure()
25+
.success()
2626
.stdout_eq(str![""])
2727
.stderr_eq(file!["stderr.term.svg"]);
2828

0 commit comments

Comments
 (0)