common: move types into separate files

This commit is contained in:
2025-02-23 16:30:21 +01:00
parent 5e0fe71feb
commit 9f07114401
10 changed files with 377 additions and 286 deletions

View File

@@ -1,286 +1,2 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
pub type SongPosition = u32;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AbsouluteRelativeSongPosition {
Absolute(SongPosition),
RelativePlus(SongPosition),
RelativeMinus(SongPosition),
}
impl FromStr for AbsouluteRelativeSongPosition {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
s if s.starts_with('+') => Self::RelativePlus(s[1..].parse().map_err(|_| ())?),
s if s.starts_with('-') => Self::RelativeMinus(s[1..].parse().map_err(|_| ())?),
s => Self::Absolute(s.parse().map_err(|_| ())?),
})
}
}
pub type SongId = u32;
pub type Seconds = u32;
pub type TimeWithFractions = f64;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum OneOrRange {
One(SongPosition),
Range(SongPosition, SongPosition),
}
impl FromStr for OneOrRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let start = parts.next().ok_or(())?.parse().map_err(|_| ())?;
Ok(match parts.next() {
Some(end) => Self::Range(start, end.parse().map_err(|_| ())?),
None => Self::One(start),
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WindowRange {
pub start: SongPosition,
pub end: Option<SongPosition>,
}
impl FromStr for WindowRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let start = parts.next().ok_or(())?.parse().map_err(|_| ())?;
let end = parts.next().map(|s| s.parse().map_err(|_| ()));
Ok(Self {
start,
end: end.transpose()?,
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimeInterval {
pub start: Option<TimeWithFractions>,
pub end: Option<TimeWithFractions>,
}
impl FromStr for TimeInterval {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let start = parts.next().map(|s| s.parse().map_err(|_| ()));
let end = parts.next().map(|s| s.parse().map_err(|_| ()));
Ok(Self {
start: start.transpose()?,
end: end.transpose()?,
})
}
}
pub type Priority = u8;
pub type PlaylistName = String;
pub type Offset = u32;
// TODO: use a proper types
pub type TagName = String;
pub type TagValue = String;
pub type Uri = String;
pub type Path = String;
pub type Sort = String;
pub type Version = String;
pub type Feature = String;
pub type PartitionName = String;
pub type AudioOutputId = String;
pub type ChannelName = String;
pub type StickerType = String;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Tag {
/// The artist name. Its meaning is not well-defined; see “composer” and “performer” for more specific tags.
Artist(String),
/// Same as artist, but for sorting. This usually omits prefixes such as “The”.
ArtistSort(String),
/// The album name.
Album(String),
/// Same as album, but for sorting.
AlbumSort(String),
/// On multi-artist albums, this is the artist name which shall be used for the whole album. The exact meaning of this tag is not well-defined.
AlbumArtist(String),
/// Same as albumartist, but for sorting.
AlbumArtistSort(String),
/// The song title.
Title(String),
/// Same as title, but for sorting.
TitleSort(String),
/// The decimal track number within the album.
Track(String),
/// A name for this song. This is not the song title. The exact meaning of this tag is not well-defined. It is often used by badly configured internet radio stations with broken tags to squeeze both the artist name and the song title in one tag.
Name(String),
/// The music genre.
Genre(String),
/// The mood of the audio with a few keywords.
Mood(String),
/// The song's release date. This is usually a 4-digit year.
Date(String),
/// The song's original release date.
OriginalDate(String),
/// The artist who composed the song.
Composer(String),
/// Same as composer, but for sorting.
ComposerSort(String),
/// The artist who performed the song.
Performer(String),
/// The conductor who conducted the song.
Conductor(String),
/// “a work is a distinct intellectual or artistic creation, which can be expressed in the form of one or more audio recordings”
Work(String),
/// The ensemble performing this song, e.g. “Wiener Philharmoniker”.
Ensemble(String),
/// Name of the movement, e.g. “Andante con moto”.
Movement(String),
/// Movement number, e.g. “2” or “II”.
MovementNumber(String),
/// If this tag is set to “1” players supporting this tag will display the work, movement, and movementnumber` instead of the track title.
ShowMovement(String),
/// Location of the recording, e.g. “Royal Albert Hall”.
Location(String),
/// “used if the sound belongs to a larger category of sounds/music” (from the IDv2.4.0 TIT1 description).
Grouping(String),
/// A human-readable comment about this song. The exact meaning of this tag is not well-defined.
Comment(String),
/// The decimal disc number in a multi-disc album.
Disc(String),
/// The name of the label or publisher.
Label(String),
/// The artist id in the MusicBrainz database.
MusicBrainzArtistId(String),
/// The album id in the MusicBrainz database.
MusicBrainzAlbumId(String),
/// The album artist id in the MusicBrainz database.
MusicBrainzAlbumArtistId(String),
/// The track id in the MusicBrainz database.
MusicBrainzTrackId(String),
/// The release group id in the MusicBrainz database.
MusicBrainzReleaseGroupId(String),
/// The release track id in the MusicBrainz database.
MusicBrainzReleaseTrackId(String),
/// The work id in the MusicBrainz database.
MusicBrainzWorkId(String),
/// Other tags not covered by the above
Other(String, String),
}
/// These are different parts of the canonical MPD server.
/// They are mostly used in the protocol with the `idle` command,
/// signalling that the client is waiting for changes in any, one or multiple of these subsystems.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SubSystem {
/// The song database has been modified after update.
Database,
/// A database update has started or finished. If the database was modified during the update, the database event is also emitted.
Update,
/// A stored playlist has been modified, renamed, created or deleted
StoredPlaylist,
/// The queue (i.e. the current playlist) has been modified
Playlist,
/// The player has been started, stopped or seeked or tags of the currently playing song have changed (e.g. received from stream)
Player,
/// The volume has been changed
Mixer,
/// An audio output has been added, removed or modified (e.g. renamed, enabled or disabled)
Output,
/// Options like repeat, random, crossfade, replay gain
Options,
/// A partition was added, removed or changed
Partition,
/// The sticker database has been modified.
Sticker,
/// A client has subscribed or unsubscribed to a channel
Subscription,
/// A message was received on a channel this client is subscribed to; this event is only emitted when the clients message queue is empty
Message,
/// A neighbor was found or lost
Neighbor,
/// The mount list has changed
Mount,
/// Other subsystems not covered by the above
Other(String),
}
impl FromStr for SubSystem {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"database" => Self::Database,
"update" => Self::Update,
"stored_playlist" => Self::StoredPlaylist,
"playlist" => Self::Playlist,
"player" => Self::Player,
"mixer" => Self::Mixer,
"output" => Self::Output,
"options" => Self::Options,
"partition" => Self::Partition,
"sticker" => Self::Sticker,
"subscription" => Self::Subscription,
"message" => Self::Message,
"neighbor" => Self::Neighbor,
"mount" => Self::Mount,
other => Self::Other(other.to_string()),
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Audio {
pub sample_rate: u64,
pub bits: u8,
pub channels: u8,
}
impl FromStr for Audio {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let sample_rate = parts.next().ok_or(())?.parse().map_err(|_| ())?;
let bits = u8::from_str_radix(parts.next().ok_or(())?, 16).map_err(|_| ())? + 1;
let channels = parts.next().ok_or(())?.parse().map_err(|_| ())?;
Ok(Self {
sample_rate,
bits,
channels,
})
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BoolOrOneshot {
True,
False,
Oneshot,
}
impl FromStr for BoolOrOneshot {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"0" => Self::False,
"1" => Self::True,
"oneshot" => Self::Oneshot,
_ => return Err(()),
})
}
}
mod types;
pub use types::*;

38
src/common/types.rs Normal file
View File

@@ -0,0 +1,38 @@
mod absolute_relative_song_position;
mod audio;
mod bool_or_oneshot;
mod one_or_range;
mod subsystem;
mod tag;
mod time_interval;
mod window_range;
pub use absolute_relative_song_position::AbsouluteRelativeSongPosition;
pub use audio::Audio;
pub use bool_or_oneshot::BoolOrOneshot;
pub use one_or_range::OneOrRange;
pub use subsystem::SubSystem;
pub use tag::Tag;
pub use time_interval::TimeInterval;
pub use window_range::WindowRange;
pub type SongPosition = u32;
pub type SongId = u32;
pub type Seconds = u32;
pub type TimeWithFractions = f64;
pub type Priority = u8;
pub type PlaylistName = String;
pub type Offset = u32;
// TODO: use a proper types
pub type TagName = String;
pub type TagValue = String;
pub type Uri = String;
pub type Path = String;
pub type Sort = String;
pub type Version = String;
pub type Feature = String;
pub type PartitionName = String;
pub type AudioOutputId = String;
pub type ChannelName = String;
pub type StickerType = String;

View File

@@ -0,0 +1,25 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use super::SongPosition;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum AbsouluteRelativeSongPosition {
Absolute(SongPosition),
RelativePlus(SongPosition),
RelativeMinus(SongPosition),
}
impl FromStr for AbsouluteRelativeSongPosition {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
s if s.starts_with('+') => Self::RelativePlus(s[1..].parse().map_err(|_| ())?),
s if s.starts_with('-') => Self::RelativeMinus(s[1..].parse().map_err(|_| ())?),
s => Self::Absolute(s.parse().map_err(|_| ())?),
})
}
}

