1use std::path::Path;
2
3use reqwest::{
4 header::{self, HeaderValue},
5 multipart::Form,
6};
7use serde::{Deserialize, Serialize};
8
9use crate::{AssetTypeId, DateTime, Error, client::Client, endpoint};
10
11pub const URL: &str = "https://apis.roblox.com/assets/user-auth/v1";
12
13#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
14#[serde(rename_all = "camelCase")]
15pub enum Creator {
16 UserId(String), GroupId(String),
19}
20
21#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
22#[serde(rename_all = "camelCase")]
23pub struct CreationContext {
24 pub creator: Creator,
25 pub expected_price: Option<u64>,
26}
27
28#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
29pub struct ModerationResult {
30 #[serde(rename = "moderationState")]
31 pub state: String,
32}
33
34#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
35#[serde(rename_all = "camelCase")]
36pub struct AssetInfo {
37 #[serde(rename = "assetId")]
38 pub id: String,
39 pub icon: Option<String>,
40 #[serde(rename = "displayName")]
41 pub name: String,
42 pub description: String,
43
44 pub path: String,
45 pub state: String,
46 pub asset_type: AssetTypeId,
47
48 pub revision_id: String,
49 #[serde(rename = "revisionCreateTime")]
50 pub revision_creation_time: DateTime,
51
52 pub creation_context: CreationContext,
53 pub moderation_result: ModerationResult,
54}
55
56#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
57#[serde(rename_all = "camelCase")]
58pub struct AssetUploadResponse {
59 pub path: String,
60 #[serde(rename = "assetId")]
62 pub id: String,
63 #[serde(rename = "displayName")]
64 pub name: String,
65
66 pub state: String,
67 pub asset_type: AssetTypeId,
68
69 pub revision_id: String,
70 #[serde(rename = "revisionCreateTime")]
71 pub revision_creation_time: DateTime,
72
73 pub creation_context: CreationContext,
74 pub moderation_result: ModerationResult,
75}
76
77#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
78#[serde(rename_all = "camelCase")]
79pub struct AssetUploadStatus {
80 pub path: String,
81 pub operation_id: String,
82 #[serde(rename = "done")]
83 pub complete: bool,
84 pub response: Option<AssetUploadResponse>,
85}
86
87endpoint! {
88 asset(id: u64) -> AssetInfo {
89 GET "{URL}/assets/{id}";
90 }
91
92 status(operation_id: &str) -> AssetUploadStatus {
93 GET "{URL}/operations/{operation_id}";
94 }
95}
96
97pub async fn upload(
99 client: &mut Client,
100 path: impl AsRef<Path>,
101 title: &str,
102 description: &str,
103 asset_type: AssetTypeId,
104 creation_context: CreationContext,
105) -> Result<AssetUploadStatus, Error> {
106 let mut headers = client.requestor.default_headers.clone();
107 headers.insert(header::ACCEPT, HeaderValue::from_str("*/*").unwrap());
108
109 #[derive(Clone, Debug, Deserialize, Serialize)]
110 #[serde(rename_all = "camelCase")]
111 struct Request<'a> {
112 #[serde(rename = "displayName")]
113 title: &'a str,
114 description: &'a str,
115 asset_type: AssetTypeId,
116 creation_context: CreationContext,
117 }
118
119 let request = serde_json::to_string(&Request {
120 title,
121 description,
122 asset_type,
123 creation_context,
124 })
125 .unwrap();
126
127 let result = client
128 .requestor
129 .client
130 .post(format!("{URL}/assets"))
131 .headers(headers)
132 .multipart(
133 Form::new()
134 .text("request", request)
135 .file("fileContent", &path)
136 .await
137 .unwrap(),
138 )
139 .send()
140 .await;
141
142 let response = client.requestor.validate_response(result).await?;
143 client
144 .requestor
145 .parse_json::<AssetUploadStatus>(response)
146 .await
147}