p2panda_rs/schema/validate/
blob.rs1use once_cell::sync::Lazy;
4use regex::Regex;
5
6use crate::hash::Hash;
7use crate::operation::plain::{PlainFields, PlainValue};
8use crate::schema::validate::error::BlobError;
9
10pub fn validate_mime_type(value: &str) -> bool {
14 static NAME_REGEX: Lazy<Regex> = Lazy::new(|| {
15 Regex::new("^[a-z-]{1,12}\\/(([a-z0-9]{1,18})[\\.|+|-]){0,6}[a-z0-9]{1,16}$").unwrap()
17 });
18
19 NAME_REGEX.is_match(value)
20}
21
22pub fn validate_pieces(value: &[Vec<Hash>]) -> bool {
26 !value.is_empty()
27}
28
29pub fn validate_blob_v1_fields(fields: &PlainFields) -> Result<(), BlobError> {
38 match fields.get("mime_type") {
42 Some(PlainValue::String(value)) => {
43 if validate_mime_type(value) {
44 Ok(())
45 } else {
46 Err(BlobError::MimeTypeInvalid)
47 }
48 }
49 _ => Ok(()),
50 }?;
51
52 match fields.get("pieces") {
54 Some(PlainValue::PinnedRelationList(value)) => {
55 if validate_pieces(value) {
56 Ok(())
57 } else {
58 Err(BlobError::PiecesEmpty)
59 }
60 }
61 _ => Ok(()),
62 }?;
63
64 Ok(())
65}
66
67#[cfg(test)]
68mod test {
69 use rstest::rstest;
70
71 use crate::document::DocumentViewId;
72 use crate::operation::plain::PlainFields;
73 use crate::test_utils::fixtures::random_document_view_id;
74
75 use super::{validate_blob_v1_fields, validate_mime_type};
76
77 #[rstest]
78 #[case(vec![
79 ("length", 1.into()),
80 ("mime_type", "image/png".into()),
81 ("pieces", vec![random_document_view_id()].into()),
82 ].into())]
83 #[case(vec![
84 ("length", 1000.into()),
85 ("mime_type", "application/x-zip-compressed".into()),
86 ("pieces", vec![random_document_view_id()].into()),
87 ].into())]
88 #[should_panic]
89 #[case::invalid_mime_type(vec![
90 ("length", 100.into()),
91 ("mime_type", "not a mime type".into()),
92 ("pieces", vec![random_document_view_id()].into()),
93 ].into())]
94 #[should_panic]
95 #[case::empty_pieces(vec![
96 ("length", 1.into()),
97 ("mime_type", "image/png".into()),
98 ("pieces", Vec::<DocumentViewId>::new().into()),
99 ].into())]
100 fn check_fields(#[case] fields: PlainFields) {
101 assert!(validate_blob_v1_fields(&fields).is_ok());
102 }
103
104 #[rstest]
105 #[case("video/webm")]
106 #[case("image/webp")]
107 #[case("x-conference/x-cooltalk")]
108 #[case("application/vnd.cluetrust.cartomobile-config-pkg")]
109 #[case("application/emma+xml")]
110 #[case("my/made.up.mime.type")] #[should_panic]
112 #[case("wrong format")]
113 #[should_panic]
114 #[case("wrong_format")]
115 #[should_panic]
116 #[case("wrong/f o r m a t")]
117 #[should_panic]
118 #[case("wrong/!format!")]
119 #[should_panic]
120 #[case("wrong/for..mat")]
121 #[should_panic]
122 #[case("wro.ng/for..mat")]
123 #[should_panic]
124 #[case("this/mime.type.has.one.too.many.elements.yes")]
125 #[should_panic]
126 #[case("this/mime.type.hasoneelementwhichiswaytoolong")]
127 #[should_panic]
128 #[case("thismimetypealsohasonelongelement/one.element.too.long")]
129 fn check_mime_type_field(#[case] name_str: &str) {
130 assert!(validate_mime_type(name_str));
131 }
132}