chore: apply format

This commit is contained in:
zawz 2024-12-30 16:10:30 +01:00
parent 819ef4db28
commit 0a6e237049
28 changed files with 705 additions and 510 deletions

View file

@ -1,13 +1,13 @@
log_devices: true
devices:
- name: 'VMPK'
- name: "VMPK"
max_connections: 1
queue_length: 3
interval: 100ms
connect:
- args: [ "sh", "-c", "echo Hello world!" ]
disconnect:
- args: [ "sh", "-c", "echo Bye!" ]
connect:
- args: ["sh", "-c", "echo Hello world!"]
disconnect:
- args: ["sh", "-c", "echo Bye!"]
events:
- type: ProgramChange
run:
@ -38,28 +38,28 @@ devices:
- cmd: "echo [$channel] PitchBend $value $raw $toto"
envconf:
timestamp: toto
- name: 'VMPK'
- name: "VMPK"
log_events: true
max_connections: 1
connect:
- args: [ "sh", "-c", "echo Hello world! 2" ]
disconnect:
- args: [ "sh", "-c", "echo Bye! 2" ]
connect:
- args: ["sh", "-c", "echo Hello world! 2"]
disconnect:
- args: ["sh", "-c", "echo Bye! 2"]
events:
- type: NoteOff
id: 25-30
run:
- args: [ "sh", "-c", "echo 2 [$channel] NoteOff $id" ]
- args: ["sh", "-c", "echo 2 [$channel] NoteOff $id"]
- type: NoteOn
channel: 0
remap: -1
run:
- args: [ "sh", "-c", "echo 2 [$channel] NoteOn $id $value" ]
- args: ["sh", "-c", "echo 2 [$channel] NoteOn $id $value"]
- type: PitchBend
remap: 0-100
float: true
value: 0-65535
run:
- args: [ "sh", "-c", "echo [$channel] PitchBend $value $raw $toto" ]
- args: ["sh", "-c", "echo [$channel] PitchBend $value $raw $toto"]
envconf:
timestamp: toto

View file

@ -11,4 +11,3 @@ pub struct Cli {
#[clap(long, short, action)]
pub list: bool,
}

View file

