Skip to content

Commit

Permalink
Use new_unchecked() constructors for tag labels and facets
Browse files Browse the repository at this point in the history
  • Loading branch information
uklotzde committed Jul 26, 2023
1 parent c05789d commit d86ce12
Show file tree
Hide file tree
Showing 14 changed files with 79 additions and 64 deletions.
8 changes: 4 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion crates/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ infect = "0.0.29"
#infect = { git = "https://github.com/uklotzde/infect.git" }
log = "0.4.19"
serde = { version = "1.0.175", features = ["derive"], optional = true }
serde_json = "1.0.103"
serde_json = "1.0.104"
serde_urlencoded = "0.7.1"
static_assertions = "1.1.0"

Expand Down
2 changes: 1 addition & 1 deletion crates/core-json/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ json-schema = ["dep:schemars", "dep:chrono", "aoide-core/json-schema"]
js = ["aoide-core/js"]

[dev-dependencies]
serde_json = "1.0.103"
serde_json = "1.0.104"
2 changes: 1 addition & 1 deletion crates/core-json/src/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl<'de> Visitor<'de> for FacetKeyVisitor {
// i.e. by the string representation of the default facet identifier.
_core::FacetKey::default()
} else {
let facet_id = _core::FacetId::new(s.into());
let facet_id = _core::FacetId::new_unchecked(s.into());
if !facet_id.is_valid() {
return Err(serde::de::Error::invalid_value(
serde::de::Unexpected::Str(s),
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
// modules for keeping the size of the source files handy. Often
// types have the same name as their parent module.
#![allow(clippy::module_name_repetitions)]
// Repeating the type name in `..Default::default()` expressions
// is not needed since the context is obvious.
// Repeating the type name in `Default::default()` expressions is not needed
// as long as the context is obvious.
#![allow(clippy::default_trait_access)]
// Using wildcard imports consciously is acceptable.
#![allow(clippy::wildcard_imports)]
Expand Down
19 changes: 10 additions & 9 deletions crates/core/src/tag/facet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,30 +84,24 @@ impl<'a> FacetId<'a> {
}

pub fn clamp_from(from: impl Into<Cow<'a, str>>) -> Option<Self> {
let clamped = Self::clamp_inner(from.into()).map(Self::new);
let clamped = Self::clamp_inner(from.into()).map(Self::new_unchecked);
debug_assert!(clamped.is_valid());
clamped
}

#[must_use]
pub fn from_unchecked(from: impl Into<Cow<'a, str>>) -> Self {
let inner = from.into();
let unchecked = Self::new(inner);
let unchecked = Self::new_unchecked(inner);
debug_assert!(unchecked.is_valid());
unchecked
}

#[must_use]
pub const fn new(inner: Cow<'a, str>) -> Self {
pub const fn new_unchecked(inner: Cow<'a, str>) -> Self {
Self(inner)
}

#[must_use]
pub fn into_inner(self) -> Cow<'a, str> {
let Self(inner) = self;
inner
}

#[must_use]
pub fn as_str(&self) -> &str {
let Self(inner) = self;
Expand Down Expand Up @@ -161,6 +155,13 @@ impl CanonicalOrd for FacetId<'_> {
}
}

impl<'a> From<FacetId<'a>> for Cow<'a, str> {
fn from(from: FacetId<'a>) -> Self {
let FacetId(inner) = from;
inner
}
}

#[derive(Copy, Clone, Debug)]
pub enum FacetIdInvalidity {
Empty,
Expand Down
14 changes: 9 additions & 5 deletions crates/core/src/tag/facet/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ fn clamp_from() {
#[test]
fn validate() {
let reverse_alphabet: String = FACET_ID_ALPHABET.chars().rev().collect();
assert!(FacetId::new(reverse_alphabet.into()).validate().is_ok());
assert!(FacetId::new(FACET_ID_ALPHABET.into()).validate().is_ok());
assert!(FacetId::new("Facet".into()).validate().is_err());
assert!(FacetId::new("a facet".into()).validate().is_err());
assert!(FacetId::new_unchecked(reverse_alphabet.into())
.validate()
.is_ok());
assert!(FacetId::new_unchecked(FACET_ID_ALPHABET.into())
.validate()
.is_ok());
assert!(FacetId::new_unchecked("Facet".into()).validate().is_err());
assert!(FacetId::new_unchecked("a facet".into()).validate().is_err());
}

#[test]
Expand All @@ -44,7 +48,7 @@ fn default_is_invalid() {

#[test]
fn empty_is_invalid() {
assert!(FacetId::new("".into()).validate().is_err());
assert!(FacetId::new_unchecked("".into()).validate().is_err());
}

#[test]
Expand Down
13 changes: 7 additions & 6 deletions crates/core/src/tag/label/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ impl<'a> Label<'a> {
Self(inner)
}

#[must_use]
pub fn into_inner(self) -> Cow<'a, str> {
let Self(inner) = self;
inner
}

#[must_use]
pub fn as_str(&self) -> &str {
let Self(inner) = self;
Expand Down Expand Up @@ -117,6 +111,13 @@ impl CanonicalOrd for Label<'_> {
}
}

impl<'a> From<Label<'a>> for Cow<'a, str> {
fn from(from: Label<'a>) -> Self {
let Label(inner) = from;
inner
}
}

#[derive(Copy, Clone, Debug)]
pub enum LabelInvalidity {
Empty,
Expand Down
2 changes: 1 addition & 1 deletion crates/core/src/tag/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ impl<'a> Tags<'a> {
continue;
}
let tombstone = FacetedTags {
facet_id: FacetId::new("".into()),
facet_id: FacetId::new_unchecked("".into()),
tags: Default::default(),
};
let faceted_tags = std::mem::replace(faceted_tags, tombstone);
Expand Down
63 changes: 36 additions & 27 deletions crates/core/src/track/tag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,109 +16,114 @@ use crate::tag::FacetId;
// Vorbis: GROUPING
// MP4: ©grp
pub const FACET_GROUPING: &str = "cgrp";
pub const FACET_ID_GROUPING: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_GROUPING));
pub const FACET_ID_GROUPING: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_GROUPING));