27
src/common/types/audio.rs Normal file
View File

@@ -0,0 +1,27 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Audio {
pub sample_rate: u64,
pub bits: u8,
pub channels: u8,
}
impl FromStr for Audio {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let sample_rate = parts.next().ok_or(())?.parse().map_err(|_| ())?;
let bits = u8::from_str_radix(parts.next().ok_or(())?, 16).map_err(|_| ())? + 1;
let channels = parts.next().ok_or(())?.parse().map_err(|_| ())?;
Ok(Self {
sample_rate,
bits,
channels,
})
}
}

View File

@@ -0,0 +1,23 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum BoolOrOneshot {
True,
False,
Oneshot,
}
impl FromStr for BoolOrOneshot {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"0" => Self::False,
"1" => Self::True,
"oneshot" => Self::Oneshot,
_ => return Err(()),
})
}
}

View File

@@ -0,0 +1,24 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use super::SongPosition;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum OneOrRange {
One(SongPosition),
Range(SongPosition, SongPosition),
}
impl FromStr for OneOrRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let start = parts.next().ok_or(())?.parse().map_err(|_| ())?;
Ok(match parts.next() {
Some(end) => Self::Range(start, end.parse().map_err(|_| ())?),
None => Self::One(start),
})
}
}

View File

@@ -0,0 +1,66 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
/// These are different parts of the canonical MPD server.
/// They are mostly used in the protocol with the `idle` command,
/// signalling that the client is waiting for changes in any, one or multiple of these subsystems.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SubSystem {
/// The song database has been modified after update.
Database,
/// A database update has started or finished. If the database was modified during the update, the database event is also emitted.
Update,
/// A stored playlist has been modified, renamed, created or deleted
StoredPlaylist,
/// The queue (i.e. the current playlist) has been modified
Playlist,
/// The player has been started, stopped or seeked or tags of the currently playing song have changed (e.g. received from stream)
Player,
/// The volume has been changed
Mixer,
/// An audio output has been added, removed or modified (e.g. renamed, enabled or disabled)
Output,
/// Options like repeat, random, crossfade, replay gain
Options,
/// A partition was added, removed or changed
Partition,
/// The sticker database has been modified.
Sticker,
/// A client has subscribed or unsubscribed to a channel
Subscription,
/// A message was received on a channel this client is subscribed to; this event is only emitted when the clients message queue is empty
Message,
/// A neighbor was found or lost
Neighbor,
/// The mount list has changed
Mount,
/// Other subsystems not covered by the above
Other(String),
}
impl FromStr for SubSystem {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"database" => Self::Database,
"update" => Self::Update,
"stored_playlist" => Self::StoredPlaylist,
"playlist" => Self::Playlist,
"player" => Self::Player,
"mixer" => Self::Mixer,
"output" => Self::Output,
"options" => Self::Options,
"partition" => Self::Partition,
"sticker" => Self::Sticker,
"subscription" => Self::Subscription,
"message" => Self::Message,
"neighbor" => Self::Neighbor,
"mount" => Self::Mount,
other => Self::Other(other.to_string()),
})
}
}

