Browse Source

Automatically generate screenshots for all examples (#2379)

pull/2322/merge
Emil Ernerfeldt 2 years ago
committed by GitHub
parent
commit
48666e1d7a
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      Cargo.lock
  2. 7
      crates/eframe/Cargo.toml
  3. 6
      crates/eframe/src/lib.rs
  4. 41
      crates/eframe/src/native/run.rs
  5. 32
      crates/egui_glow/src/painter.rs
  6. 4
      examples/confirm_exit/Cargo.toml
  7. 4
      examples/confirm_exit/README.md
  8. BIN
      examples/confirm_exit/screenshot.png
  9. 5
      examples/confirm_exit/src/main.rs
  10. 4
      examples/custom_3d_glow/Cargo.toml
  11. 2
      examples/custom_3d_glow/README.md
  12. BIN
      examples/custom_3d_glow/screenshot.png
  13. 4
      examples/custom_3d_three-d/Cargo.toml
  14. 2
      examples/custom_3d_three-d/README.md
  15. BIN
      examples/custom_3d_three-d/screenshot.png
  16. 4
      examples/custom_font/Cargo.toml
  17. 4
      examples/custom_font/README.md
  18. BIN
      examples/custom_font/screenshot.png
  19. 5
      examples/custom_font/src/main.rs
  20. 4
      examples/custom_font_style/Cargo.toml
  21. 5
      examples/custom_font_style/README.md
  22. BIN
      examples/custom_font_style/custom_font_style.png
  23. BIN
      examples/custom_font_style/screenshot.png
  24. 4
      examples/custom_window_frame/Cargo.toml
  25. 4
      examples/custom_window_frame/README.md
  26. BIN
      examples/custom_window_frame/screenshot.png
  27. 1
      examples/custom_window_frame/src/main.rs
  28. 4
      examples/download_image/Cargo.toml
  29. 4
      examples/download_image/README.md
  30. BIN
      examples/download_image/screenshot.png
  31. 4
      examples/file_dialog/Cargo.toml
  32. 2
      examples/file_dialog/README.md
  33. BIN
      examples/file_dialog/screenshot.png
  34. 1
      examples/file_dialog/src/main.rs
  35. 4
      examples/hello_world/Cargo.toml
  36. 4
      examples/hello_world/README.md
  37. BIN
      examples/hello_world/screenshot.png
  38. 5
      examples/hello_world/src/main.rs
  39. 4
      examples/keyboard_events/Cargo.toml
  40. 2
      examples/keyboard_events/README.md
  41. BIN
      examples/keyboard_events/screenshot.png
  42. 4
      examples/puffin_profiler/Cargo.toml
  43. 2
      examples/puffin_profiler/README.md
  44. BIN
      examples/puffin_profiler/screenshot.png
  45. 4
      examples/retained_image/Cargo.toml
  46. 4
      examples/retained_image/README.md
  47. BIN
      examples/retained_image/screenshot.png
  48. 2
      examples/retained_image/src/main.rs
  49. 4
      examples/screenshot/Cargo.toml
  50. 4
      examples/screenshot/README.md
  51. BIN
      examples/screenshot/screenshot.png
  52. 10
      examples/screenshot/src/main.rs
  53. 4
      examples/serial_windows/Cargo.toml
  54. 2
      examples/serial_windows/README.md
  55. BIN
      examples/serial_windows/screenshot.png
  56. 1
      examples/serial_windows/src/main.rs
  57. 4
      examples/svg/Cargo.toml
  58. 3
      examples/svg/README.md
  59. BIN
      examples/svg/screenshot.png
  60. 11
      sh/generate_example_screenshots.sh

1
Cargo.lock

@ -1147,6 +1147,7 @@ dependencies = [
"egui_glow",
"glow",
"glutin",
"image",
"js-sys",
"percent-encoding",
"puffin",

7
crates/eframe/Cargo.toml

@ -52,6 +52,10 @@ puffin = ["dep:puffin", "egui_glow?/puffin", "egui-wgpu?/puffin"]
## Enable screen reader support (requires `ctx.options().screen_reader = true;`)
screen_reader = ["egui-winit/screen_reader", "tts"]
## If set, eframe will look for the env-var `EFRAME_SCREENSHOT_TO` and write a screenshot to that location, and then quit.
## This is used to generate images for the examples.
__screenshot = ["dep:image"]
## Use [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/master/crates/egui-wgpu)).
## This overrides the `glow` feature.
wgpu = ["dep:wgpu", "dep:egui-wgpu"]
@ -89,6 +93,9 @@ directories-next = { version = "2", optional = true }
egui-wgpu = { version = "0.19.0", path = "../egui-wgpu", optional = true, features = [
"winit",
] }
image = { version = "0.24", optional = true, default-features = false, features = [
"png",
] }
puffin = { version = "0.14", optional = true }
wgpu = { version = "0.14", optional = true }

6
crates/eframe/src/lib.rs

@ -168,6 +168,12 @@ mod native;
pub fn run_native(app_name: &str, native_options: NativeOptions, app_creator: AppCreator) {
let renderer = native_options.renderer;
#[cfg(not(feature = "__screenshot"))]
assert!(
std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
"EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature"
);
match renderer {
#[cfg(feature = "glow")]
Renderer::Glow => {

41
crates/eframe/src/native/run.rs

@ -241,7 +241,7 @@ fn run_and_exit(event_loop: EventLoop<UserEvent>, mut winit_app: impl WinitApp +
})
}
fn centere_window_pos(
fn center_window_pos(
monitor: Option<winit::monitor::MonitorHandle>,
native_options: &mut epi::NativeOptions,
) {
@ -308,6 +308,8 @@ mod glow_integration {
// suspends and resumes.
app_creator: Option<epi::AppCreator>,
is_focused: bool,
frame_nr: u64,
}
impl GlowWinitApp {
@ -324,6 +326,7 @@ mod glow_integration {
running: None,
app_creator: Some(app_creator),
is_focused: true,
frame_nr: 0,
}
}
@ -517,6 +520,26 @@ mod glow_integration {
integration.post_present(window);
#[cfg(feature = "__screenshot")]
// give it time to settle:
if self.frame_nr == 2 {
if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") {
assert!(
path.ends_with(".png"),
"Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}"
);
let [w, h] = screen_size_in_pixels;
let pixels = painter.read_screen_rgba(screen_size_in_pixels);
let image = image::RgbaImage::from_vec(w, h, pixels).unwrap();
let image = image::imageops::flip_vertical(&image);
image.save(&path).unwrap_or_else(|err| {
panic!("Failed to save screenshot to {path:?}: {err}");
});
eprintln!("Screenshot saved to {path:?}.");
std::process::exit(0);
}
}
let control_flow = if integration.should_close() {
EventResult::Exit
} else if repaint_after.is_zero() {
@ -546,6 +569,8 @@ mod glow_integration {
std::thread::sleep(std::time::Duration::from_millis(10));
}
self.frame_nr += 1;
control_flow
} else {
EventResult::Wait
@ -659,7 +684,7 @@ mod glow_integration {
if native_options.run_and_return {
with_event_loop(native_options, |event_loop, mut native_options| {
if native_options.centered {
centere_window_pos(event_loop.available_monitors().next(), &mut native_options);
center_window_pos(event_loop.available_monitors().next(), &mut native_options);
}
let glow_eframe =
@ -670,7 +695,7 @@ mod glow_integration {
let event_loop = create_event_loop_builder(&mut native_options).build();
if native_options.centered {
centere_window_pos(event_loop.available_monitors().next(), &mut native_options);
center_window_pos(event_loop.available_monitors().next(), &mut native_options);
}
let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator);
@ -718,6 +743,12 @@ mod wgpu_integration {
native_options: epi::NativeOptions,
app_creator: epi::AppCreator,
) -> Self {
#[cfg(feature = "__screenshot")]
assert!(
std::env::var("EFRAME_SCREENSHOT_TO").is_err(),
"EFRAME_SCREENSHOT_TO not yet implemented for wgpu backend"
);
Self {
repaint_proxy: Arc::new(std::sync::Mutex::new(event_loop.create_proxy())),
app_name: app_name.to_owned(),
@ -1050,7 +1081,7 @@ mod wgpu_integration {
if native_options.run_and_return {
with_event_loop(native_options, |event_loop, mut native_options| {
if native_options.centered {
centere_window_pos(event_loop.available_monitors().next(), &mut native_options);
center_window_pos(event_loop.available_monitors().next(), &mut native_options);
}
let wgpu_eframe =
@ -1061,7 +1092,7 @@ mod wgpu_integration {
let event_loop = create_event_loop_builder(&mut native_options).build();
if native_options.centered {
centere_window_pos(event_loop.available_monitors().next(), &mut native_options);
center_window_pos(event_loop.available_monitors().next(), &mut native_options);
}
let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator);

32
crates/egui_glow/src/painter.rs

@ -605,6 +605,38 @@ impl Painter {
}
}
pub fn read_screen_rgba(&self, [w, h]: [u32; 2]) -> Vec<u8> {
let mut pixels = vec![0_u8; (w * h * 4) as usize];
unsafe {
self.gl.read_pixels(
0,
0,
w as _,
h as _,
glow::RGBA,
glow::UNSIGNED_BYTE,
glow::PixelPackData::Slice(&mut pixels),
);
}
pixels
}
pub fn read_screen_rgb(&self, [w, h]: [u32; 2]) -> Vec<u8> {
let mut pixels = vec![0_u8; (w * h * 3) as usize];
unsafe {
self.gl.read_pixels(
0,
0,
w as _,
h as _,
glow::RGB,
glow::UNSIGNED_BYTE,
glow::PixelPackData::Slice(&mut pixels),
);
}
pixels
}
unsafe fn destroy_gl(&self) {
self.gl.delete_program(self.program);
for tex in self.textures.values() {

4
examples/confirm_exit/Cargo.toml

@ -9,4 +9,6 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }

4
examples/confirm_exit/README.md

@ -1,3 +1,7 @@
Example how to show a confirm dialog before exiting an application.
```sh
cargo run -p confirm_exit
```
![](screenshot.png)

BIN
examples/confirm_exit/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

5
examples/confirm_exit/src/main.rs

@ -3,7 +3,10 @@
use eframe::egui;
fn main() {
let options = eframe::NativeOptions::default();
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};
eframe::run_native(
"Confirm exit",
options,

4
examples/custom_3d_glow/Cargo.toml

@ -9,6 +9,8 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe", features = ["glow"] }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
egui_glow = { path = "../../crates/egui_glow" }
glow = "0.11"

2
examples/custom_3d_glow/README.md

@ -13,3 +13,5 @@ If you are content of having egui sit on top of a 3D background, take a look at:
```sh
cargo run -p custom_3d_glow
```
![](screenshot.png)

BIN
examples/custom_3d_glow/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

4
examples/custom_3d_three-d/Cargo.toml

@ -11,7 +11,9 @@ publish = false
crate-type = ["cdylib", "rlib"]
[dependencies]
eframe = { path = "../../crates/eframe", features = ["glow"] }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
egui_glow = { path = "../../crates/egui_glow" }
glow = "0.11"
three-d = { version = "0.13", default-features = false }

2
examples/custom_3d_three-d/README.md

@ -18,3 +18,5 @@ cargo run -p custom_3d_three-d
```
wasm-pack build examples/custom_3d_three-d --target web
```
![](screenshot.png)

BIN
examples/custom_3d_three-d/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

4
examples/custom_font/Cargo.toml

@ -9,4 +9,6 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }

4
examples/custom_font/README.md

@ -1,3 +1,7 @@
Example of how to use custom fonts.
```sh
cargo run -p custom_font
```
![](screenshot.png)

BIN
examples/custom_font/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

5
examples/custom_font/src/main.rs

@ -3,7 +3,10 @@
use eframe::egui;
fn main() {
let options = eframe::NativeOptions::default();
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};
eframe::run_native(
"egui example: custom font",
options,

4
examples/custom_font_style/Cargo.toml

@ -9,4 +9,6 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }

5
examples/custom_font_style/README.md

@ -1,4 +1,7 @@
Example how to define custom test styles.
```sh
cargo run -p custom_font_style
```
![custom_font_style example](custom_font_style.png)
![](screenshot.png)

BIN
examples/custom_font_style/custom_font_style.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

BIN
examples/custom_font_style/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

4
examples/custom_window_frame/Cargo.toml

@ -9,4 +9,6 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }

4
examples/custom_window_frame/README.md

@ -1,3 +1,7 @@
Example how to show a custom window frame instead of the default OS window chrome decorations.
```sh
cargo run -p custom_window_frame
```
![](screenshot.png)

BIN
examples/custom_window_frame/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

1
examples/custom_window_frame/src/main.rs

@ -11,6 +11,7 @@ fn main() {
// To have rounded corners we need transparency:
transparent: true,
min_window_size: Some(egui::vec2(320.0, 100.0)),
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};
eframe::run_native(

4
examples/download_image/Cargo.toml

@ -9,7 +9,9 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
egui_extras = { path = "../../crates/egui_extras", features = ["image"] }
ehttp = "0.2"
image = { version = "0.24", default-features = false, features = ["jpeg"] }

4
examples/download_image/README.md

@ -1,3 +1,7 @@
Example how to download and show an image with eframe/egui.
```sh
cargo run -p download_image
```
![](screenshot.png)

BIN
examples/download_image/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

4
examples/file_dialog/Cargo.toml

@ -9,5 +9,7 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
rfd = "0.10"

2
examples/file_dialog/README.md

@ -3,3 +3,5 @@ How to show a file dialog using [`rfd`](https://github.com/PolyMeilex/rfd).
```sh
cargo run -p file_dialog
```
![](screenshot.png)

BIN
examples/file_dialog/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

1
examples/file_dialog/src/main.rs

@ -5,6 +5,7 @@ use eframe::egui;
fn main() {
let options = eframe::NativeOptions {
drag_and_drop_support: true,
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};
eframe::run_native(

4
examples/hello_world/Cargo.toml

@ -9,5 +9,7 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
tracing-subscriber = "0.3"

4
examples/hello_world/README.md

@ -1,3 +1,7 @@
Example showing some UI controls like `Label`, `TextEdit`, `Slider`, `Button`.
```sh
cargo run -p hello_world
```
![](screenshot.png)

BIN
examples/hello_world/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

5
examples/hello_world/src/main.rs

@ -6,7 +6,10 @@ fn main() {
// Log to stdout (if you run with `RUST_LOG=debug`).
tracing_subscriber::fmt::init();
let options = eframe::NativeOptions::default();
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};
eframe::run_native(
"My egui App",
options,

4
examples/keyboard_events/Cargo.toml

@ -9,5 +9,7 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
tracing-subscriber = "0.3"

2
examples/keyboard_events/README.md

@ -1,3 +1,5 @@
```sh
cargo run -p hello_world
```
![](screenshot.png)

BIN
examples/keyboard_events/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

4
examples/puffin_profiler/Cargo.toml

@ -9,6 +9,8 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe", features = ["puffin"] }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
puffin = "0.14"
puffin_http = "0.11"

2
examples/puffin_profiler/README.md

@ -7,3 +7,5 @@ cargo run -p puffin_profiler &
cargo install puffin_viewer
puffin_viewer --url 127.0.0.1:8585
```
![](screenshot.png)

BIN
examples/puffin_profiler/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

4
examples/retained_image/Cargo.toml

@ -9,6 +9,8 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
egui_extras = { path = "../../crates/egui_extras", features = ["image"] }
image = { version = "0.24", default-features = false, features = ["png"] }

4
examples/retained_image/README.md

@ -1,3 +1,7 @@
Example how to show an image with eframe/egui.
```sh
cargo run -p retained_image
```
![](screenshot.png)

BIN
examples/retained_image/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

2
examples/retained_image/src/main.rs

@ -5,7 +5,7 @@ use egui_extras::RetainedImage;
fn main() {
let options = eframe::NativeOptions {
initial_window_size: Some(egui::vec2(500.0, 900.0)),
initial_window_size: Some(egui::vec2(300.0, 900.0)),
..Default::default()
};

4
examples/screenshot/Cargo.toml

@ -9,6 +9,8 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
egui_extras = { path = "../../crates/egui_extras", features = ["image"] }
itertools = "0.10.3"

4
examples/screenshot/README.md

@ -1,3 +1,7 @@
Example how to take screenshots and display them with eframe/egui.
```sh
cargo run -p screenshot
```
![](screenshot.png)

BIN
examples/screenshot/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

10
examples/screenshot/src/main.rs

@ -74,23 +74,25 @@ impl eframe::App for MyApp {
self.take_screenshot = false;
if let Some(gl) = frame.gl() {
let mut buf = vec![0u8; screen_size_px[0] as usize * screen_size_px[1] as usize * 4];
let [w, h] = screen_size_px;
let mut buf = vec![0u8; w as usize * h as usize * 4];
let pixels = glow::PixelPackData::Slice(&mut buf[..]);
unsafe {
gl.read_pixels(
0,
0,
screen_size_px[0] as i32,
screen_size_px[1] as i32,
w as i32,
h as i32,
glow::RGBA,
glow::UNSIGNED_BYTE,
pixels,
);
}
// Flip vertically:
let mut rows: Vec<Vec<u8>> = buf
.into_iter()
.chunks(screen_size_px[0] as usize * 4)
.chunks(w as usize * 4)
.into_iter()
.map(|chunk| chunk.collect())
.collect();

4
examples/serial_windows/Cargo.toml

@ -9,4 +9,6 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }

2
examples/serial_windows/README.md

@ -6,3 +6,5 @@ See also <https://github.com/emilk/egui/issues/1918>.
```sh
cargo run -p serial_windows
```
![](screenshot.png)

BIN
examples/serial_windows/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

1
examples/serial_windows/src/main.rs

@ -9,6 +9,7 @@ fn main() {
let options = eframe::NativeOptions {
run_and_return: true,
initial_window_size: Some(egui::vec2(320.0, 240.0)),
..Default::default()
};

4
examples/svg/Cargo.toml

@ -9,5 +9,7 @@ publish = false
[dependencies]
eframe = { path = "../../crates/eframe" }
eframe = { path = "../../crates/eframe", features = [
"__screenshot", # __screenshot is so we can dump a ascreenshot using EFRAME_SCREENSHOT_TO
] }
egui_extras = { path = "../../crates/egui_extras", features = ["svg"] }

3
examples/svg/README.md

@ -1,6 +1,7 @@
Example how to show an SVG image.
```sh
cargo run -p svg
```
![](screenshot.png)

BIN
examples/svg/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

11
sh/generate_example_screenshots.sh

@ -0,0 +1,11 @@
#!/usr/bin/env bash
# This script generates screenshots for all the examples in examples/
set -eu
script_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
cd "$script_path/.."
cd examples
for VARIABLE in $(ls -1d */ | sed 's/\/$//'); do
EFRAME_SCREENSHOT_TO="$VARIABLE/screenshot.png" cargo run -p $VARIABLE
done
Loading…
Cancel
Save