// Comment
// ID3v2.4: COMM (without `description`)
// Vorbis: COMMENT
// MP4: ©cmt
pub const FACET_COMMENT: &str = "comm";
pub const FACET_ID_COMMENT: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_COMMENT));
pub const FACET_ID_COMMENT: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_COMMENT));

// Description
// ID3v2.4: COMM:description
// Vorbis: DESCRIPTION
// MP4: desc
pub const FACET_DESCRIPTION: &str = "desc";
pub const FACET_ID_DESCRIPTION: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_DESCRIPTION));
pub const FACET_ID_DESCRIPTION: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_DESCRIPTION));

// ISO 639-3 language codes: "eng", "fre"/"fra", "ita", "spa", "ger"/"deu", ...
// ID3v2.4: TLAN
// Vorbis: LANGUAGE
// MP4: ----:com.apple.iTunes:LANGUAGE
pub const FACET_LANGUAGE: &str = "lang";
pub const FACET_ID_LANGUAGE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_LANGUAGE));
pub const FACET_ID_LANGUAGE: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_LANGUAGE));

// "Pop", "Dance", "Electronic", "R&B/Soul", "Hip Hop/Rap", ...
// ID3v2.4: TCON
// Vorbis: GENRE
// MP4: ©gen
pub const FACET_GENRE: &str = "genre";
pub const FACET_ID_GENRE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_GENRE));
pub const FACET_ID_GENRE: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_GENRE));

// Personal mental or emotional state, e.g. "happy", "sexy", "sad", "melancholic", "joyful", ...
// ID3v2.4: TMOO
// Vorbis: MOOD
// MP4: ----:com.apple.iTunes:MOOD
pub const FACET_MOOD: &str = "mood";
pub const FACET_ID_MOOD: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_MOOD));
pub const FACET_ID_MOOD: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_MOOD));

// International Standard Recording Code (ISRC, ISO 3901)
// ID3v2.4: TSRC
// Vorbis: ISRC
// MP4: isrc
pub const FACET_ISRC: &str = "isrc";
pub const FACET_ID_ISRC: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_ISRC));
pub const FACET_ID_ISRC: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_ISRC));

// Vendor-supplied, globally unique identifier(s) used by iTunes
// Format: prefix:scheme:identifier
// Supported schemes: upc, isrc, isan, grid, uuid, vendor_id
// Example: "SonyBMG:isrc:USRC10900295"
// See also: https://www.apple.com/au/itunes/lp-and-extras/docs/Development_Guide.pdf
pub const FACET_XID: &str = "xid";
pub const FACET_ID_XID: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_XID));
pub const FACET_ID_XID: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_XID));

