Compare commits
15
Commits
d166be47ed
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ddd3fee887 | ||
|
|
b6a59f6563 | ||
|
|
575b70d4ba | ||
|
|
b0a46f0710 | ||
|
|
3b0cb7e82b | ||
|
|
4d4c157f3a | ||
|
|
b047bd4324 | ||
|
|
366c481827 | ||
|
|
01765bf115 | ||
|
|
c440f75a4a | ||
|
|
714aebc2ed | ||
|
|
e3edf57745 | ||
|
|
627969a23b | ||
|
|
b7f34f795e | ||
|
|
8c559d605e |
@@ -64,9 +64,9 @@ jobs:
|
|||||||
# we specify bash to get pipefail; it guards against the `curl` command
|
# we specify bash to get pipefail; it guards against the `curl` command
|
||||||
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
# failing. otherwise `sh` won't catch that `curl` returned non-0
|
||||||
shell: bash
|
shell: bash
|
||||||
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.32.0/cargo-dist-installer.sh | sh"
|
run: "curl --proto '=https' --tlsv1.2 -LsSf https://github.com/axodotdev/cargo-dist/releases/download/v0.31.0/cargo-dist-installer.sh | sh"
|
||||||
- name: Cache dist
|
- name: Cache dist
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: cargo-dist-cache
|
name: cargo-dist-cache
|
||||||
path: ~/.cargo/bin/dist
|
path: ~/.cargo/bin/dist
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
cat plan-dist-manifest.json
|
cat plan-dist-manifest.json
|
||||||
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
echo "manifest=$(jq -c "." plan-dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||||
- name: "Upload dist-manifest.json"
|
- name: "Upload dist-manifest.json"
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: artifacts-plan-dist-manifest
|
name: artifacts-plan-dist-manifest
|
||||||
path: plan-dist-manifest.json
|
path: plan-dist-manifest.json
|
||||||
@@ -131,7 +131,7 @@ jobs:
|
|||||||
run: ${{ matrix.install_dist.run }}
|
run: ${{ matrix.install_dist.run }}
|
||||||
# Get the dist-manifest
|
# Get the dist-manifest
|
||||||
- name: Fetch local artifacts
|
- name: Fetch local artifacts
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: artifacts-*
|
pattern: artifacts-*
|
||||||
path: target/distrib/
|
path: target/distrib/
|
||||||
@@ -158,7 +158,7 @@ jobs:
|
|||||||
|
|
||||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||||
- name: "Upload artifacts"
|
- name: "Upload artifacts"
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
|
name: artifacts-build-local-${{ join(matrix.targets, '_') }}
|
||||||
path: |
|
path: |
|
||||||
@@ -180,14 +180,14 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install cached dist
|
- name: Install cached dist
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: cargo-dist-cache
|
name: cargo-dist-cache
|
||||||
path: ~/.cargo/bin/
|
path: ~/.cargo/bin/
|
||||||
- run: chmod +x ~/.cargo/bin/dist
|
- run: chmod +x ~/.cargo/bin/dist
|
||||||
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
# Get all the local artifacts for the global tasks to use (for e.g. checksums)
|
||||||
- name: Fetch local artifacts
|
- name: Fetch local artifacts
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: artifacts-*
|
pattern: artifacts-*
|
||||||
path: target/distrib/
|
path: target/distrib/
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
|
|
||||||
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
cp dist-manifest.json "$BUILD_MANIFEST_NAME"
|
||||||
- name: "Upload artifacts"
|
- name: "Upload artifacts"
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
name: artifacts-build-global
|
name: artifacts-build-global
|
||||||
path: |
|
path: |
|
||||||
@@ -230,14 +230,14 @@ jobs:
|
|||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install cached dist
|
- name: Install cached dist
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
name: cargo-dist-cache
|
name: cargo-dist-cache
|
||||||
path: ~/.cargo/bin/
|
path: ~/.cargo/bin/
|
||||||
- run: chmod +x ~/.cargo/bin/dist
|
- run: chmod +x ~/.cargo/bin/dist
|
||||||
# Fetch artifacts from scratch-storage
|
# Fetch artifacts from scratch-storage
|
||||||
- name: Fetch artifacts
|
- name: Fetch artifacts
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: artifacts-*
|
pattern: artifacts-*
|
||||||
path: target/distrib/
|
path: target/distrib/
|
||||||
@@ -250,14 +250,14 @@ jobs:
|
|||||||
cat dist-manifest.json
|
cat dist-manifest.json
|
||||||
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
echo "manifest=$(jq -c "." dist-manifest.json)" >> "$GITHUB_OUTPUT"
|
||||||
- name: "Upload dist-manifest.json"
|
- name: "Upload dist-manifest.json"
|
||||||
uses: actions/upload-artifact@v7
|
uses: actions/upload-artifact@v6
|
||||||
with:
|
with:
|
||||||
# Overwrite the previous copy
|
# Overwrite the previous copy
|
||||||
name: artifacts-dist-manifest
|
name: artifacts-dist-manifest
|
||||||
path: dist-manifest.json
|
path: dist-manifest.json
|
||||||
# Create a GitHub Release while uploading all files to it
|
# Create a GitHub Release while uploading all files to it
|
||||||
- name: "Download GitHub Artifacts"
|
- name: "Download GitHub Artifacts"
|
||||||
uses: actions/download-artifact@v8
|
uses: actions/download-artifact@v7
|
||||||
with:
|
with:
|
||||||
pattern: artifacts-*
|
pattern: artifacts-*
|
||||||
path: artifacts
|
path: artifacts
|
||||||
|
|||||||
Generated
+1
-1
@@ -2120,7 +2120,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termato"
|
name = "termato"
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cpal",
|
"cpal",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "termato"
|
name = "termato"
|
||||||
version = "0.0.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
repository = "https://github.com/StevanFreeborn/termato"
|
repository = "https://github.com/StevanFreeborn/termato"
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -4,10 +4,17 @@ members = ["cargo:."]
|
|||||||
# Config for 'dist'
|
# Config for 'dist'
|
||||||
[dist]
|
[dist]
|
||||||
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
# The preferred dist version to use in CI (Cargo.toml SemVer syntax)
|
||||||
cargo-dist-version = "0.32.0"
|
cargo-dist-version = "0.31.0"
|
||||||
# CI backends to support
|
# CI backends to support
|
||||||
ci = "github"
|
ci = "github"
|
||||||
# The installers to generate for each app
|
# The installers to generate for each app
|
||||||
installers = ["shell", "powershell"]
|
installers = ["shell", "powershell"]
|
||||||
# Target platforms to build apps for (Rust target-triple syntax)
|
# Target platforms to build apps for (Rust target-triple syntax)
|
||||||
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
targets = ["aarch64-apple-darwin", "aarch64-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"]
|
||||||
|
# Path that installers should place binaries in
|
||||||
|
install-path = "CARGO_HOME"
|
||||||
|
# Whether to install an updater program
|
||||||
|
install-updater = true
|
||||||
|
|
||||||
|
[dist.dependencies.apt]
|
||||||
|
libasound2-dev = "*"
|
||||||
|
|||||||
+79
-34
@@ -1,6 +1,10 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, stdout},
|
io::{self, stdout},
|
||||||
sync::{Arc, Mutex, mpsc},
|
sync::{
|
||||||
|
Arc, Mutex,
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
mpsc,
|
||||||
|
},
|
||||||
thread,
|
thread,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
@@ -143,8 +147,9 @@ impl Drop for TerminalGuard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct AudioVisualizer {
|
struct AudioVisualizer {
|
||||||
pub bar_data: Arc<Mutex<Vec<u64>>>,
|
bar_data: Arc<Mutex<Vec<u64>>>,
|
||||||
|
stop_flag: Arc<AtomicBool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a implementation largely lifted from these
|
// This is a implementation largely lifted from these
|
||||||
@@ -152,18 +157,22 @@ pub struct AudioVisualizer {
|
|||||||
// 1. CAVA (C): https://github.com/karlstav/cava
|
// 1. CAVA (C): https://github.com/karlstav/cava
|
||||||
// 2. cli-visualizer (C++): https://github.com/dpayne/cli-visualizer
|
// 2. cli-visualizer (C++): https://github.com/dpayne/cli-visualizer
|
||||||
impl AudioVisualizer {
|
impl AudioVisualizer {
|
||||||
pub fn new(num_bars: usize) -> Self {
|
fn new(num_bars: usize) -> Self {
|
||||||
//
|
|
||||||
let bar_data = Arc::new(Mutex::new(vec![0; num_bars]));
|
let bar_data = Arc::new(Mutex::new(vec![0; num_bars]));
|
||||||
let bar_data_clone = Arc::clone(&bar_data);
|
let bar_data_clone = Arc::clone(&bar_data);
|
||||||
|
let stop_flag = Arc::new(AtomicBool::new(false));
|
||||||
|
let stop_clone = Arc::clone(&stop_flag);
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
if let Err(e) = Self::run_audio_loop(bar_data_clone, num_bars) {
|
if let Err(e) = Self::run_audio_loop(bar_data_clone, num_bars, stop_clone) {
|
||||||
eprintln!("Audio capture error: {:?}", e);
|
eprintln!("Audio capture error: {:?}", e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Self { bar_data }
|
Self {
|
||||||
|
bar_data,
|
||||||
|
stop_flag,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doing best effort to support cross-platform functionality
|
// Doing best effort to support cross-platform functionality
|
||||||
@@ -185,9 +194,11 @@ impl AudioVisualizer {
|
|||||||
{
|
{
|
||||||
if let Ok(devices) = host.devices() {
|
if let Ok(devices) = host.devices() {
|
||||||
for dev in devices {
|
for dev in devices {
|
||||||
if let Ok(name) = dev.name() {
|
if let Ok(desc) = dev.description() {
|
||||||
// Monitor devices mirror system output under PulseAudio/PipeWire
|
let name = desc.to_string();
|
||||||
|
|
||||||
if name.contains("monitor") {
|
if name.contains("monitor") {
|
||||||
|
// Monitor devices mirror system output under PulseAudio/PipeWire
|
||||||
if let Ok(config) = dev.default_input_config() {
|
if let Ok(config) = dev.default_input_config() {
|
||||||
return Ok((dev, config.into()));
|
return Ok((dev, config.into()));
|
||||||
}
|
}
|
||||||
@@ -304,11 +315,12 @@ impl AudioVisualizer {
|
|||||||
smoothed
|
smoothed
|
||||||
}
|
}
|
||||||
|
|
||||||
// songs can be quiet and load so we try to adjust
|
// songs can be quiet and loud so we try to adjust
|
||||||
// sensitivity to avoid bars becoming flattened or
|
// sensitivity to avoid bars becoming flattened or
|
||||||
// clipped
|
// clipped
|
||||||
fn adjust_autosens(autosens: &mut f32, bars: &[f32]) {
|
fn adjust_autosens(autosens: &mut f32, bars: &[f32]) {
|
||||||
let max_val = bars.iter().cloned().fold(0.0f32, f32::max);
|
let max_val = bars.iter().copied().fold(0.0f32, f32::max);
|
||||||
|
|
||||||
if max_val > 8.0 {
|
if max_val > 8.0 {
|
||||||
*autosens *= 0.98;
|
*autosens *= 0.98;
|
||||||
} else if max_val < 3.0 && *autosens < 3.0 {
|
} else if max_val < 3.0 && *autosens < 3.0 {
|
||||||
@@ -321,6 +333,7 @@ impl AudioVisualizer {
|
|||||||
fn run_audio_loop(
|
fn run_audio_loop(
|
||||||
bar_data: Arc<Mutex<Vec<u64>>>,
|
bar_data: Arc<Mutex<Vec<u64>>>,
|
||||||
num_bars: usize,
|
num_bars: usize,
|
||||||
|
stop: Arc<AtomicBool>,
|
||||||
) -> Result<(), Box<dyn std::error::Error>> {
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let (device, config) = Self::get_audio_device(&host)?;
|
let (device, config) = Self::get_audio_device(&host)?;
|
||||||
@@ -353,16 +366,16 @@ impl AudioVisualizer {
|
|||||||
let stream = device.build_input_stream(
|
let stream = device.build_input_stream(
|
||||||
config,
|
config,
|
||||||
move |data: &[f32], _| {
|
move |data: &[f32], _| {
|
||||||
let mut buf = buffer_clone.lock().unwrap();
|
if let Ok(mut buf) = buffer_clone.lock() {
|
||||||
|
for chunk in data.chunks(channels) {
|
||||||
|
let mono: f32 = chunk.iter().sum::<f32>() / channels as f32;
|
||||||
|
buf.push(mono);
|
||||||
|
}
|
||||||
|
|
||||||
for chunk in data.chunks(channels) {
|
if buf.len() > chunk_size * 2 {
|
||||||
let mono: f32 = chunk.iter().sum::<f32>() / channels as f32;
|
let drain_amt = buf.len() - chunk_size;
|
||||||
buf.push(mono);
|
buf.drain(0..drain_amt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if buf.len() > chunk_size * 2 {
|
|
||||||
let drain_amt = buf.len() - chunk_size;
|
|
||||||
buf.drain(0..drain_amt);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|err| eprintln!("Stream error: {}", err),
|
|err| eprintln!("Stream error: {}", err),
|
||||||
@@ -371,11 +384,14 @@ impl AudioVisualizer {
|
|||||||
|
|
||||||
stream.play()?;
|
stream.play()?;
|
||||||
|
|
||||||
loop {
|
while !stop.load(Ordering::Relaxed) {
|
||||||
thread::sleep(std::time::Duration::from_millis(16));
|
thread::sleep(std::time::Duration::from_millis(16));
|
||||||
|
|
||||||
let samples = {
|
let samples = {
|
||||||
let buf = audio_buffer.lock().unwrap();
|
let buf = match audio_buffer.lock() {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => continue,
|
||||||
|
};
|
||||||
if buf.len() < chunk_size {
|
if buf.len() < chunk_size {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -384,7 +400,10 @@ impl AudioVisualizer {
|
|||||||
|
|
||||||
Self::apply_hann_window(&samples, &mut windowed_buffer);
|
Self::apply_hann_window(&samples, &mut windowed_buffer);
|
||||||
|
|
||||||
fft.process(&mut windowed_buffer, &mut spectrum).unwrap();
|
if let Err(e) = fft.process(&mut windowed_buffer, &mut spectrum) {
|
||||||
|
eprintln!("FFT error: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let raw_bars = Self::process_fft_magnitudes(
|
let raw_bars = Self::process_fft_magnitudes(
|
||||||
&spectrum,
|
&spectrum,
|
||||||
@@ -407,6 +426,14 @@ impl AudioVisualizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for AudioVisualizer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.stop_flag.store(true, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,8 +449,10 @@ impl AudioVisualizer {
|
|||||||
fn render_visualizer(f: &mut Frame, area: Rect, bar_values: &[u64]) {
|
fn render_visualizer(f: &mut Frame, area: Rect, bar_values: &[u64]) {
|
||||||
const BLOCKS: [&str; 8] = ["▂", "▃", "▄", "▅", "▆", "▇", "█", "█"];
|
const BLOCKS: [&str; 8] = ["▂", "▃", "▄", "▅", "▆", "▇", "█", "█"];
|
||||||
|
|
||||||
|
let max_bars = area.width as usize;
|
||||||
let line: String = bar_values
|
let line: String = bar_values
|
||||||
.iter()
|
.iter()
|
||||||
|
.take(max_bars)
|
||||||
.map(|&val| {
|
.map(|&val| {
|
||||||
if val == 0 {
|
if val == 0 {
|
||||||
" "
|
" "
|
||||||
@@ -437,7 +466,7 @@ fn render_visualizer(f: &mut Frame, area: Rect, bar_values: &[u64]) {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let viz_paragraph = Paragraph::new(line)
|
let viz_paragraph = Paragraph::new(line)
|
||||||
.style(Style::default().fg(Color::Cyan))
|
.style(Style::default().fg(Color::Rgb(200, 184, 224)))
|
||||||
.alignment(Alignment::Center);
|
.alignment(Alignment::Center);
|
||||||
|
|
||||||
f.render_widget(viz_paragraph, area);
|
f.render_widget(viz_paragraph, area);
|
||||||
@@ -480,11 +509,18 @@ fn main() -> Result<(), io::Error> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let positional_args: Vec<&String> = args
|
let is_flag = |arg: &str| {
|
||||||
.iter()
|
arg == "-n"
|
||||||
.skip(1)
|
|| arg == "--notify"
|
||||||
.filter(|arg| *arg != "-n" && *arg != "--notify" && *arg != "-z" && *arg != "--visualizer")
|
|| arg == "-z"
|
||||||
.collect();
|
|| arg == "--visualizer"
|
||||||
|
|| arg == "-h"
|
||||||
|
|| arg == "--help"
|
||||||
|
|| arg == "-v"
|
||||||
|
|| arg == "--version"
|
||||||
|
};
|
||||||
|
|
||||||
|
let positional_args: Vec<&String> = args.iter().skip(1).filter(|arg| !is_flag(arg)).collect();
|
||||||
|
|
||||||
let work_mins = positional_args
|
let work_mins = positional_args
|
||||||
.first()
|
.first()
|
||||||
@@ -520,11 +556,11 @@ fn main() -> Result<(), io::Error> {
|
|||||||
let time_str = format!("{:02}:{:02}", minutes, seconds);
|
let time_str = format!("{:02}:{:02}", minutes, seconds);
|
||||||
|
|
||||||
let time_color = if !termato.is_running {
|
let time_color = if !termato.is_running {
|
||||||
Color::Yellow
|
Color::Rgb(212, 200, 122)
|
||||||
} else {
|
} else {
|
||||||
match termato.mode {
|
match termato.mode {
|
||||||
TimerMode::Work => Color::Red,
|
TimerMode::Work => Color::Rgb(212, 115, 115),
|
||||||
TimerMode::Break => Color::Green,
|
TimerMode::Break => Color::Rgb(126, 200, 192),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -609,7 +645,7 @@ fn main() -> Result<(), io::Error> {
|
|||||||
let help_block = Block::default()
|
let help_block = Block::default()
|
||||||
.title(" Controls ")
|
.title(" Controls ")
|
||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.border_style(Style::default().fg(Color::DarkGray));
|
.border_style(Style::default().fg(Color::Rgb(58, 62, 70)));
|
||||||
|
|
||||||
let help_content = Paragraph::new(
|
let help_content = Paragraph::new(
|
||||||
"[Space] Pause / Resume\n\
|
"[Space] Pause / Resume\n\
|
||||||
@@ -619,7 +655,7 @@ fn main() -> Result<(), io::Error> {
|
|||||||
[Q] Quit",
|
[Q] Quit",
|
||||||
)
|
)
|
||||||
.block(help_block)
|
.block(help_block)
|
||||||
.style(Style::default().fg(Color::DarkGray))
|
.style(Style::default().fg(Color::Rgb(224, 228, 232)))
|
||||||
.alignment(Alignment::Left);
|
.alignment(Alignment::Left);
|
||||||
|
|
||||||
f.render_widget(Clear, popup_area);
|
f.render_widget(Clear, popup_area);
|
||||||
@@ -798,4 +834,13 @@ mod tests {
|
|||||||
let result = result.with_notifications(true);
|
let result = result.with_notifications(true);
|
||||||
assert!(result.enable_notifications);
|
assert!(result.enable_notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn termato_visualizer_when_called_it_should_verify_opt_in_behavior() {
|
||||||
|
let result = Termato::new(25, 5);
|
||||||
|
assert!(!result.enable_visualizer);
|
||||||
|
|
||||||
|
let result = result.with_visualizer(true);
|
||||||
|
assert!(result.enable_visualizer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user