use eframe::egui;
#[cfg(target_arch = "wasm32")]
mod web;
#[cfg(target_arch = "wasm32")]
pub use web::Splash;
pub(crate) const VERSION: &str = env!("CARGO_PKG_VERSION");
pub(crate) const WIDTH: f32 = 560.0;
pub(crate) const GAP: f32 = 4.0;
pub(crate) fn title(ui: &mut egui::Ui) {
ui.label(
egui::RichText::new(format!("drawbar {VERSION}"))
.font(egui::FontId::new(18.0, crate::app::bold())),
);
}
pub(crate) fn link(ui: &mut egui::Ui, label: &str, url: &str) {
ui.add(egui::Hyperlink::from_label_and_url(label, url).open_in_new_tab(true));
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Standing {
Supported,
Untested,
Unsupported,
}
pub const EXPECTATIONS: &[(Standing, &str)] = &[
(
Standing::Supported,
"Piano (npno) and sample (nsmp) files can be created, trimmed, modified, encoded, \
decoded, auditioned and transferred.",
),
(
Standing::Supported,
"All Nord Electro 5 files can be viewed, edited and transferred: programs, live, \
settings, and set lists or songs.",
),
(
Standing::Supported,
"The Nord Electro 5 has full USB support.",
),
(
Standing::Untested,
"nsmp3 and nsmp4 files are fully supported, but playback of files drawbar has edited \
or modified has not been tested on a real instrument.",
),
(
Standing::Untested,
"Nord Stage 2, 3 and 4 programs and presets are supported, but not tested on real \
instruments.",
),
(
Standing::Unsupported,
"Instruments other than the Nord Electro 5 have not been tested with Connect an \
instrument…, so USB support for other models cannot be guaranteed.",
),
(
Standing::Unsupported,
"Long-term storage in drawbar is not guaranteed while it is in alpha. Back up your \
files elsewhere.",
),
];
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Line<'a> {
Blank,
Heading(&'a str),
Item {
scope: Option<&'a str>,
text: &'a str,
commit: Option<Commit<'a>>,
},
Changelog(&'a str),
Text(&'a str),
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub struct Commit<'a> {
pub sha: &'a str,
pub url: &'a str,
}
pub fn plain(body: &str) -> String {
body.replace('\u{fe0f}', "")
}
pub fn classify(line: &str) -> Line<'_> {
let line = line.trim_end();
if line.is_empty() {
return Line::Blank;
}
if let Some(title) = line.strip_prefix("### ") {
return Line::Heading(title);
}
if let Some(url) = line.strip_prefix("**Full changelog**: ") {
if let Some(url) = https(url) {
return Line::Changelog(url);
}
}
let Some(item) = line.strip_prefix("- ") else {
return Line::Text(line);
};
let (item, commit) = split_commit(item);
let (scope, text) = split_scope(item);
Line::Item {
scope,
text,
commit,
}
}
fn https(url: &str) -> Option<&str> {
match url
.strip_prefix("https://")
.is_some_and(|rest| !rest.is_empty())
{
true => Some(url),
false => None,
}
}
fn split_commit(item: &str) -> (&str, Option<Commit<'_>>) {
const OPEN: &str = " ([";
let Some(at) = item.rfind(OPEN) else {
return (item, None);
};
let Some(inner) = item[at + OPEN.len()..].strip_suffix("))") else {
return (item, None);
};
let Some((sha, url)) = inner.split_once("](") else {
return (item, None);
};
if sha.is_empty() {
return (item, None);
}
let Some(url) = https(url) else {
return (item, None);
};
(&item[..at], Some(Commit { sha, url }))
}
fn split_scope(item: &str) -> (Option<&str>, &str) {
let Some(rest) = item.strip_prefix("**") else {
return (None, item);
};
let Some((scope, text)) = rest.split_once(":** ") else {
return (None, item);
};
match scope.contains('*') {
true => (None, item),
false => (Some(scope), text),
}
}
#[cfg(test)]
mod tests {
use super::*;
const RELEASED: &str = "\
### ⚠️ Breaking changes
- **nord-cli:** ship the v2 sample encoder unflagged and gate v3/v4 as unverified (#83) ([163abc4](https://github.com/jmoo/drawbar/commit/163abc4e00b0b088fca9a1212fd26faf4cb11770))
### Features
- name every write drawbar sends, and read settings from nord-cli (#71) ([3029a35](https://github.com/jmoo/drawbar/commit/3029a35ff1b0f812ccb21cb1a86fbaf0ae5e0256))
**Full changelog**: https://github.com/jmoo/drawbar/compare/drawbar-v0.4.0...drawbar-v0.5.0";
#[test]
fn a_heading_keeps_its_title_alone() {
assert_eq!(classify("### Bug fixes"), Line::Heading("Bug fixes"));
}
#[test]
fn an_item_splits_its_scope_its_text_and_its_commit() {
let line = "- **drawbar:** say what changed ([abc1234](https://example.com/c/abc1234))";
assert_eq!(
classify(line),
Line::Item {
scope: Some("drawbar"),
text: "say what changed",
commit: Some(Commit {
sha: "abc1234",
url: "https://example.com/c/abc1234",
}),
}
);
}
#[test]
fn an_unscoped_item_keeps_its_whole_description() {
assert_eq!(
classify("- say what changed"),
Line::Item {
scope: None,
text: "say what changed",
commit: None,
}
);
}
#[test]
fn a_commit_link_that_is_not_https_stays_in_the_text() {
let line = "- fixed it ([abc1234](javascript:alert(1)))";
assert_eq!(
classify(line),
Line::Item {
scope: None,
text: "fixed it ([abc1234](javascript:alert(1)))",
commit: None,
}
);
}
#[test]
fn a_changelog_line_that_is_not_https_is_not_a_link() {
assert_eq!(
classify("**Full changelog**: ftp://example.com/log"),
Line::Text("**Full changelog**: ftp://example.com/log")
);
}
#[test]
fn a_line_the_shape_does_not_cover_stays_as_it_reads() {
assert_eq!(
classify(" Ordinary prose."),
Line::Text(" Ordinary prose.")
);
assert_eq!(classify(" "), Line::Blank);
}
#[test]
fn a_heading_keeps_its_warning_sign_without_the_variation_selector() {
assert_eq!(
plain("### \u{26a0}\u{fe0f} Breaking changes"),
"### \u{26a0} Breaking changes"
);
}
#[test]
fn every_line_of_a_published_release_body_is_recognised() {
let read: Vec<Line<'_>> = RELEASED.lines().map(classify).collect();
assert_eq!(
read,
vec![
Line::Heading("⚠️ Breaking changes"),
Line::Item {
scope: Some("nord-cli"),
text: "ship the v2 sample encoder unflagged and gate v3/v4 as unverified (#83)",
commit: Some(Commit {
sha: "163abc4",
url: "https://github.com/jmoo/drawbar/commit/163abc4e00b0b088fca9a1212fd26faf4cb11770",
}),
},
Line::Blank,
Line::Heading("Features"),
Line::Item {
scope: None,
text: "name every write drawbar sends, and read settings from nord-cli (#71)",
commit: Some(Commit {
sha: "3029a35",
url: "https://github.com/jmoo/drawbar/commit/3029a35ff1b0f812ccb21cb1a86fbaf0ae5e0256",
}),
},
Line::Blank,
Line::Changelog(
"https://github.com/jmoo/drawbar/compare/drawbar-v0.4.0...drawbar-v0.5.0"
),
]
);
}
}