// [MusicBrainz Recording Identifier](https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html#id21)
// ID3v2.4: UFID:http://musicbrainz.org
// Vorbis: MUSICBRAINZ_TRACKID
// MP4: ----:com.apple.iTunes:MusicBrainz Track Id
pub const FACET_MBID_RECORDING: &str = "mbid-rec";
pub const FACET_ID_MBID_RECORDING: &FacetId<'_> =
&FacetId::new(Cow::Borrowed(FACET_MBID_RECORDING));
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_RECORDING));

// [MusicBrainz Track Identifier](https://picard-docs.musicbrainz.org/en/appendices/tag_mapping.html#id24)
// ID3v2.4: TXXX:MusicBrainz Release Track Id
// Vorbis: MUSICBRAINZ_TRACKID
// MP4: ----:com.apple.iTunes:MusicBrainz Release Track Id
pub const FACET_MBID_TRACK: &str = "mbid-trk";
pub const FACET_ID_MBID_TRACK: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_MBID_TRACK));
pub const FACET_ID_MBID_TRACK: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_TRACK));

// [MusicBrainz Release Identifier](https://musicbrainz.org/doc/Release)
// ID3v2.4: TXXX:MusicBrainz Album Id
// Vorbis: MUSICBRAINZ_ALBUMID
// MP4: ----:com.apple.iTunes:MusicBrainz Album Id
pub const FACET_MBID_RELEASE: &str = "mbid-rel";
pub const FACET_ID_MBID_RELEASE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_MBID_RELEASE));
pub const FACET_ID_MBID_RELEASE: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_RELEASE));

// [MusicBrainz Release Group Identifier](https://musicbrainz.org/doc/Release_Group)
// ID3v2.4: TXXX:MusicBrainz Release Group Id
// Vorbis: MUSICBRAINZ_RELEASEGROUPID
// MP4: ----:com.apple.iTunes:MusicBrainz Release Group Id
pub const FACET_MBID_RELEASE_GROUP: &str = "mbid-rel-grp";
pub const FACET_ID_MBID_RELEASE_GROUP: &FacetId<'_> =
&FacetId::new(Cow::Borrowed(FACET_MBID_RELEASE_GROUP));
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_RELEASE_GROUP));

// [MusicBrainz Artist Identifier](https://musicbrainz.org/doc/Artist)
// ID3v2.4: TXXX:MusicBrainz Artist Id
// Vorbis: MUSICBRAINZ_ARTISTID
// MP4: ----:com.apple.iTunes:MusicBrainz Artist Id
pub const FACET_MBID_ARTIST: &str = "mbid-art";
pub const FACET_ID_MBID_ARTIST: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_MBID_ARTIST));
pub const FACET_ID_MBID_ARTIST: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_ARTIST));

// [MusicBrainz Release Artist Identifier](https://musicbrainz.org/doc/Release_Artist)
// ID3v2.4: TXXX:MusicBrainz Album Artist Id
// Vorbis: MUSICBRAINZ_ALBUMARTISTID
// MP4: ----:com.apple.iTunes:MusicBrainz Album Id
pub const FACET_MBID_RELEASE_ARTIST: &str = "mbid-rel-art";
pub const FACET_ID_MBID_RELEASE_ARTIST: &FacetId<'_> =
&FacetId::new(Cow::Borrowed(FACET_MBID_RELEASE_ARTIST));
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_RELEASE_ARTIST));

// [MusicBrainz Work Identifier](https://musicbrainz.org/doc/Work)
// ID3v2.4: TXXX:MusicBrainz Work Id
// Vorbis: MUSICBRAINZ_WORKID
// MP4: ----:com.apple.iTunes:MusicBrainz Work Id
pub const FACET_MBID_WORK: &str = "mbid-wrk";
pub const FACET_ID_MBID_WORK: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_MBID_WORK));
pub const FACET_ID_MBID_WORK: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_MBID_WORK));

// Predefined musical or audio feature scores (as of Spotify/EchoNest).
// A label is optional and could be used for identifying the source of
Expand All @@ -131,41 +136,45 @@ pub const FACET_ID_MBID_WORK: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_M
// See also: [Spotify Audio Features](https://developer.spotify.com/documentation/web-api/reference/tracks/get-audio-features/)

