Skip to main content

surrealdb_expr/val/
file.rs

1use revision::revisioned;
2use storekey::{BorrowDecode, Encode};
3use surrealdb_types::{SqlFormat, ToSql, write_sql};
4
5use crate::val::IndexFormat;
6
7#[revisioned(revision = 1)]
8#[derive(Clone, Debug, Eq, PartialEq, Hash, PartialOrd, Encode, BorrowDecode)]
9#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
10#[storekey(format = "()")]
11#[storekey(format = "IndexFormat")]
12pub struct File {
13	pub bucket: String,
14	pub key: String,
15}
16
17impl File {
18	pub fn new(bucket: String, key: String) -> Self {
19		let key = if key.starts_with("/") {
20			key
21		} else {
22			format!("/{key}")
23		};
24
25		Self {
26			bucket,
27			key,
28		}
29	}
30
31	/// Check if this File belongs to a certain bucket type
32	pub fn is_bucket_type(&self, types: &[String]) -> bool {
33		types.is_empty() || types.contains(&self.bucket)
34	}
35
36	pub fn display_inner(&self) -> String {
37		format!("{}:{}", fmt_inner(&self.bucket, true), fmt_inner(&self.key, false))
38	}
39}
40
41impl From<surrealdb_types::File> for File {
42	fn from(v: surrealdb_types::File) -> Self {
43		Self {
44			bucket: v.bucket,
45			key: v.key,
46		}
47	}
48}
49
50impl From<File> for surrealdb_types::File {
51	fn from(x: File) -> Self {
52		surrealdb_types::File::new(x.bucket, x.key)
53	}
54}
55
56fn fmt_inner(v: &str, escape_slash: bool) -> String {
57	v.chars()
58		.flat_map(|c| {
59			if c.is_ascii_alphanumeric()
60				|| matches!(c, '-' | '_' | '.')
61				|| (!escape_slash && c == '/')
62			{
63				vec![c]
64			} else {
65				vec!['\\', c]
66			}
67		})
68		.collect::<String>()
69}
70
71impl ToSql for File {
72	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
73		write_sql!(f, sql_fmt, "f\"{}\"", self.display_inner())
74	}
75}