veloren_voxygen/
credits.rs

1use serde::Deserialize;
2use std::path::PathBuf;
3
4// NOTE: we are free to split the manifest asset format and the format processed
5// for display into separate structs but they happen to be identical for now
6
7// See best practices for attribution: https://wiki.creativecommons.org/wiki/Best_practices_for_attribution
8
9#[expect(dead_code)]
10#[derive(Clone, Deserialize)]
11pub struct Art {
12    /// Name of the art.
13    pub name: String,
14    /// Link if the asset is from or derived from an external source that can be
15    /// linked.
16    #[serde(default)]
17    pub source_link: String,
18    /// List of authors for the credited art, field can be omitted if there are
19    /// no authors to list.
20    #[serde(default)]
21    pub authors: Vec<String>,
22    /// Relative path to the asset from the top level asset folder.
23    /// Used so we can keep track of the actual files, but not currently used in
24    /// the credits screen to display anything.
25    pub asset_path: PathBuf,
26    /// License that the art is under, can be omitted, if not present assumed to
27    /// be GPL3.
28    #[serde(default)]
29    pub license: String,
30    /// Link to the license if one is available.
31    #[serde(default)]
32    pub license_link: String,
33    /// Notes on any modifications that were made if the original work was
34    /// modified by us.
35    #[serde(default)]
36    pub modifications: String,
37    /// Any additional attribution notes that may be desired and/or required by
38    /// the respective license that can't be conveyed or would be awkward to
39    /// convey with the other provided fields.
40    #[serde(default)]
41    pub notes: String,
42}
43
44#[expect(dead_code)]
45#[derive(Clone, Deserialize, Debug)]
46pub struct Sounds {
47    /// List of authors in this credit
48    pub authors: Vec<String>,
49    /// List of files created by authors
50    pub files: Vec<PathBuf>,
51    /// License that the art is under, can be omitted, if not present assumed to
52    /// be GPL3.
53    #[serde(default)]
54    pub license: String,
55    /// Link to the license if one is available.
56    #[serde(default)]
57    pub license_link: String,
58    #[serde(default)]
59    /// Any additional attribution notes that may be desired and/or required by
60    /// the respective license that can't be conveyed or would be awkward to
61    /// convey with the other provided fields.
62    pub notes: String,
63}
64
65#[derive(Clone, Deserialize)]
66pub struct Contributor {
67    pub name: String,
68    /// Short note or description of the contributions
69    /// Optional, can be left empty/omitted
70    #[serde(default)]
71    pub contributions: String,
72}
73
74/// Credits manifest processed into format for display in the UI
75#[derive(Clone, Deserialize)]
76pub struct Credits {
77    pub music: Vec<Art>,
78    pub sounds: Vec<Sounds>,
79    pub fonts: Vec<Art>,
80    pub other_art: Vec<Art>,
81    pub contributors: Vec<Contributor>,
82    // TODO: include credits for dependencies where the license requires attribution?
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88    use common::assets::{self, AssetExt, Ron};
89
90    #[test]
91    fn all_art_asset_paths_exists() {
92        let credits = Ron::<Credits>::load_expect_cloned("credits").into_inner();
93
94        credits
95            .music
96            .into_iter()
97            .chain(credits.fonts)
98            .chain(credits.other_art)
99            .for_each(|art| {
100                assert!(
101                    assets::ASSETS_PATH.join(&art.asset_path).exists(),
102                    "assets/{} does not exist!",
103                    art.asset_path.display(),
104                );
105            });
106    }
107
108    #[test]
109    fn all_sounds_asset_paths_exists() {
110        let credits = Ron::<Credits>::load_expect_cloned("credits").into_inner();
111        let sfx_path = assets::ASSETS_PATH.join(PathBuf::from("voxygen/audio/sfx/"));
112
113        credits.sounds.into_iter().for_each(|sounds| {
114            sounds.files.iter().for_each(|path| {
115                assert!(
116                    sfx_path.join(path).exists(),
117                    "assets/{} does not exist!",
118                    path.display(),
119                );
120            });
121        });
122    }
123}