@ -1,12 +1,12 @@
use std::time::Duration;
use super::serializer::DeviceConfigSerializer;
use super::{EventConfig, RunConfig};
use crate::event::Event;
use crate::util;
use crate::Error;
use super::{RunConfig,EventConfig};
use super::serializer::DeviceConfigSerializer;
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub enum Identifier {
All,
Name(String),
@ -14,7 +14,7 @@ pub enum Identifier {
Addr(String),
}
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct DeviceConfig {
pub identifier: Identifier,
pub max_connections: Option<u32>,
@ -29,12 +29,16 @@ pub struct DeviceConfig {
impl DeviceConfig {
fn run_internal<'a, T>(&self, v: Option<T>) -> Result<Vec<std::process::ExitStatus>, Error>
where
T: IntoIterator<Item = &'a RunConfig>
T: IntoIterator<Item = &'a RunConfig>,
{
let mut r = Vec::new();
if let Some(ev) = v {
for e in ev {
if let Some(v) = e.run(Event::new().make_env(None, false)?.to_map(e.envconf.as_ref()))? {
if let Some(v) = e.run(
Event::new()
.make_env(None, false)?
.to_map(e.envconf.as_ref()),
)? {
r.push(v);
}
}
@ -46,7 +50,7 @@ impl DeviceConfig {
self.run_internal(self.connect.as_ref())
}
pub fn run_disconnect(&self) -> Result<Vec<std::process::ExitStatus>, Error> {
pub fn run_disconnect(&self) -> Result<Vec<std::process::ExitStatus>, Error> {
self.run_internal(self.disconnect.as_ref())
}
}
@ -57,21 +61,33 @@ impl TryFrom<DeviceConfigSerializer> for DeviceConfig {
Ok(DeviceConfig {
identifier: {
match (v.name, v.regex, v.addr) {
(Some(_), Some(_), _ ) => return Err(Error::IncompatibleArgs("name","regex")),
(Some(_), None , Some(_)) => return Err(Error::IncompatibleArgs("name","addr")),
(None , Some(_), Some(_)) => return Err(Error::IncompatibleArgs("regex","addr")),
(Some(n), None, None ) => Identifier::Name(n),
(None, Some(r), None ) => Identifier::Regex(regex::Regex::new(&r)?),
(None, None , Some(a)) => Identifier::Addr(a),
(None, None, None ) => Identifier::All,
(Some(_), Some(_), Some(_)) => {
return Err(Error::IncompatibleArgs("name", "regex"))
}
(Some(_), Some(_), None) => {
return Err(Error::IncompatibleArgs("name", "regex"))
}
(Some(_), None, Some(_)) => {
return Err(Error::IncompatibleArgs("name", "addr"))
}
(None, Some(_), Some(_)) => {
return Err(Error::IncompatibleArgs("regex", "addr"))
}
(Some(n), None, None) => Identifier::Name(n),
(None, Some(r), None) => Identifier::Regex(regex::Regex::new(&r)?),
(None, None, Some(a)) => Identifier::Addr(a),
(None, None, None) => Identifier::All,
}
},
max_connections: v.max_connections,
connect: util::map_opt_tryfrom(v.connect)?,
connect: util::map_opt_tryfrom(v.connect)?,
disconnect: util::map_opt_tryfrom(v.disconnect)?,
events: util::map_opt_tryfrom(v.events)?,
events: util::map_opt_tryfrom(v.events)?,
queue_length: v.queue_length.unwrap_or(256),
interval: v.interval.map(|x| x.unwrap()).unwrap_or_else(|| Duration::new(0, 0)),
interval: v
.interval
.map(|x| x.unwrap())
.unwrap_or_else(|| Duration::new(0, 0)),
log: v.log_events.unwrap_or(false),
})
}

View file

@ -1,6 +1,6 @@
use super::RunConfig;
use crate::event::{Event,EventType};
use crate::util::{self, SmartSet, Range, Remapper};
use crate::event::{Event, EventType};
use crate::util::{self, Range, Remapper, SmartSet};
use super::serializer::EventConfigSerializer;
@ -32,7 +32,7 @@ lazy_static! {
};
}
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct EventConfig {
pub run: Vec<RunConfig>,
pub r#type: EventType,
@ -46,7 +46,7 @@ pub struct EventConfig {
impl EventConfig {
pub fn match_value(&self, event: &Event) -> bool {
match &self.value {
Some(v) => v.set.contains(&event.value),
Some(v) => v.set.contains(&event.value),
None => true,
}
}
@ -59,21 +59,30 @@ impl TryFrom<EventConfigSerializer> for EventConfig {
run: util::map_tryfrom(v.run)?,
r#type: v.r#type,
channel: match v.r#type.has_channel() {
true => v.channel.unwrap_or_else(|| CHANNEL_DEFAULT_MAP.clone()),
true => v.channel.unwrap_or_else(|| CHANNEL_DEFAULT_MAP.clone()),
false => NULL_DEFAULT_MAP.clone(),
},
id: match v.r#type.has_id() {
true => v.id.unwrap_or_else(|| ID_DEFAULT_MAP.clone()),
true => v.id.unwrap_or_else(|| ID_DEFAULT_MAP.clone()),
false => NULL_DEFAULT_MAP.clone(),
},
remap: v.remap.map(|x| Remapper::new(Range::new(v.r#type.min_value() as f64, v.r#type.max_value() as f64), x )),
remap: v.remap.map(|x| {
Remapper::new(
Range::new(v.r#type.min_value() as f64, v.r#type.max_value() as f64),
x,
)
}),
float: v.float.unwrap_or(false),
value: v.value,
};
if let Some(remap) = &r.remap {
let range = remap.src();
if range.start() < i64::MIN as f64 { return Err(Self::Error::RemapTooLow(range.start())) }
if range.end() > i64::MAX as f64 { return Err(Self::Error::RemapTooBig(range.end())) }
if range.start() < i64::MIN as f64 {
return Err(Self::Error::RemapTooLow(range.start()));
}
if range.end() > i64::MAX as f64 {
return Err(Self::Error::RemapTooBig(range.end()));
}
}
Ok(r)
}

View file

@ -1,5 +1,5 @@
pub mod event;
pub mod device;
pub mod event;
pub mod run;
pub mod serializer;
@ -14,7 +14,7 @@ pub use event::EventConfig;
pub use run::RunConfig;
pub type EventEnvMap = serializer::EventEnvSerializer;
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
pub struct Config {
pub log: bool,
pub driver: Option<crate::midi::MidiDriver>,

View file

@ -4,7 +4,7 @@ use std::process::{Command, ExitStatus};
use super::serializer::RunConfigSerializer;
use super::EventEnvMap;
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct RunConfig {
pub args: Vec<String>,
pub envconf: Option<EventEnvMap>,
@ -19,9 +19,7 @@ impl RunConfig {
}
c.envs(env);
if self.detach {
std::thread::spawn(move || {
c.status()
});
std::thread::spawn(move || c.status());
Ok(None)
} else {
c.status().map(|v| Some(v))
@ -34,19 +32,17 @@ impl TryFrom<RunConfigSerializer> for RunConfig {
fn try_from(v: RunConfigSerializer) -> Result<Self, Self::Error> {
let args = if v.args.is_some() {
v.args.unwrap()
}
else if v.cmd.is_some() {
} else if v.cmd.is_some() {
crate::run::cross_shell(v.cmd.as_ref().unwrap())
}
else {
return Err(crate::Error::from(crate::error::ConfigError::RunMissingArgs));
} else {
return Err(crate::Error::from(
crate::error::ConfigError::RunMissingArgs,
));
};
Ok(
RunConfig {
args,
envconf: v.envconf,
detach: v.detach.unwrap_or(false),
}
)
Ok(RunConfig {
args,
envconf: v.envconf,
detach: v.detach.unwrap_or(false),
})
}
}

View file

@ -1,11 +1,11 @@
use std::time::Duration;
use super::{RunConfigSerializer,EventConfigSerializer};
use super::{EventConfigSerializer, RunConfigSerializer};
use duration_str::deserialize_duration;
use serde::Deserialize;
#[derive(Debug,Clone,Deserialize)]
#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]
pub enum DurationWrapper {
#[serde(deserialize_with = "deserialize_duration")]
@ -13,15 +13,14 @@ pub enum DurationWrapper {
}
impl DurationWrapper {
pub fn unwrap(self) -> Duration
{
pub fn unwrap(self) -> Duration {
match self {
DurationWrapper::Some(v) => v,
}
}
}
#[derive(Deserialize,Debug,Clone)]
#[derive(Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct DeviceConfigSerializer {
pub name: Option<String>,

View file

@ -1,10 +1,10 @@
use super::RunConfigSerializer;
use crate::event::EventType;
use crate::util::{SmartSet,Range};
use crate::util::{Range, SmartSet};
use serde::Deserialize;
#[derive(Deserialize,Debug,Clone)]
#[derive(Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct EventConfigSerializer {
pub run: Vec<RunConfigSerializer>,

View file

@ -1,7 +1,6 @@
use serde::{Deserialize, Serialize};
use serde::{Serialize,Deserialize};
#[derive(Serialize,Deserialize,Debug,Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct EventEnvSerializer {
pub channel: Option<String>,
@ -10,4 +9,4 @@ pub struct EventEnvSerializer {
pub rawvalue: Option<String>,
pub timestamp: Option<String>,
pub value: Option<String>,
}
}

View file

@ -1,16 +1,16 @@
pub mod event;
pub mod device;
pub mod run;
pub mod event;
pub mod eventenv;
pub mod run;
pub use device::DeviceConfigSerializer;
pub use event::EventConfigSerializer;
pub use run::RunConfigSerializer;
pub use eventenv::EventEnvSerializer;
pub use run::RunConfigSerializer;
use serde::Deserialize;
#[derive(Deserialize,Clone,Debug)]
#[derive(Deserialize, Clone, Debug)]
#[serde(deny_unknown_fields)]
pub struct ConfigSerializer {
pub log_devices: Option<bool>,

View file

@ -1,12 +1,12 @@
use super::EventEnvSerializer;
use serde::{Serialize,Deserialize};
use serde::{Deserialize, Serialize};
#[derive(Serialize,Deserialize,Debug,Clone)]
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(deny_unknown_fields)]
pub struct RunConfigSerializer {
pub args: Option<Vec<String>>,
pub cmd: Option<String>,
pub cmd: Option<String>,
pub envconf: Option<EventEnvSerializer>,
pub detach: Option<bool>,
}

View file

@ -1,4 +1,3 @@
pub const CLIENT_NAME: &str = "rmidimap";
pub const CLIENT_NAME_HANDLER: &str = "rmidimap-handler";
pub const CLIENT_NAME_EVENT: &str = "rmidimap-event-watcher";
pub const CLIENT_NAME_EVENT: &str = "rmidimap-event-watcher";

View file

@ -8,7 +8,7 @@ use crate::midi::backend::alsa::AlsaError;
use thiserror::Error;
#[derive(Error,Debug)]
#[derive(Error, Debug)]
pub enum Error {
#[error(transparent)]
IO(#[from] std::io::Error),
@ -44,7 +44,7 @@ pub enum Error {
Unknown,
}
#[derive(Error,Debug)]
#[derive(Error, Debug)]
pub enum ConfigError {
#[error("run config is missing execution configuration, either \"args\" or \"cmd\" has to be specified")]
RunMissingArgs,

View file

@ -1,11 +1,11 @@
use std::fmt::{Display, Write};
use std::{collections::HashMap, time::SystemTime};
use std::fmt::{Write,Display};
use crate::config::EventEnvMap;
use crate::util::Remapper;
use crate::Error;
use serde::{Serialize,Deserialize};
use serde::{Deserialize, Serialize};
use lazy_static::lazy_static;
@ -21,11 +21,12 @@ lazy_static! {
}
pub fn event_to_key(r#type: EventType, channel: u8, id: u8) -> u32 {
(r#type as u32)*256*256 + (channel as u32)*256 + (id as u32)
(r#type as u32) * 256 * 256 + (channel as u32) * 256 + (id as u32)
}
#[rustfmt::skip]
#[repr(u8)]
#[derive(Serialize,Deserialize,Debug,Copy,Clone,Default,Display)]
#[derive(Serialize, Deserialize, Debug, Copy, Clone, Default, enum_display_derive::Display)]
pub enum EventType {
#[default]
Unknown = 0b0000,
@ -41,42 +42,36 @@ pub enum EventType {
impl EventType {
pub fn has_id(&self) -> bool {
!matches!(self, EventType::Unknown | EventType::ChannelPressure | EventType::PitchBend | EventType::System )
!matches!(
self,
EventType::Unknown
| EventType::ChannelPressure
| EventType::PitchBend
| EventType::System
)
}
pub fn has_channel(&self) -> bool {
!matches!(self, EventType::Unknown | EventType::System )
!matches!(self, EventType::Unknown | EventType::System)
}
pub fn min_value(&self) -> i32 {
match self {
EventType::NoteOff |
EventType::NoteOn |
EventType::Controller
=> 0,
EventType::PolyphonicKeyPressure |
EventType::ChannelPressure
=> 127,
EventType::PitchBend
=> 0,
EventType::NoteOff | EventType::NoteOn | EventType::Controller => 0,
EventType::PolyphonicKeyPressure | EventType::ChannelPressure => 127,
EventType::PitchBend => 0,
_ => 0,
}
}
pub fn max_value(&self) -> i32 {
match self {
EventType::NoteOff |
EventType::NoteOn |
EventType::Controller
=> 127,
EventType::PolyphonicKeyPressure |
EventType::ChannelPressure
=> 127,
EventType::PitchBend
=> 32767,
EventType::NoteOff | EventType::NoteOn | EventType::Controller => 127,
EventType::PolyphonicKeyPressure | EventType::ChannelPressure => 127,
EventType::PitchBend => 32767,
_ => 0,
}
}
}
#[derive(Debug,Default)]
#[derive(Debug, Default)]
pub struct Event<'a> {
pub r#type: EventType,
pub channel: u8,
@ -86,7 +81,7 @@ pub struct Event<'a> {
pub timestamp: Option<SystemTime>,
}
#[derive(Debug,Clone,Default)]
#[derive(Debug, Clone, Default)]
pub struct EventBuf {
pub r#type: EventType,
pub channel: u8,
@ -105,7 +100,7 @@ pub struct EventEnv {
pub value: String,
}
#[derive(Clone,Debug)]
#[derive(Clone, Debug)]
struct EventEnvRef<'a> {
pub channel: &'a str,
pub id: &'a str,
@ -115,11 +110,17 @@ struct EventEnvRef<'a> {
pub value: &'a str,
}
impl<'a> std::fmt::Display for Event<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{{ \"type\": \"{}\", \"channel\": {}, \"id\": {}, \"value\": {}, \"raw\": \"{}\" }}",
self.r#type, self.channel, self.id, self.value, bytes_to_strhex(self.raw, " "))
write!(
f,
"{{ \"type\": \"{}\", \"channel\": {}, \"id\": {}, \"value\": {}, \"raw\": \"{}\" }}",
self.r#type,
self.channel,
self.id,
self.value,
bytes_to_strhex(self.raw, " ")
)
}
}
@ -157,11 +158,10 @@ impl Into<EventBuf> for Event<'_> {
impl From<u8> for EventType {
fn from(v: u8) -> Self {
if ! (0b1000..=0b1111).contains(&v) {
if !(0b1000..=0b1111).contains(&v) {
// not in defined space: unknown
EventType::Unknown
}
else {
} else {
// safe since all valid cases are defined
unsafe { std::mem::transmute(v) }
}
@ -195,19 +195,23 @@ impl<'a> Event<'a> {
event_to_key(self.r#type, self.channel, self.id)
}
pub fn make_env(&self, remap: Option<&Remapper<f64>>, float: bool) -> Result<EventEnv, Error>
{
pub fn make_env(&self, remap: Option<&Remapper<f64>>, float: bool) -> Result<EventEnv, Error> {
Ok(EventEnv {
channel: self.channel.to_string(),
id: self.id.to_string(),
rawvalue: self.value.to_string(),
raw: bytes_to_strhex(self.raw, " "),
timestamp: self.timestamp.unwrap_or(SystemTime::now()).duration_since(SystemTime::UNIX_EPOCH)?.as_secs_f64().to_string(),
value: match (remap,float) {
(Some(r),true) => r.remap(self.value as f64).to_string(),
(Some(r),false) => r.remap_to::<i64>(self.value as f64).unwrap().to_string(),
timestamp: self
.timestamp
.unwrap_or(SystemTime::now())
.duration_since(SystemTime::UNIX_EPOCH)?
.as_secs_f64()
.to_string(),
value: match (remap, float) {
(Some(r), true) => r.remap(self.value as f64).to_string(),
(Some(r), false) => r.remap_to::<i64>(self.value as f64).unwrap().to_string(),
_ => self.value.to_string(),
}
},
})
}
}
@ -218,20 +222,25 @@ impl<'a> From<&'a [u8]> for Event<'a> {
eprintln!("warning: empty signal");
return Default::default();
}
let event_type = EventType::from(v[0]/16);
let channel = if event_type.has_channel() { v[0]%16 } else { 0 };
let event_type = EventType::from(v[0] / 16);
let channel = if event_type.has_channel() {
v[0] % 16
} else {
0
};
let (id, value) = match event_type {
EventType::PitchBend => {
(0, (v[2] as u16)*256 + (v[1] as u16) )
},
EventType::PitchBend => (0, (v[2] as u16) * 256 + (v[1] as u16)),
EventType::Unknown => {
eprintln!("warning: unknown signal type: {}", v[0]);
(0,0)
(0, 0)
}
EventType::System => (0,0),
EventType::ChannelPressure => (0,v[1] as u16),
EventType::ProgramChange => (v[1],0),
EventType::NoteOn | EventType::NoteOff | EventType::PolyphonicKeyPressure | EventType::Controller => (v[1],(v[2] as u16)),
EventType::System => (0, 0),
EventType::ChannelPressure => (0, v[1] as u16),
EventType::ProgramChange => (v[1], 0),
EventType::NoteOn
| EventType::NoteOff
| EventType::PolyphonicKeyPressure
| EventType::Controller => (v[1], (v[2] as u16)),
};
Event {
r#type: event_type,
@ -245,19 +254,41 @@ impl<'a> From<&'a [u8]> for Event<'a> {
}
impl EventEnv {
pub fn to_map(self, m: Option<&EventEnvMap>) -> HashMap<&str,String> {
pub fn to_map(self, m: Option<&EventEnvMap>) -> HashMap<&str, String> {
let mut r = HashMap::new();
let keys: EventEnvRef = match m {
Some(v) => {
EventEnvRef {
channel: v.channel.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.channel),
id: v.id.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.id),
raw: v.raw.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.raw),
rawvalue: v.rawvalue.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.rawvalue),
timestamp: v.timestamp.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.timestamp),
value: v.value.as_ref().map(|x| &x[..]).unwrap_or(EVENT_ENV_DEFAULT.value),
}
}
Some(v) => EventEnvRef {
channel: v
.channel
.as_ref()
.map(|x| &x[..])
.unwrap_or(EVENT_ENV_DEFAULT.channel),
id: v
.id
.as_ref()
.map(|x| &x[..])
.unwrap_or(EVENT_ENV_DEFAULT.id),
raw: v
.raw
.as_ref()
.map(|x| &x[..])
.unwrap_or(EVENT_ENV_DEFAULT.raw),
rawvalue: v
.rawvalue
.as_ref()
.map(|x| &x[..])
.unwrap_or(EVENT_ENV_DEFAULT.rawvalue),
timestamp: v
.timestamp
.as_ref()
.map(|x| &x[..])
.unwrap_or(EVENT_ENV_DEFAULT.timestamp),
value: v
.value
.as_ref()
.map(|x| &x[..])
.unwrap_or(EVENT_ENV_DEFAULT.value),
},
_ => EVENT_ENV_DEFAULT.clone(),
};
r.insert(keys.channel, self.channel);

View file

@ -1,24 +1,27 @@
use std::collections::HashMap;
use crate::config::{EventConfig,DeviceConfig};
use crate::event::{EventType,Event};
use crate::config::{DeviceConfig, EventConfig};
use crate::event::{Event, EventType};
use crate::Error;
#[derive(Debug,Default)]
#[derive(Debug, Default)]
pub struct EventMap<'a> {
pub map: HashMap<u32, Vec<&'a EventConfig>>,
}
fn event_to_key(r#type: EventType, channel: u8, id: u8) -> u32 {
(r#type as u32)*256*256 + (channel as u32)*256 + (id as u32)
(r#type as u32) * 256 * 256 + (channel as u32) * 256 + (id as u32)
}
pub fn count_events(events: &[EventConfig]) -> usize {
events.iter().map(|x| {
let nchannel = x.channel.len();
let nid = x.id.len();
nchannel * nid
}).sum()
events
.iter()
.map(|x| {
let nchannel = x.channel.len();
let nid = x.id.len();
nchannel * nid
})
.sum()
}
impl<'a> EventMap<'a> {
@ -29,8 +32,7 @@ impl<'a> EventMap<'a> {
let key = event_to_key(event.r#type, channel, id);
if let Some(v) = self.map.get_mut(&key) {
v.push(event);
}
else {
} else {
self.map.insert(key, Vec::from([event]));
}
}
@ -38,13 +40,15 @@ impl<'a> EventMap<'a> {
}
}
pub fn run_event(&self, event: &Event) -> Result<(), Error > {
pub fn run_event(&self, event: &Event) -> Result<(), Error> {
let key = event_to_key(event.r#type, event.channel, event.id);
if let Some(v) = self.map.get(&key) {
for ev in v {
if ev.match_value(event) {
for r in &ev.run {
let env = event.make_env(ev.remap.as_ref(), ev.float )?.to_map(r.envconf.as_ref());
let env = event
.make_env(ev.remap.as_ref(), ev.float)?
.to_map(r.envconf.as_ref());
r.run(env)?;
}
}
@ -58,12 +62,13 @@ impl<'a> From<&'a [EventConfig]> for EventMap<'a> {
fn from(events: &'a [EventConfig]) -> Self {
// init hashmap with size for optimizing
let size = count_events(events);
let mut ret = EventMap { map: HashMap::with_capacity(size) };
let mut ret = EventMap {
map: HashMap::with_capacity(size),
};
// insert references
ret.add_events(events);
ret
}
}
impl<'a> From<&'a DeviceConfig> for EventMap<'a> {
@ -71,12 +76,13 @@ impl<'a> From<&'a DeviceConfig> for EventMap<'a> {
// init hashmap with size for optimizing
let size = count_events(device.events.as_ref().map(|x| &x[..]).unwrap_or(&[]));
//let size = events.iter().map(|x| x.channels.len()*x.ids.len() ).sum();
let mut ret = EventMap { map: HashMap::with_capacity(size) };
let mut ret = EventMap {
map: HashMap::with_capacity(size),
};
// insert references
if let Some(x) = device.events.as_ref() {
ret.add_events(x);
}
ret
}
}

View file

@ -1,15 +1,12 @@
#[macro_use]
extern crate enum_display_derive;
pub mod cli;
pub mod config;
pub mod run;
pub mod constant;
pub mod error;
pub mod event;
pub mod eventmap;
pub mod midi;
pub mod run;
pub mod util;
pub mod cli;
pub mod error;
pub mod constant;
type Error = error::Error;
@ -17,33 +14,27 @@ use std::path::Path;
use clap::Parser;
use config::Config;
use cli::Cli;
use config::Config;
use midi::MidiHandler;
fn main() {
let c = Cli::parse();
if c.list {
let mut handler = err_handle(MidiHandler::new(constant::CLIENT_NAME));
err_handle(
handler.builder_handler(run::ListDevicesBuilder, ())
);
err_handle(handler.builder_handler(run::ListDevicesBuilder, ()));
return;
}
let map_file = err_handle(
c.map_file.ok_or(Error::NoArgument)
);
let map_file = err_handle(c.map_file.ok_or(Error::NoArgument));
loop {
err_handle(
run_file(&map_file)
);
err_handle(run_file(&map_file));
}
}
fn err_handle<T,E>(r: Result<T, E>) -> T
fn err_handle<T, E>(r: Result<T, E>) -> T
where
E: std::fmt::Display
E: std::fmt::Display,
{
match r {
Ok(v) => v,
@ -56,7 +47,7 @@ where
fn run_file(filepath: &Path) -> Result<(), Error> {
println!("Load file {}", filepath.to_str().unwrap_or("<unknown>"));
let dat = std::fs::read( filepath )?;
let dat = std::fs::read(filepath)?;
let conf = Config::try_from(&dat[..])?;
let mut handler = match conf.driver {
Some(v) => MidiHandler::new_with_driver(constant::CLIENT_NAME, v),

View file

@ -1,21 +1,24 @@
extern crate libc;
extern crate alsa;
extern crate libc;
use std::ffi::{CStr, CString};
use std::str::FromStr;
use std::{mem, thread};
use std::ffi::{CString, CStr};
use std::time::SystemTime;
use std::sync::mpsc;
use std::time::SystemTime;
use std::{mem, thread};
use crate::midi::{MidiInput,MidiPort,PortFilter};
use crate::error::Error;
use crate::midi::{MidiInput, MidiPort, PortFilter};
use crate::util::InternalTryFrom;
use alsa::{Seq, Direction};
use alsa::seq::{ClientIter, PortIter, MidiEvent, PortInfo, PortSubscribe, Addr, QueueTempo, EventType, PortCap, PortType};
use alsa::seq::{
Addr, ClientIter, EventType, MidiEvent, PortCap, PortInfo, PortIter, PortSubscribe, PortType,
QueueTempo,
};
use alsa::{Direction, Seq};
use thiserror::Error;
#[derive(Debug,Clone,PartialEq,Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DeviceAddr(Addr);
const ANNOUNCE_ADDR: &str = "System:Announce";
@ -39,15 +42,16 @@ impl FromStr for DeviceAddr {
// todo!()
// let mut osep = ;
if let Some(sep) = s.find(':') {
let (p1,p2) = (&s[..sep], &s[sep+1..] );
Ok(DeviceAddr(
Addr {
client: p1.parse().map_err(|_| AlsaError::AddrParse(s.to_string()))?,
port: p2.parse().map_err(|_| AlsaError::AddrParse(s.to_string()))?,
}
))
}
else {
let (p1, p2) = (&s[..sep], &s[sep + 1..]);
Ok(DeviceAddr(Addr {
client: p1
.parse()
.map_err(|_| AlsaError::AddrParse(s.to_string()))?,
port: p2
.parse()
.map_err(|_| AlsaError::AddrParse(s.to_string()))?,
}))
} else {
Err(AlsaError::AddrParse(s.to_string()))
}
}
@ -72,7 +76,8 @@ impl DeviceAddr {
}
pub fn get_ports(s: &Seq, capability: PortCap) -> Vec<PortInfo> {
ClientIter::new(s).flat_map(|c| PortIter::new(s, c.get_client()))
ClientIter::new(s)
.flat_map(|c| PortIter::new(s, c.get_client()))
.filter(|p| p.get_capability().contains(capability))
.collect()
}
@ -83,7 +88,7 @@ mod helpers {
}
}
#[derive(Error,Debug)]
#[derive(Error, Debug)]
pub enum AlsaError {
#[error(transparent)]
ALSA(#[from] alsa::Error),
@ -99,7 +104,7 @@ pub struct MidiInputAlsa {
subscription: Option<PortSubscribe>,
connect_addr: Option<Addr>,
start_time: Option<SystemTime>,
stop_trigger: [i32;2],
stop_trigger: [i32; 2],
}
impl Drop for MidiInputAlsa {
@ -119,10 +124,11 @@ impl MidiInputAlsa {
}
}
fn init_queue(&mut self) -> Result<i32, alsa::Error> {
// Create the input queue
let queue_id = self.seq.alloc_named_queue(unsafe { CStr::from_bytes_with_nul_unchecked(b"midir queue\0") })?;
let queue_id = self
.seq
.alloc_named_queue(unsafe { CStr::from_bytes_with_nul_unchecked(b"midir queue\0") })?;
// Set arbitrary tempo (mm=100) and resolution (240)
let qtempo = QueueTempo::empty()?;
qtempo.set_tempo(600_000);
@ -130,7 +136,6 @@ impl MidiInputAlsa {
self.seq.set_queue_tempo(queue_id, &qtempo)?;
let _ = self.seq.drain_output();
Ok(queue_id)
}
@ -159,18 +164,20 @@ impl MidiInputAlsa {
}
}
fn close_internal(&mut self)
{
fn close_internal(&mut self) {
if let Some(ref subscription) = self.subscription {
let _ = self.seq.unsubscribe_port(subscription.get_sender(), subscription.get_dest());
let _ = self
.seq
.unsubscribe_port(subscription.get_sender(), subscription.get_dest());
}
// Stop and free the input queue
let _ = self.seq.control_queue(self.queue_id, EventType::Stop, 0, None);
let _ = self
.seq
.control_queue(self.queue_id, EventType::Stop, 0, None);
let _ = self.seq.drain_output();
let _ = self.seq.free_queue(self.queue_id);
for fd in self.stop_trigger {
if fd >= 0 {
unsafe { self::libc::close(fd) };
@ -179,16 +186,24 @@ impl MidiInputAlsa {
}
fn signal_stop_input_internal(stop_trigger: i32) -> Result<(), Error> {
if unsafe { self::libc::write(stop_trigger, &false as *const bool as *const _, mem::size_of::<bool>() as self::libc::size_t) } == -1 {
if unsafe {
self::libc::write(
stop_trigger,
&false as *const bool as *const _,
mem::size_of::<bool>() as self::libc::size_t,
)
} == -1
{
Err(Error::Pipe)
}
else {
} else {
Ok(())
}
}
fn alsa_input_handler<F, D>(&mut self, callback: F, mut userdata: D) -> Result<(), Error>
where F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error> {
where
F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error>,
{
// fd defitions
use self::alsa::PollDescriptors;
use self::libc::pollfd;
@ -202,7 +217,7 @@ impl MidiInputAlsa {
// make poll fds
let poll_desc_info = (&self.seq, Some(Direction::Capture));
let mut poll_fds = vec![INVALID_POLLFD; poll_desc_info.count()+1];
let mut poll_fds = vec![INVALID_POLLFD; poll_desc_info.count() + 1];
poll_fds[0] = pollfd {
fd: self.stop_trigger[0],
events: self::libc::POLLIN,
@ -217,7 +232,13 @@ impl MidiInputAlsa {
// Read stop event from triggerer
if poll_fds[0].revents & self::libc::POLLIN != 0 {
let mut pollread = false;
let _res = unsafe { self::libc::read(poll_fds[0].fd, &mut pollread as *mut bool as *mut libc::c_void, mem::size_of::<bool>() as self::libc::size_t) };
let _res = unsafe {
self::libc::read(
poll_fds[0].fd,
&mut pollread as *mut bool as *mut libc::c_void,
mem::size_of::<bool>() as self::libc::size_t,
)
};
if !pollread {
break;
}
@ -243,7 +264,7 @@ impl MidiInputAlsa {
Err(e) => {
eprintln!("ALSA CALLBACK ERROR: {}", e);
eprintln!("continuing execution");
},
}
}
}
Ok(())
@ -251,13 +272,13 @@ impl MidiInputAlsa {
fn binary_input_handler<F, D>(&mut self, callback: F, userdata: D) -> Result<(), Error>
where
F: Fn(&Self, Option<SystemTime>, &[u8], &mut D) -> Result<(),Error> + Send
F: Fn(&Self, Option<SystemTime>, &[u8], &mut D) -> Result<(), Error> + Send,
{
let decoder = MidiEvent::new(0)?;
decoder.enable_running_status(false);
let message = vec!();
let buffer: [u8;12] = [0;12];
let message = vec![];
let buffer: [u8; 12] = [0; 12];
let continue_sysex = false;
let ts = match self.start_time.as_ref() {
@ -265,48 +286,61 @@ impl MidiInputAlsa {
_ => SystemTime::now(),
};
self.alsa_input_handler(|s, mut ev, (message, buffer, continue_sysex, userdata)| {
if !*continue_sysex { message.clear() }
let do_decode = match ev.get_type() {
EventType::PortSubscribed |
EventType::PortUnsubscribed |
EventType::Qframe |
EventType::Tick |
EventType::Clock |
EventType::Sensing => false,
EventType::Sysex => {
message.extend_from_slice(ev.get_ext().unwrap());
*continue_sysex = *message.last().unwrap() != 0xF7;
false
self.alsa_input_handler(
|s, mut ev, (message, buffer, continue_sysex, userdata)| {
if !*continue_sysex {
message.clear()
}
_ => true
};
// NOTE: SysEx messages have already been "decoded" at this point!
if do_decode {
let nbytes = decoder.decode(buffer, &mut ev).map_err(|_| AlsaError::Decode)?;
if nbytes > 0 {
message.extend_from_slice(&buffer[0..nbytes+1]);
let do_decode = match ev.get_type() {
EventType::PortSubscribed
| EventType::PortUnsubscribed
| EventType::Qframe
| EventType::Tick
| EventType::Clock
| EventType::Sensing => false,
EventType::Sysex => {
message.extend_from_slice(ev.get_ext().unwrap());
*continue_sysex = *message.last().unwrap() != 0xF7;
false
}
_ => true,
};
// NOTE: SysEx messages have already been "decoded" at this point!
if do_decode {
let nbytes = decoder
.decode(buffer, &mut ev)
.map_err(|_| AlsaError::Decode)?;
if nbytes > 0 {
message.extend_from_slice(&buffer[0..nbytes + 1]);
}
}
}
if message.is_empty() || *continue_sysex { return Ok(false); }
if message.is_empty() || *continue_sysex {
return Ok(false);
}
let ts: Option<SystemTime> = ev.get_time().map(|v| ts+v);
(callback)(s, ts, message, userdata)?;
Ok(false)
}
, (message, buffer, continue_sysex, userdata))?;
let ts: Option<SystemTime> = ev.get_time().map(|v| ts + v);
(callback)(s, ts, message, userdata)?;
Ok(false)
},
(message, buffer, continue_sysex, userdata),
)?;
Ok(())
}
fn threaded_alsa_input<F, D>(&mut self, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(),Error>
fn threaded_alsa_input<F, D>(
&mut self,
callback: F,
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
userdata: D,
) -> Result<(), Error>
where
F: Fn(&Self, alsa::seq::Event, &mut D) -> Result<bool, Error> + Send,
D: Send,
{
thread::scope( |sc| -> Result<(), Error> {
thread::scope(|sc| -> Result<(), Error> {
let stop_trigger = self.stop_trigger[1];
let t = sc.spawn(move || -> Result<(), Error> {
let userdata = userdata;
@ -316,20 +350,26 @@ impl MidiInputAlsa {
});
match rs.recv()? {
true => Self::signal_stop_input_internal(stop_trigger)?,
false => ()
false => (),
};
t.join().expect("unexpected thread error")?;
Ok(())
})
}
fn threaded_handler<H, F, T, RF: ?Sized, D, R>(&mut self, handler: H, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(),Error>
fn threaded_handler<H, F, T, RF: ?Sized, D, R>(
&mut self,
handler: H,
callback: F,
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
userdata: D,
) -> Result<(), Error>
where
H: Fn(&mut Self, F,D) -> Result<(), Error> + Send,
F: Fn(&Self, T, &RF, &mut D) -> Result<R,Error> + Send,
H: Fn(&mut Self, F, D) -> Result<(), Error> + Send,
F: Fn(&Self, T, &RF, &mut D) -> Result<R, Error> + Send,
D: Send,
{
thread::scope( |sc| -> Result<(), Error> {
thread::scope(|sc| -> Result<(), Error> {
let stop_trigger = self.stop_trigger[1];
let t = sc.spawn(move || -> Result<(), Error> {
let userdata = userdata;
@ -339,7 +379,7 @@ impl MidiInputAlsa {
});
match rs.recv()? {
true => Self::signal_stop_input_internal(stop_trigger)?,
false => ()
false => (),
};
t.join().expect("unexpected thread error")?;
Ok(())
@ -361,7 +401,7 @@ impl MidiInput for MidiInputAlsa {
subscription: None,
connect_addr: None,
start_time: None,
stop_trigger: [-1,-1],
stop_trigger: [-1, -1],
})
}
@ -371,26 +411,29 @@ impl MidiInput for MidiInputAlsa {
}
fn ports(&self) -> Result<Vec<MidiPort<DeviceAddr>>, Error> {
get_ports(&self.seq, PortCap::READ | PortCap::SUBS_READ).iter().map(|x| -> Result<MidiPort<DeviceAddr>, Error> {
let cinfo = self.seq.get_any_client_info(x.get_client())?;
Ok(MidiPort {
name: cinfo.get_name()?.to_string()+":"+x.get_name()?,
addr: x.addr().into(),
get_ports(&self.seq, PortCap::READ | PortCap::SUBS_READ)
.iter()
.map(|x| -> Result<MidiPort<DeviceAddr>, Error> {
let cinfo = self.seq.get_any_client_info(x.get_client())?;
Ok(MidiPort {
name: cinfo.get_name()?.to_string() + ":" + x.get_name()?,
addr: x.addr().into(),
})
})
}).collect()
.collect()
}
fn filter_ports(&self, mut ports: Vec<MidiPort<DeviceAddr>>, filter: PortFilter<Self::DeviceAddr>) -> Vec<MidiPort<DeviceAddr>> {
ports.retain(
|p| {
match &filter {
PortFilter::All => true,
PortFilter::Name(s) => p.name.contains(s),
PortFilter::Regex(s) => s.is_match(&p.name),
PortFilter::Addr(s) => p.addr == *s,
}
}
);
fn filter_ports(
&self,
mut ports: Vec<MidiPort<DeviceAddr>>,
filter: PortFilter<Self::DeviceAddr>,
) -> Vec<MidiPort<DeviceAddr>> {
ports.retain(|p| match &filter {
PortFilter::All => true,
PortFilter::Name(s) => p.name.contains(s),
PortFilter::Regex(s) => s.is_match(&p.name),
PortFilter::Addr(s) => p.addr == *s,
});
ports
}
@ -403,7 +446,10 @@ impl MidiInput for MidiInputAlsa {
let sub = PortSubscribe::empty()?;
sub.set_sender(src_pinfo.addr());
sub.set_dest(Addr { client: self.seq.client_id()?, port: vport});
sub.set_dest(Addr {
client: self.seq.client_id()?,
port: vport,
});
self.seq.subscribe_port(&sub)?;
self.subscription = Some(sub);
self.init_trigger()?;
@ -413,26 +459,35 @@ impl MidiInput for MidiInputAlsa {
Ok(())
}
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, (tss, rss): (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error> {
fn device_events(
&mut self,
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
(tss, rss): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
) -> Result<(), Error> {
let ports = self.ports()?;
let port = self.filter_ports(ports, PortFilter::Name(ANNOUNCE_ADDR.to_string()));
self.connect(&port[0].addr, CLIENT_NAME_ANNOUNCE)?;
self.threaded_alsa_input(move |s: &Self, ev: alsa::seq::Event, _| -> Result<bool, Error> {
// handle disconnect event on watched port
match ev.get_type() {
EventType::PortStart => {
if let Some(a) = ev.get_data::<alsa::seq::Addr>() {
let p = s.ports()?;
let pp = s.filter_ports(p, PortFilter::Addr( a.into() ) );
if !pp.is_empty() {
ts.send(Some(pp[0].clone())).expect("unexpected send() error");
}
};
Ok(false)
self.threaded_alsa_input(
move |s: &Self, ev: alsa::seq::Event, _| -> Result<bool, Error> {
// handle disconnect event on watched port
match ev.get_type() {
EventType::PortStart => {
if let Some(a) = ev.get_data::<alsa::seq::Addr>() {
let p = s.ports()?;
let pp = s.filter_ports(p, PortFilter::Addr(a.into()));
if !pp.is_empty() {
ts.send(Some(pp[0].clone()))
.expect("unexpected send() error");
}
};
Ok(false)
}
_ => Ok(false),
}
_ => Ok(false),
}
}, (tss, rss), ())?;
},
(tss, rss),
(),
)?;
self.close_internal();
Ok(())
}
@ -441,15 +496,24 @@ impl MidiInput for MidiInputAlsa {
Self::signal_stop_input_internal(self.stop_trigger[1])
}
fn handle_input<F, D>(&mut self, callback: F, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(), Error>
fn handle_input<F, D>(
&mut self,
callback: F,
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
userdata: D,
) -> Result<(), Error>
where
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
D: Send,
{
self.threaded_handler(Self::binary_input_handler,
|s, t, m, d| -> Result<(),Error> {
callback(s,m,t,d);
self.threaded_handler(
Self::binary_input_handler,
|s, t, m, d| -> Result<(), Error> {
callback(s, m, t, d);
Ok(())
}, (ts, rs), userdata)
},
(ts, rs),
userdata,
)
}
}

View file

@ -1,13 +1,12 @@
use crate::util::InternalTryFrom;
pub use super::input::{MidiInput,MidiInputHandler};
pub use super::input::{MidiInput, MidiInputHandler};
pub trait Builder<D, R> {
fn build<T>(&self) -> fn(&T, D) -> R
where
T: MidiInputHandler+MidiInput+Send+'static,
<T as MidiInputHandler>::DeviceAddr: std::fmt::Display+'static+InternalTryFrom<String>,
;
T: MidiInputHandler + MidiInput + Send + 'static,
<T as MidiInputHandler>::DeviceAddr: std::fmt::Display + 'static + InternalTryFrom<String>;
}
macro_rules! builder {
@ -16,12 +15,13 @@ macro_rules! builder {
impl Builder<$intype, $rettype> for $name {
fn build<T>(&self) -> fn(&T, $intype) -> $rettype
where
T: MidiInputHandler+Send+'static,
<T as MidiInputHandler>::DeviceAddr: std::fmt::Display+'static+InternalTryFrom<String>,
T: MidiInputHandler + Send + 'static,
<T as MidiInputHandler>::DeviceAddr:
std::fmt::Display + 'static + InternalTryFrom<String>,
{
$fct
}
}
};
}
pub(crate) use builder;
pub(crate) use builder;

View file

@ -1,6 +1,6 @@
use serde::Deserialize;
#[derive(Deserialize,Debug,Clone,Copy,Eq,PartialEq)]
#[derive(Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum MidiDriver {
ALSA,

View file

@ -1,57 +1,83 @@
use crate::util::InternalTryFrom;
use crate::{Error, constant};
use crate::config::DeviceConfig;
use crate::eventmap::EventMap;
use crate::event::{Event, EventBuf};
use crate::eventmap::EventMap;
use crate::util::InternalTryFrom;
use crate::{constant, Error};
use std::str::FromStr;
use std::sync::{mpsc, Arc, Mutex};
use std::thread;
use std::time::{SystemTime, Instant};
use std::sync::{mpsc, Mutex, Arc};
use std::time::{Instant, SystemTime};
use queues::{CircularBuffer, IsQueue};
use super::{PortFilter, MidiPort};
use super::{MidiPort, PortFilter};
pub trait MidiInput
where
<Self as MidiInput>::DeviceAddr: Clone+Send+FromStr,
<Self as MidiInput>::DeviceAddr: InternalTryFrom<std::string::String>
<Self as MidiInput>::DeviceAddr: Clone + Send + FromStr,
<Self as MidiInput>::DeviceAddr: InternalTryFrom<std::string::String>,
{
type DeviceAddr;
fn new(client_name: &str) -> Result<Self, Error>
where Self: Sized;
where
Self: Sized;
fn close(self) -> Result<(), Error>;
fn ports(&self) -> Result<Vec<MidiPort<Self::DeviceAddr>>, Error>;
fn filter_ports(&self, ports: Vec<MidiPort<Self::DeviceAddr>>, filter: PortFilter<Self::DeviceAddr>) -> Vec<MidiPort<Self::DeviceAddr>>;
fn filter_ports(
&self,
ports: Vec<MidiPort<Self::DeviceAddr>>,
filter: PortFilter<Self::DeviceAddr>,
) -> Vec<MidiPort<Self::DeviceAddr>>;
fn connect(&mut self, port_addr: &Self::DeviceAddr, port_name: &str) -> Result<(), Error>;
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error>;
fn device_events(
&mut self,
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
) -> Result<(), Error>;
fn signal_stop_input(&self) -> Result<(), Error>;
fn handle_input<F, D>(&mut self, callback: F, rts: (mpsc::Sender<bool>, mpsc::Receiver<bool>), userdata: D) -> Result<(), Error>
fn handle_input<F, D>(
&mut self,
callback: F,
rts: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
userdata: D,
) -> Result<(), Error>
where
F: Fn(&Self, &[u8], Option<SystemTime>, &mut D) + Send + Sync,
D: Send,
;
D: Send;
}
pub trait MidiInputHandler
pub trait MidiInputHandler
where
Self: Sized,
<Self as MidiInputHandler>::DeviceAddr: Clone+Send+FromStr,
<Self as MidiInputHandler>::DeviceAddr: Clone + Send + FromStr,
{
type DeviceAddr;
fn new(client_name: &str) -> Result<Self, Error>;
fn ports(&self) -> Result<Vec<MidiPort<Self::DeviceAddr>>, Error>;
fn try_connect(&self, port: MidiPort<Self::DeviceAddr>, filter: PortFilter<Self::DeviceAddr> ) -> Result<Option<Self>, Error>;
fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, trs: (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error>;
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, ss: (mpsc::Sender<bool>,mpsc::Receiver<bool>)) -> Result<(), Error>;
fn try_connect(
&self,
port: MidiPort<Self::DeviceAddr>,
filter: PortFilter<Self::DeviceAddr>,
) -> Result<Option<Self>, Error>;
fn run(
&mut self,
conf: &DeviceConfig,
eventmap: &EventMap,
trs: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
) -> Result<(), Error>;
fn device_events(
&mut self,
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
) -> Result<(), Error>;
}
// Generic implementation
@ -62,7 +88,6 @@ where
<T as MidiInput>::DeviceAddr: FromStr,
{
type DeviceAddr = T::DeviceAddr;
fn new(client_name: &str) -> Result<Self, Error> {
MidiInput::new(client_name)
@ -72,7 +97,11 @@ where
MidiInput::ports(self)
}
fn try_connect(&self, port: MidiPort<Self::DeviceAddr>, filter: PortFilter<T::DeviceAddr> ) -> Result<Option<Self>, Error> {
fn try_connect(
&self,
port: MidiPort<Self::DeviceAddr>,
filter: PortFilter<T::DeviceAddr>,
) -> Result<Option<Self>, Error> {
let portmap = self.ports()?;
let pv = self.filter_ports(portmap, PortFilter::Addr(port.addr));
let pv = self.filter_ports(pv, filter);
@ -81,35 +110,44 @@ where
let mut v = T::new(constant::CLIENT_NAME_HANDLER)?;
v.connect(&port.addr, constant::CLIENT_NAME_HANDLER)?;
Ok(Some(v))
}
else {
} else {
Ok(None)
}
}
fn device_events(&mut self, ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>, ss: (mpsc::Sender<bool>,mpsc::Receiver<bool>)) -> Result<(), Error> {
fn device_events(
&mut self,
ts: mpsc::Sender<Option<MidiPort<Self::DeviceAddr>>>,
ss: (mpsc::Sender<bool>, mpsc::Receiver<bool>),
) -> Result<(), Error> {
self.device_events(ts, ss)
}
fn run(&mut self, conf: &DeviceConfig, eventmap: &EventMap, (ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>)) -> Result<(), Error> {
fn run(
&mut self,
conf: &DeviceConfig,
eventmap: &EventMap,
(ts, rs): (mpsc::Sender<bool>, mpsc::Receiver<bool>),
) -> Result<(), Error> {
thread::scope(|s| -> Result<(), Error> {
// parking signal for runner, true = stop
let (pts,prs) = mpsc::channel::<bool>();
let (pts, prs) = mpsc::channel::<bool>();
// event queue populated by the main thread and consumed by the exec thread
let evq = Arc::new(Mutex::new(CircularBuffer::<EventBuf>::new(conf.queue_length)));
let evq = Arc::new(Mutex::new(CircularBuffer::<EventBuf>::new(
conf.queue_length,
)));
// background execution loop
let rq = evq.clone();
let exec_thread = s.spawn(move || -> Result<(),Error> {
let exec_thread = s.spawn(move || -> Result<(), Error> {
loop {
if prs.recv()? {
break;
}
loop {
// nest the lock into a scope to release it before run
let (ev,start): (EventBuf,Instant) = {
let (ev, start): (EventBuf, Instant) = {
let mut evq = rq.lock().unwrap();
if evq.size() > 0 {
(evq.remove().unwrap(), Instant::now())
@ -117,7 +155,9 @@ where
break;
}
};
eventmap.run_event(&ev.as_event()).unwrap_or_else(|e| eprintln!("ERROR: error on run: {}", e) );
eventmap
.run_event(&ev.as_event())
.unwrap_or_else(|e| eprintln!("ERROR: error on run: {}", e));
// wait until interval has been reached
let elapsed_time = start.elapsed();
if elapsed_time < conf.interval {
@ -127,23 +167,26 @@ where
}
Ok(())
});
self.handle_input(|_,m,t,(evq,pts)| {
let mut event: EventBuf = Event::from(m).into();
event.timestamp = t;
if conf.log {
println!("{}: event: {}", constant::CLIENT_NAME, event);
}
let mut evq = evq.lock().unwrap();
evq.add(event).unwrap();
pts.send(false).expect("unexpected write error");
}, (ts,rs), (evq,pts.clone()))?;
self.handle_input(
|_, m, t, (evq, pts)| {
let mut event: EventBuf = Event::from(m).into();
event.timestamp = t;
if conf.log {
println!("{}: event: {}", constant::CLIENT_NAME, event);
}
let mut evq = evq.lock().unwrap();
evq.add(event).unwrap();
pts.send(false).expect("unexpected write error");
},
(ts, rs),
(evq, pts.clone()),
)?;
pts.send(true).expect("unexpected write error");
let _ = exec_thread.join();
Ok(())
})?;
Ok(())
}

View file

@ -1,20 +1,20 @@
pub mod backend;
pub mod port;
pub mod portfilter;
pub mod input;
pub mod builder;
pub mod driver;
pub mod input;
pub mod port;
pub mod portfilter;
use crate::Error;
extern crate libc;
pub use driver::MidiDriver;
pub use builder::Builder;
pub use driver::MidiDriver;
pub use input::{MidiInput, MidiInputHandler};
pub use port::MidiPort;
pub use portfilter::PortFilter;
pub use input::{MidiInput,MidiInputHandler};
pub enum MidiHandler {
ALSA(backend::MidiInputAlsa),
@ -35,7 +35,7 @@ impl MidiHandler {
// wrap generic functions into builder because functions with generic traits cannot be passed as arguments
pub fn builder_handler<B, D, R>(&mut self, builder: B, data: D) -> R
where
B: Builder<D,R>,
B: Builder<D, R>,
D: Send,
{
match self {

View file

@ -1,21 +1,19 @@
use std::fmt::{Display, Formatter};
#[derive(Eq,PartialEq,Debug,Clone)]
#[derive(Eq, PartialEq, Debug, Clone)]
pub struct MidiPort<T>
where
T: Clone
T: Clone,
{
pub name: String,
pub addr: T,
}
impl<T> Display for MidiPort<T>
where
T: Display+Clone
T: Display + Clone,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}\t{}", self.addr, self.name)
}
}
}

View file

@ -1,18 +1,16 @@
use crate::Error;
use crate::config::DeviceConfig;
use crate::config::device::Identifier;
use crate::config::DeviceConfig;
use crate::util::InternalTryFrom;
use crate::Error;
#[derive(Debug,Clone)]
pub enum PortFilter<T>{
#[derive(Debug, Clone)]
pub enum PortFilter<T> {
All,
Name(String),
Regex(regex::Regex),
Addr(T),
}
impl<T> InternalTryFrom<&DeviceConfig> for PortFilter<T>
where
T: InternalTryFrom<String>,
@ -25,4 +23,4 @@ where
Identifier::Addr(s) => PortFilter::Addr(T::i_try_from(s.to_string())?),
})
}
}
}

View file

@ -1,29 +1,36 @@
use std::sync::mpsc;
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::{Mutex,Arc};
use libc::SIGUSR1;
use signal_hook::iterator::Signals;
use crate::util::InternalTryFrom;
use crate::{Error, constant};
use crate::midi::{PortFilter,MidiInputHandler, MidiPort, Builder};
use crate::config::{Config,DeviceConfig};
use crate::config::{Config, DeviceConfig};
use crate::eventmap::EventMap;
use crate::midi::builder::builder;
use crate::midi::{Builder, MidiInputHandler, MidiPort, PortFilter};
use crate::util::InternalTryFrom;
use crate::{constant, Error};
type DeviceRunItem<'a> = (&'a DeviceConfig, EventMap<'a>, Option<Arc<Mutex<(u32, u32)>>>);
type DeviceRunResult<'a> =(thread::ScopedJoinHandle<'a, Result<(), Error>>, mpsc::Sender<bool>);
type DeviceRunItem<'a> = (
&'a DeviceConfig,
EventMap<'a>,
Option<Arc<Mutex<(u32, u32)>>>,
);
type DeviceRunResult<'a> = (
thread::ScopedJoinHandle<'a, Result<(), Error>>,
mpsc::Sender<bool>,
);
pub fn cross_shell(cmd: &str) -> Vec<String> {
if cfg!(target_os = "windows") {
vec!("cmd", "/C", cmd)
vec!["cmd", "/C", cmd]
} else {
vec!("sh", "-c", cmd)
vec!["sh", "-c", cmd]
}
.iter().map(
|x| x.to_string()
).collect()
.iter()
.map(|x| x.to_string())
.collect()
}
builder!(ListDevicesBuilder, list_devices, (), Result<(), Error>);
@ -31,8 +38,8 @@ builder!(RunConfigBuilder, run_config, &Config, Result<(), Error>);
pub fn list_devices<T>(input: &T, _: ()) -> Result<(), Error>
where
T: MidiInputHandler+Send+'static,
<T as MidiInputHandler>::DeviceAddr: 'static+std::fmt::Display,
T: MidiInputHandler + Send + 'static,
<T as MidiInputHandler>::DeviceAddr: 'static + std::fmt::Display,
{
let ports = MidiInputHandler::ports(input)?;
println!(" Addr\t Name");
@ -44,17 +51,23 @@ where
pub fn run_config<T>(input: &T, conf: &Config) -> Result<(), Error>
where
T: MidiInputHandler+Send+'static,
<T as MidiInputHandler>::DeviceAddr: 'static+std::fmt::Display+InternalTryFrom<String>,
T: MidiInputHandler + Send + 'static,
<T as MidiInputHandler>::DeviceAddr: 'static + std::fmt::Display + InternalTryFrom<String>,
{
let cfevmap: Vec<DeviceRunItem> = conf.devices.iter().map(|x|
(x, EventMap::from(x),
x.max_connections.map(|v| (Arc::new(Mutex::new((0,v)))))
)
).collect();
let cfevmap: Vec<DeviceRunItem> = conf
.devices
.iter()
.map(|x| {
(
x,
EventMap::from(x),
x.max_connections.map(|v| (Arc::new(Mutex::new((0, v))))),
)
})
.collect();
let (tdev,rdev) = mpsc::channel::<Option<MidiPort<T::DeviceAddr>>>();
let (tsd,rsd) = mpsc::channel::<bool>();
let (tdev, rdev) = mpsc::channel::<Option<MidiPort<T::DeviceAddr>>>();
let (tsd, rsd) = mpsc::channel::<bool>();
let ntsd = tsd.clone();
let ntdev = tdev.clone();
@ -77,12 +90,14 @@ where
let mut threads: Vec<DeviceRunResult> = Vec::new();
let ports = input.ports()?;
for p in ports {
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? { threads.push(v) }
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? {
threads.push(v)
}
}
let event_thread = s.spawn(move || {
let mut input = T::new(constant::CLIENT_NAME_EVENT).unwrap();
let r = input.device_events(tdev.clone(), (tsd,rsd));
let r = input.device_events(tdev.clone(), (tsd, rsd));
tdev.send(None).unwrap();
r
});
@ -96,12 +111,17 @@ where
if conf.log {
println!("{}: device connect: {}", constant::CLIENT_NAME, p);
}
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? { threads.push(v) }
};
if let Some(v) = try_connect_process(input, s, &p, &cfevmap)? {
threads.push(v)
}
}
event_thread.join().unwrap()?;
for (thread,ss) in threads {
for (thread, ss) in threads {
let _ = ss.send(true);
let _ = thread.join().unwrap().map_err(|e| eprintln!("WARN: error in thread: {}", e));
let _ = thread
.join()
.unwrap()
.map_err(|e| eprintln!("WARN: error in thread: {}", e));
}
Ok(())
})?;
@ -112,12 +132,11 @@ fn try_connect_process<'a, T>(
input: &T,
s: &'a thread::Scope<'a, '_>,
p: &MidiPort<T::DeviceAddr>,
cfevmap: &'a[DeviceRunItem<'a>],
)
-> Result<Option<DeviceRunResult<'a>>, Error>
cfevmap: &'a [DeviceRunItem<'a>],
) -> Result<Option<DeviceRunResult<'a>>, Error>
where
T: MidiInputHandler+Send+'static,
<T as MidiInputHandler>::DeviceAddr: 'static+InternalTryFrom<String>,
T: MidiInputHandler + Send + 'static,
<T as MidiInputHandler>::DeviceAddr: 'static + InternalTryFrom<String>,
{
for (dev, eventmap, counter) in cfevmap {
// device counter is full
@ -135,13 +154,13 @@ where
m.0 += 1;
}
// stop signal channel
let (sts,srs) = mpsc::channel::<bool>();
let (sts, srs) = mpsc::channel::<bool>();
let mm = counter.as_ref().map(Arc::clone);
let nsts = sts.clone();
let t = s.spawn( move || -> Result<(), Error> {
let t = s.spawn(move || -> Result<(), Error> {
dev.run_connect()?;
// blocking process
c.run(dev, eventmap, (nsts,srs))?;
c.run(dev, eventmap, (nsts, srs))?;
// decrease device counter
if let Some(m) = mm {
let mut m = m.lock().unwrap();

View file

@ -1,13 +1,11 @@
pub mod smartset;
pub mod range;
pub mod remap;
pub mod smartset;
pub type SmartSet<T> = smartset::SmartSet<T>;
pub type Range<T> = range::Range<T>;
pub type Remapper<T> = remap::Remapper<T>;
macro_rules! visit_from {
( $obj:ident , $( ($fct:ident, $type:ty) ),+ $(,)?) => {
$(
@ -23,21 +21,25 @@ macro_rules! visit_from {
pub(crate) use visit_from;
pub fn map_tryfrom<S,D>(v: Vec<S>) -> Result<Vec<D>, <D as TryFrom<S>>::Error>
pub fn map_tryfrom<S, D>(v: Vec<S>) -> Result<Vec<D>, <D as TryFrom<S>>::Error>
where
D: TryFrom<S>,
{
v.into_iter().map(|x| D::try_from(x)).collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()
v.into_iter()
.map(|x| D::try_from(x))
.collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()
}
pub fn map_opt_tryfrom<S,D>(v: Option<Vec<S>>) -> Result<Option<Vec<D>>, <D as TryFrom<S>>::Error>
pub fn map_opt_tryfrom<S, D>(v: Option<Vec<S>>) -> Result<Option<Vec<D>>, <D as TryFrom<S>>::Error>
where
D: TryFrom<S>,
{
match v {
Some(v) => {
Ok( Some(v.into_iter().map(|x| D::try_from(x)).collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()?) )
}
Some(v) => Ok(Some(
v.into_iter()
.map(|x| D::try_from(x))
.collect::<Result<Vec<D>, <D as TryFrom<S>>::Error>>()?,
)),
None => Ok(None),
}
}

View file

@ -1,36 +1,34 @@
use std::str::FromStr;
use std::cmp::PartialOrd;
use std::str::FromStr;
use num::{Num,NumCast};
use num::{Num, NumCast};
use super::visit_from;
// Trait aliases are unstable
//trait smartsetnum = T: Num+Copy + FromStr;
#[derive(Debug,Clone,Copy)]
#[derive(Debug, Clone, Copy)]
pub struct Range<T>
where
T: Num+Copy + FromStr + PartialOrd,
T: Num + Copy + FromStr + PartialOrd,
{
start: T,
end: T,
}
pub fn parse_range<T>(s: &str) -> Result<Range<T>, <T as FromStr>::Err>
where
T: Num+Copy + FromStr + PartialOrd,
T: Num + Copy + FromStr + PartialOrd,
{
let mut osep = s.find(':');
if osep.is_none() {
osep = s.find('-');
}
let r = if let Some(sep) = osep {
let (p1,p2) = (&s[..sep], &s[sep+1..] );
( p1.parse()?, p2.parse()? )
}
else {
let (p1, p2) = (&s[..sep], &s[sep + 1..]);
(p1.parse()?, p2.parse()?)
} else {
let n = s.parse()?;
(n, n)
};
@ -39,13 +37,10 @@ T: Num+Copy + FromStr + PartialOrd,
impl<T> Range<T>
where
T: Num+Copy + FromStr + PartialOrd,
T: Num + Copy + FromStr + PartialOrd,
{
pub fn new(start: T, end: T) -> Self {
Self {
start,
end,
}
Self { start, end }
}
pub fn start(&self) -> T {
@ -57,10 +52,10 @@ T: Num+Copy + FromStr + PartialOrd,
}
}
impl<T,U> From<U> for Range<T>
impl<T, U> From<U> for Range<T>
where
T: Num+Copy+NumCast + FromStr + PartialOrd,
U: Num+Copy+num::ToPrimitive + FromStr,
T: Num + Copy + NumCast + FromStr + PartialOrd,
U: Num + Copy + num::ToPrimitive + FromStr,
{
fn from(i: U) -> Self {
let ti: T = num::NumCast::from(i).unwrap();
@ -70,7 +65,7 @@ where
impl<T> FromStr for Range<T>
where
T: Num+Copy + FromStr + PartialOrd,
T: Num + Copy + FromStr + PartialOrd,
{
type Err = <T as FromStr>::Err;
@ -79,32 +74,31 @@ where
}
}
use std::marker::PhantomData;
use serde::de::{self, Deserialize, Deserializer, Visitor};
use std::fmt;
use serde::de::{self,Deserialize, Deserializer, Visitor};
use std::marker::PhantomData;
struct RangeVisitor<T>
where
T: Num+Copy + FromStr + PartialOrd,
T: Num + Copy + FromStr + PartialOrd,
{
marker: PhantomData<fn() -> Range<T>>
marker: PhantomData<fn() -> Range<T>>,
}
impl<T> RangeVisitor<T>
where
T: Num+Copy + FromStr + PartialOrd,
T: Num + Copy + FromStr + PartialOrd,
{
fn new() -> Self {
Self {
marker: PhantomData
marker: PhantomData,
}
}
}
impl<'de, T> Visitor<'de> for RangeVisitor<T>
where
T: Num+Copy+PartialOrd + FromStr +NumCast + Deserialize<'de> + std::fmt::Debug,
T: Num + Copy + PartialOrd + FromStr + NumCast + Deserialize<'de> + std::fmt::Debug,
<T as FromStr>::Err: std::fmt::Display,
{
type Value = Range<T>;
@ -113,7 +107,7 @@ where
formatter.write_str("a set of integer")
}
visit_from!{ Range ,
visit_from! { Range ,
(visit_i8, i8),
(visit_i16, i16),
(visit_i32, i32),
@ -135,7 +129,7 @@ where
// This is the trait that informs Serde how to deserialize MyMap.
impl<'de, T> Deserialize<'de> for Range<T>
where
T: Num+Copy+NumCast+PartialOrd + Deserialize<'de> + FromStr + std::fmt::Debug,
T: Num + Copy + NumCast + PartialOrd + Deserialize<'de> + FromStr + std::fmt::Debug,
<T as FromStr>::Err: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>

View file

@ -1,17 +1,25 @@
use num::{Num,NumCast,ToPrimitive, Bounded};
use std::str::FromStr;
use num::{Bounded, Num, NumCast, ToPrimitive};
use std::ops;
use std::str::FromStr;
use super::Range;
// Trait aliases are unstable
//trait remapnum = T: Num+ToPrimitive+Copy+PartialOrd + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul;
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct Remapper<T>
where
T: Num+ToPrimitive+Copy+PartialOrd+NumCast + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul,
T: Num
+ ToPrimitive
+ Copy
+ PartialOrd
+ NumCast
+ FromStr
+ ops::Add
+ ops::Sub
+ ops::Div
+ ops::Mul,
{
src: Range<T>,
dst: Range<T>,
@ -19,9 +27,18 @@ where
impl<T> Remapper<T>
where
T: Num+ToPrimitive+Copy+PartialOrd+NumCast + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul + std::fmt::Debug,
T: Num
+ ToPrimitive
+ Copy
+ PartialOrd
+ NumCast
+ FromStr
+ ops::Add
+ ops::Sub
+ ops::Div
+ ops::Mul
+ std::fmt::Debug,
{
pub fn src(&self) -> &Range<T> {
&self.src
}
@ -31,17 +48,13 @@ where
}
pub fn new(src: Range<T>, dst: Range<T>) -> Self {
Self {
src,
dst,
}
Self { src, dst }
}
pub fn remap(&self, v: T) -> T
{
pub fn remap(&self, v: T) -> T {
// compute actual value in source type
let r: T = (v-self.src.start())*(self.dst.end()-self.dst.start())
/ (self.src.end()-self.src.start())
let r: T = (v - self.src.start()) * (self.dst.end() - self.dst.start())
/ (self.src.end() - self.src.start())
+ self.dst.start();
r
}
@ -49,30 +62,39 @@ where
impl<T> Remapper<T>
where
T: Num+ToPrimitive+Copy+PartialOrd+NumCast + FromStr + ops::Add + ops::Sub + ops::Div + ops::Mul + std::fmt::Debug,
T: Num
+ ToPrimitive
+ Copy
+ PartialOrd
+ NumCast
+ FromStr
+ ops::Add
+ ops::Sub
+ ops::Div
+ ops::Mul
+ std::fmt::Debug,
{
pub fn remap_to<U>(&self, v: T) -> Option<U>
where
U: Num+NumCast+Copy+Bounded + FromStr,
U: Num + NumCast + Copy + Bounded + FromStr,
{
// compute actual value in source type
let r: T = self.remap(v);
// find min/max values of target type
let (rmin,rmax): (Option<T>,Option<T>) = (
let (rmin, rmax): (Option<T>, Option<T>) = (
num::NumCast::from(U::min_value()),
num::NumCast::from(U::max_value()),
);
// if target min/max can be casted onto source, bound output to those
match (rmin,rmax) {
(Some(min),Some(max)) => {
match (rmin, rmax) {
(Some(min), Some(max)) => {
if r >= max {
Some(U::max_value())
} else if r <= min {
Some(U::min_value())
}
else {
} else {
num::NumCast::from(r)
}
}

View file

@ -1,10 +1,9 @@
use std::collections::BTreeSet;
use std::str::FromStr;
use std::ops;
use std::str::FromStr;
use num::{Num,NumCast};
use num::{Num, NumCast};
use thiserror::Error;
@ -13,9 +12,8 @@ use thiserror::Error;
pub fn parse_int_set<T>(s: &str) -> Result<BTreeSet<T>, <T as std::str::FromStr>::Err>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
let mut r: BTreeSet<T> = BTreeSet::new();
let parts: Vec<&str> = s.split(',').collect();
for p in parts {
@ -25,11 +23,11 @@ where
osep = s.find('-');
}
if let Some(sep) = osep {
let (p1,p2) = (&s[..sep], &s[sep+1..] );
let (low,high): (T,T) = ( p1.parse()?, p2.parse()? );
let (mut low,high) = match low <= high {
true => (low,high),
false => (high,low),
let (p1, p2) = (&s[..sep], &s[sep + 1..]);
let (low, high): (T, T) = (p1.parse()?, p2.parse()?);
let (mut low, high) = match low <= high {
true => (low, high),
false => (high, low),
};
r.insert(low);
loop {
@ -39,8 +37,7 @@ where
}
low += T::one();
}
}
else {
} else {
r.insert(p.parse()?);
}
}
@ -48,17 +45,17 @@ where
Ok(r)
}
#[derive(Debug,Clone)]
#[derive(Debug, Clone)]
pub struct SmartSet<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
pub set: BTreeSet<T>
pub set: BTreeSet<T>,
}
impl<T> SmartSet<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
pub fn new() -> Self {
Self {
@ -77,7 +74,7 @@ where
impl<T> From<T> for SmartSet<T>
where
T: Num+Ord+Copy+NumCast + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + NumCast + std::str::FromStr + ops::AddAssign,
{
fn from(i: T) -> Self {
SmartSet {
@ -86,20 +83,20 @@ where
}
}
#[derive(Error,Debug)]
#[derive(Error, Debug)]
pub enum Error<T>
where
T: std::fmt::Display,
{
#[error("invalid type: `{0}`, expected {1}")]
Cast(T,String),
Cast(T, String),
#[error("unknown error")]
Unknown,
}
trait InternalTryFrom<T>
where
Self: Sized
Self: Sized,
{
type Error;
fn i_try_from(other: T) -> Result<Self, Self::Error>;
@ -107,19 +104,17 @@ where
impl<T, U> InternalTryFrom<U> for SmartSet<T>
where
T: Num+Ord+Copy+NumCast + std::str::FromStr + ops::AddAssign,
U: Num+Ord+Copy+num::ToPrimitive + std::str::FromStr + ops::AddAssign + std::fmt::Display,
T: Num + Ord + Copy + NumCast + std::str::FromStr + ops::AddAssign,
U: Num + Ord + Copy + num::ToPrimitive + std::str::FromStr + ops::AddAssign + std::fmt::Display,
{
type Error = Error<U>;
fn i_try_from(i: U) -> Result<Self, Self::Error> {
// let mut r = SmartSet::<T>::new();
match num::NumCast::from(i) {
Some(v) => {
Ok(SmartSet {
set: BTreeSet::from([v]),
})
}
_ => Err(Error::Cast(i, std::any::type_name::<T>().to_string()))
Some(v) => Ok(SmartSet {
set: BTreeSet::from([v]),
}),
_ => Err(Error::Cast(i, std::any::type_name::<T>().to_string())),
}
}
}
@ -141,7 +136,7 @@ where
impl<T> FromStr for SmartSet<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
type Err = <T as FromStr>::Err;
@ -154,7 +149,7 @@ where
impl<T> IntoIterator for SmartSet<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
type Item = T;
type IntoIter = std::collections::btree_set::IntoIter<Self::Item>;
@ -166,7 +161,7 @@ where
impl<'a, T> IntoIterator for &'a SmartSet<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
type Item = &'a T;
type IntoIter = std::collections::btree_set::Iter<'a, T>;
@ -176,25 +171,24 @@ where
}
}
use std::marker::PhantomData;
use serde::de::{self, Deserialize, Deserializer, Visitor};
use std::fmt;
use serde::de::{self,Deserialize, Deserializer, Visitor};
use std::marker::PhantomData;
struct SmartSetVisitor<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
marker: PhantomData<fn() -> SmartSet<T>>
marker: PhantomData<fn() -> SmartSet<T>>,
}
impl<T> SmartSetVisitor<T>
where
T: Num+Ord+Copy + std::str::FromStr + ops::AddAssign,
T: Num + Ord + Copy + std::str::FromStr + ops::AddAssign,
{
fn new() -> Self {
Self {
marker: PhantomData
marker: PhantomData,
}
}
}
@ -214,7 +208,14 @@ macro_rules! visit_from {
impl<'de, T> Visitor<'de> for SmartSetVisitor<T>
where
T: Num+Ord+Copy+NumCast + Deserialize<'de> + std::str::FromStr + ops::AddAssign + std::fmt::Debug,
T: Num
+ Ord
+ Copy
+ NumCast
+ Deserialize<'de>
+ std::str::FromStr
+ ops::AddAssign
+ std::fmt::Debug,
<T as FromStr>::Err: std::fmt::Display,
{
type Value = SmartSet<T>;
@ -223,7 +224,7 @@ where
formatter.write_str("a set of integer")
}
visit_from!{
visit_from! {
(visit_i8, i8),
(visit_i16, i16),
(visit_i32, i32),
@ -250,12 +251,14 @@ where
loop {
if let Ok(Some(value)) = seq.next_element::<String>() {
r.set.extend(&SmartSet::<T>::from_str(&value).map_err(serde::de::Error::custom)?.set);
}
else if let Some(value) = seq.next_element()? {
r.set.extend(
&SmartSet::<T>::from_str(&value)
.map_err(serde::de::Error::custom)?
.set,
);
} else if let Some(value) = seq.next_element()? {
r.set.insert(value);
}
else {
} else {
break;
}
}
@ -267,7 +270,14 @@ where
// This is the trait that informs Serde how to deserialize MyMap.
impl<'de, T> Deserialize<'de> for SmartSet<T>
where
T: Num+Ord+Copy+NumCast + Deserialize<'de> + std::str::FromStr + ops::AddAssign + std::fmt::Debug,
T: Num
+ Ord
+ Copy
+ NumCast
+ Deserialize<'de>
+ std::str::FromStr
+ ops::AddAssign
+ std::fmt::Debug,
<T as FromStr>::Err: std::fmt::Display,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>