121
src/common/types/tag.rs Normal file
View File

@@ -0,0 +1,121 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum Tag {
/// The artist name. Its meaning is not well-defined; see “composer” and “performer” for more specific tags.
Artist(String),
/// Same as artist, but for sorting. This usually omits prefixes such as “The”.
ArtistSort(String),
/// The album name.
Album(String),
/// Same as album, but for sorting.
AlbumSort(String),
/// On multi-artist albums, this is the artist name which shall be used for the whole album. The exact meaning of this tag is not well-defined.
AlbumArtist(String),
/// Same as albumartist, but for sorting.
AlbumArtistSort(String),
/// The song title.
Title(String),
/// Same as title, but for sorting.
TitleSort(String),
/// The decimal track number within the album.
Track(String),
/// A name for this song. This is not the song title. The exact meaning of this tag is not well-defined. It is often used by badly configured internet radio stations with broken tags to squeeze both the artist name and the song title in one tag.
Name(String),
/// The music genre.
Genre(String),
/// The mood of the audio with a few keywords.
Mood(String),
/// The song's release date. This is usually a 4-digit year.
Date(String),
/// The song's original release date.
OriginalDate(String),
/// The artist who composed the song.
Composer(String),
/// Same as composer, but for sorting.
ComposerSort(String),
/// The artist who performed the song.
Performer(String),
/// The conductor who conducted the song.
Conductor(String),
/// “a work is a distinct intellectual or artistic creation, which can be expressed in the form of one or more audio recordings”
Work(String),
/// The ensemble performing this song, e.g. “Wiener Philharmoniker”.
Ensemble(String),
/// Name of the movement, e.g. “Andante con moto”.
Movement(String),
/// Movement number, e.g. “2” or “II”.
MovementNumber(String),
/// If this tag is set to “1” players supporting this tag will display the work, movement, and movementnumber` instead of the track title.
ShowMovement(String),
/// Location of the recording, e.g. “Royal Albert Hall”.
Location(String),
/// “used if the sound belongs to a larger category of sounds/music” (from the IDv2.4.0 TIT1 description).
Grouping(String),
/// A human-readable comment about this song. The exact meaning of this tag is not well-defined.
Comment(String),
/// The decimal disc number in a multi-disc album.
Disc(String),
/// The name of the label or publisher.
Label(String),
/// The artist id in the MusicBrainz database.
MusicBrainzArtistId(String),
/// The album id in the MusicBrainz database.
MusicBrainzAlbumId(String),
/// The album artist id in the MusicBrainz database.
MusicBrainzAlbumArtistId(String),
/// The track id in the MusicBrainz database.
MusicBrainzTrackId(String),
/// The release group id in the MusicBrainz database.
MusicBrainzReleaseGroupId(String),
/// The release track id in the MusicBrainz database.
MusicBrainzReleaseTrackId(String),
/// The work id in the MusicBrainz database.
MusicBrainzWorkId(String),
/// Other tags not covered by the above
Other(String, String),
}
impl Tag {
pub fn new(key: String, value: String) -> Tag {
match key.as_str() {
"Artist" => Self::Artist(value),
"ArtistSort" => Self::ArtistSort(value),
"Album" => Self::Album(value),
"AlbumSort" => Self::AlbumSort(value),
"AlbumArtist" => Self::AlbumArtist(value),
"AlbumArtistSort" => Self::AlbumArtistSort(value),
"Title" => Self::Title(value),
"TitleSort" => Self::TitleSort(value),
"Track" => Self::Track(value),
"Name" => Self::Name(value),
"Genre" => Self::Genre(value),
"Mood" => Self::Mood(value),
"Date" => Self::Date(value),
"OriginalDate" => Self::OriginalDate(value),
"Composer" => Self::Composer(value),
"ComposerSort" => Self::ComposerSort(value),
"Performer" => Self::Performer(value),
"Conductor" => Self::Conductor(value),
"Work" => Self::Work(value),
"Ensemble" => Self::Ensemble(value),
"Movement" => Self::Movement(value),
"MovementNumber" => Self::MovementNumber(value),
"ShowMovement" => Self::ShowMovement(value),
"Location" => Self::Location(value),
"Grouping" => Self::Grouping(value),
"Comment" => Self::Comment(value),
"Disc" => Self::Disc(value),
"Label" => Self::Label(value),
"MusicBrainzArtistId" => Self::MusicBrainzArtistId(value),
"MusicBrainzAlbumId" => Self::MusicBrainzAlbumId(value),
"MusicBrainzAlbumArtistId" => Self::MusicBrainzAlbumArtistId(value),
"MusicBrainzTrackId" => Self::MusicBrainzTrackId(value),
"MusicBrainzReleaseGroupId" => Self::MusicBrainzReleaseGroupId(value),
"MusicBrainzReleaseTrackId" => Self::MusicBrainzReleaseTrackId(value),
"MusicBrainzWorkId" => Self::MusicBrainzWorkId(value),
other => Self::Other(other.to_string(), value),
}
}
}

View File

@@ -0,0 +1,25 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use super::TimeWithFractions;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TimeInterval {
pub start: Option<TimeWithFractions>,
pub end: Option<TimeWithFractions>,
}
impl FromStr for TimeInterval {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let start = parts.next().map(|s| s.parse().map_err(|_| ()));
let end = parts.next().map(|s| s.parse().map_err(|_| ()));
Ok(Self {
start: start.transpose()?,
end: end.transpose()?,
})
}
}

View File

@@ -0,0 +1,26 @@
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use super::SongPosition;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WindowRange {
pub start: SongPosition,
pub end: Option<SongPosition>,
}
impl FromStr for WindowRange {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parts = s.split(':');
let start = parts.next().ok_or(())?.parse().map_err(|_| ())?;
let end = parts.next().map(|s| s.parse().map_err(|_| ()));
Ok(Self {
start,
end: end.transpose()?,
})
}
}