pub const FACET_ACOUSTICNESS: &str = "acousticness";
pub const FACET_ID_ACOUSTICNESS: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_ACOUSTICNESS));
pub const FACET_ID_ACOUSTICNESS: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_ACOUSTICNESS));

pub const FACET_AROUSAL: &str = "arousal";
pub const FACET_ID_AROUSAL: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_AROUSAL));
pub const FACET_ID_AROUSAL: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_AROUSAL));

pub const FACET_DANCEABILITY: &str = "danceability";
pub const FACET_ID_DANCEABILITY: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_DANCEABILITY));
pub const FACET_ID_DANCEABILITY: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_DANCEABILITY));

pub const FACET_ENERGY: &str = "energy";
pub const FACET_ID_ENERGY: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_ENERGY));
pub const FACET_ID_ENERGY: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_ENERGY));

pub const FACET_INSTRUMENTALNESS: &str = "instrumentalness";
pub const FACET_ID_INSTRUMENTALNESS: FacetId<'_> =
FacetId::new(Cow::Borrowed(FACET_INSTRUMENTALNESS));
FacetId::new_unchecked(Cow::Borrowed(FACET_INSTRUMENTALNESS));

pub const FACET_LIVENESS: &str = "liveness";
pub const FACET_ID_LIVENESS: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_LIVENESS));
pub const FACET_ID_LIVENESS: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_LIVENESS));

pub const FACET_POPULARITY: &str = "popularity";
pub const FACET_ID_POPULARITY: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_POPULARITY));
pub const FACET_ID_POPULARITY: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_POPULARITY));

pub const FACET_SPEECHINESS: &str = "speechiness";
pub const FACET_ID_SPEECHINESS: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_SPEECHINESS));
pub const FACET_ID_SPEECHINESS: &FacetId<'_> =
&FacetId::new_unchecked(Cow::Borrowed(FACET_SPEECHINESS));

pub const FACET_VALENCE: &str = "valence";
pub const FACET_ID_VALENCE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_VALENCE));
pub const FACET_ID_VALENCE: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_VALENCE));

// Custom: Decades like "1980s", "2000s", ..., or other time-based properties
pub const FACET_DECADE: &str = "decade";
pub const FACET_ID_DECADE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_DECADE));
pub const FACET_ID_DECADE: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_DECADE));

// Custom: Sub-genres or details like "East Coast", "West Coast", ...
pub const FACET_STYLE: &str = "style";
pub const FACET_ID_STYLE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_STYLE));
pub const FACET_ID_STYLE: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_STYLE));

// Custom: Atmosphere of the situation, e.g. "bouncy", "driving", "dreamy", "poppy", "punchy", "spiritual", "tropical", "uplifting" ...
pub const FACET_VIBE: &str = "vibe";
pub const FACET_ID_VIBE: &FacetId<'_> = &FacetId::new(Cow::Borrowed(FACET_VIBE));
pub const FACET_ID_VIBE: &FacetId<'_> = &FacetId::new_unchecked(Cow::Borrowed(FACET_VIBE));
6 changes: 3 additions & 3 deletions crates/media-file/src/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use aoide_core::{
},
music::tempo::TempoBpm,
prelude::*,
tag::{FacetId, FacetKey, FacetedTags, Label, PlainTag, Tags, TagsMap},
tag::{FacetId, FacetKey, FacetedTags, PlainTag, Tags, TagsMap},
track::{
actor::{Kind as ActorKind, Role as ActorRole},
album::Kind as AlbumKind,
Expand Down Expand Up @@ -1032,7 +1032,7 @@ fn export_faceted_tags(
if let Some(config) = config {
let joined_labels = config.join_labels(
tags.into_iter()
.filter_map(|PlainTag { label, score: _ }| label.map(Label::into_inner)),
.filter_map(|PlainTag { label, score: _ }| label.map(Into::into)),
);
if let Some(joined_labels) = joined_labels {
let inserted = tag.insert_text(item_key, joined_labels.into_owned());
Expand All @@ -1047,7 +1047,7 @@ fn export_faceted_tags(
.filter_map(|PlainTag { label, score: _ }| label)
{
let item_key = item_key.clone();
let item_val = ItemValue::Text(label.into_inner().into_owned());
let item_val = ItemValue::Text(Cow::from(label).into_owned());
let pushed = tag.push(TagItem::new(item_key, item_val));
if !pushed {
// Unsupported key
Expand Down
Loading

0 comments on commit d86ce12

Please sign in to comment.