1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
use serde::{Deserialize, Serialize};
use crate::presentation::{Context, LangMap, Metadata, Resource};
/// 顶层 Collection 结构
///
/// Top-level Collection structure, covering the main fields from Presentation API 3.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Collection {
#[serde(rename = "@context", default = "Context::presentation_default")]
pub context: Context,
pub id: String,
#[serde(default = "collection_type")]
pub r#type: String,
/// 集合的标题(可多语言、多值)。
///
/// Human-readable label of the collection (multi-language, multi-value).
pub label: LangMap,
/// 集合的简要摘要(可多语言)。
///
/// Short summary/description of the collection (multi-language).
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<LangMap>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Vec<Metadata>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required_statement: Option<Metadata>,
/// 权利或许可 URI。
///
/// Rights or license URI.
#[serde(skip_serializing_if = "Option::is_none")]
pub rights: Option<String>,
/// 该集合的语言代码列表(如 "en"、"zh-Hans")。
///
/// List of language codes used in this collection (e.g. "en", "zh-Hans").
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<Vec<String>>,
/// 提供该资源的机构或主体。
///
/// Providers (institutions, organizations) of this resource.
#[serde(skip_serializing_if = "Option::is_none")]
pub provider: Option<Vec<Resource>>,
/// 集合的 logo 资源。
///
/// Logo resources for the collection.
#[serde(skip_serializing_if = "Option::is_none")]
pub logo: Option<Vec<Resource>>,
/// 集合的缩略图资源。
///
/// Thumbnail resources for the collection.
#[serde(skip_serializing_if = "Option::is_none")]
pub thumbnail: Option<Vec<Resource>>,
/// 集合的主页链接。
///
/// Homepage resources for the collection.
#[serde(skip_serializing_if = "Option::is_none")]
pub homepage: Option<Vec<Resource>>,
/// 相关的外部机器可读资源(如 RDF、METS 等)。
///
/// Machine-readable external resources related to this collection.
#[serde(skip_serializing_if = "Option::is_none")]
pub see_also: Option<Vec<Resource>>,
/// 该集合的可选渲染形式(如 PDF 导出)。
///
/// Alternative renderings of this collection (e.g. PDF).
#[serde(skip_serializing_if = "Option::is_none")]
pub rendering: Option<Vec<Resource>>,
/// 与此集合相关的服务(如搜索服务、认证服务等)。
///
/// Services related to this collection (search, auth, etc.).
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<Vec<Resource>>,
/// 上级集合或清单。
///
/// Parent collections or manifests this collection is part of.
#[serde(skip_serializing_if = "Option::is_none")]
pub part_of: Option<Vec<Resource>>,
/// 行为提示(如 paged、continuous 等)。
///
/// Behavioral hints such as `paged`, `continuous`, etc.
#[serde(skip_serializing_if = "Option::is_none")]
pub behavior: Option<Vec<String>>,
/// 子项可以是 Manifest 或子 Collection,这里统一建模为 Resource 引用。
///
/// Items can be Manifests or child Collections, modeled as generic resources.
pub items: Vec<Resource>,
}
fn collection_type() -> String {
"Collection".to_string()
}
impl Default for Collection {
fn default() -> Self {
Self {
context: Context::presentation_default(),
id: "".to_string(),
r#type: collection_type(),
label: LangMap::default(),
summary: None,
metadata: None,
required_statement: None,
rights: None,
language: None,
provider: None,
logo: None,
thumbnail: None,
homepage: None,
see_also: None,
rendering: None,
service: None,
part_of: None,
behavior: None,
items: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_collection_default() {
let collection = Collection::default();
assert_eq!(collection.id, "");
assert_eq!(collection.r#type, "Collection");
}
}