example & help commands

This commit is contained in:
brevalferrari 2025-07-01 19:40:38 +02:00
parent 1293694b0e
commit 1c3b8069aa
Signed by: breval
GPG key ID: 913954DA013FAD4F
4 changed files with 70 additions and 25 deletions

7
Cargo.lock generated
View file

@ -41,7 +41,6 @@ version = "0.0.2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bliplib", "bliplib",
"constcat",
"derive-new", "derive-new",
"serde", "serde",
"serde_json", "serde_json",
@ -110,12 +109,6 @@ dependencies = [
"windows-link", "windows-link",
] ]
[[package]]
name = "constcat"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "136d3e02915a2cea4d74caa8681e2d44b1c3254bdbf17d11d41d587ff858832c"
[[package]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.7" version = "0.8.7"

View file

@ -13,14 +13,14 @@ name = "bliplab"
[dependencies] [dependencies]
anyhow = { version = "1", optional = true } anyhow = { version = "1", optional = true }
bliplib = { version = "0.2.5", default-features = false } bliplib = { version = "0.2.5", default-features = false }
constcat = { version = "0.6.1", optional = true }
derive-new = "0.7.0" derive-new = "0.7.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", optional = true } serde_json = { version = "1.0", optional = true }
serde_with = "3.13" serde_with = "3.13"
[features] [features]
bin = ["serde_json", "anyhow", "constcat"] bin = ["serde_json", "anyhow"]
example-skip-defaults = []
[[bin]] [[bin]]
name = "bliplab" name = "bliplab"

View file

@ -3,26 +3,62 @@
use std::{env::args, fs::File}; use std::{env::args, fs::File};
use anyhow::Context; use anyhow::Context;
use bliplab::Song; use bliplab::{Channel, PathOrString, Song, VariableChange};
use constcat::concat;
const TARGET: &str = "BLIP lab file"; const TARGET: &str = "BLIP lab file";
fn main() -> anyhow::Result<()> { fn example_song() -> Song<'static> {
let song: Song = serde_json::from_reader( Song::new(
File::open(args().skip(1).next().context(concat!( Default::default(),
"You need to give a single argument with the filename of your ", [(
TARGET, "Piano".into(),
"." Channel::new(
))?) PathOrString::String("hi".into()),
.context(concat!("Failed to open the ", TARGET, "."))?, vec!["do", "", "mi"].into_iter().map(Into::into),
"sin(2*pi()*(442*2^((n+1)/N))*t)".parse().unwrap(),
[
('l', 4f64),
('L', 0.0),
('t', 0.0),
('T', 60.0),
('N', 12.0),
],
[],
[(
"L".into(),
VariableChange::new('L', "2^(2-log(2, l))*(60/T)".parse().unwrap()),
)],
),
)],
) )
.context(concat!("Failed to read the ", TARGET, "."))?; }
println!( fn main() -> anyhow::Result<()> {
"{}", let string = args().skip(1).next().context(format!(
serde_json::to_string_pretty(&song).context("Failed to pretty print the song! What?!")? "You need to give a single argument with the filename of your {TARGET}."
); ))?;
if let Some(argument) = string.strip_prefix("--") {
if argument == "example" {
println!("{}", serde_json::to_string_pretty(&example_song()).unwrap());
} else if argument == "help" {
println!(
"BLIP lab file player. Pass it a path to a {TARGET} or use with \"--example\" for an example {TARGET}."
);
} else {
eprintln!("Unknown argument: \"{argument}\"");
}
} else {
let song: Song = serde_json::from_reader(
File::open(string).context(format!("Failed to open the {TARGET}."))?,
)
.context(format!("Failed to read the {TARGET}."))?;
println!(
"{}",
serde_json::to_string_pretty(&song)
.context("Failed to pretty print the song! What?!")?
);
}
Ok(()) Ok(())
} }

View file

@ -7,11 +7,19 @@ use derive_new::new;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{DisplayFromStr, serde_as}; use serde_with::{DisplayFromStr, serde_as};
fn is_default<T: Default + PartialEq>(t: &T) -> bool {
t == &T::default()
}
/// Entry point to a BLIPlab song. Made of BLIP channels. /// Entry point to a BLIPlab song. Made of BLIP channels.
#[derive(new, Serialize, Deserialize)] #[derive(new, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Song<'a> { pub struct Song<'a> {
#[serde(default)] #[serde(default)]
#[cfg_attr(
feature = "example-skip-defaults",
serde(skip_serializing_if = "is_default")
)]
global_variables: Globals, global_variables: Globals,
#[new(into_iter = "(Cow<'a, str>, Channel<'a>)")] #[new(into_iter = "(Cow<'a, str>, Channel<'a>)")]
channels: HashMap<Cow<'a, str>, Channel<'a>>, channels: HashMap<Cow<'a, str>, Channel<'a>>,
@ -19,7 +27,7 @@ pub struct Song<'a> {
/// Global variables used by the compiler for rendering every sample. /// Global variables used by the compiler for rendering every sample.
/// They are made global for the sake of consistency but can be modified. /// They are made global for the sake of consistency but can be modified.
#[derive(new, Serialize, Deserialize)] #[derive(new, Serialize, Deserialize, PartialEq)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Globals { pub struct Globals {
#[new(into)] #[new(into)]
@ -46,9 +54,17 @@ pub struct Channel<'a> {
instrument: Expression, instrument: Expression,
#[new(into_iter = "(char, f64)")] #[new(into_iter = "(char, f64)")]
#[serde(default)] #[serde(default)]
#[cfg_attr(
feature = "example-skip-defaults",
serde(skip_serializing_if = "is_default")
)]
variables: HashMap<char, f64>, variables: HashMap<char, f64>,
#[new(into_iter = "(char, Cow<'a, str>)")] #[new(into_iter = "(char, Cow<'a, str>)")]
#[serde(default)] #[serde(default)]
#[cfg_attr(
feature = "example-skip-defaults",
serde(skip_serializing_if = "is_default")
)]
macros: HashMap<char, Cow<'a, str>>, macros: HashMap<char, Cow<'a, str>>,
#[new(into_iter = "(Cow<'a, str>, VariableChange)")] #[new(into_iter = "(Cow<'a, str>, VariableChange)")]
#[serde(default = "default_slope")] #[serde(default = "default_slope")]