roblox_api/api/assets/
v1.rs

1use std::path::Path;
2
3use reqwest::{header::HeaderValue, multipart::Form};
4use serde::{Deserialize, Serialize};
5
6use crate::{DateTime, Error, client::Client};
7
8const URL: &str = "https://apis.roblox.com/assets/user-auth/v1";
9
10#[derive(Clone, Debug, Deserialize, Serialize)]
11pub enum AssetType {
12    Decal,
13}
14
15#[derive(Clone, Debug, Deserialize, Serialize)]
16pub enum Creator {
17    #[serde(rename = "userId")]
18    // TODO: cast to u64
19    UserId(String), // can be sent as a u64, but it's returned as a string in some cases
20    #[serde(rename = "groupId")]
21    GroupId(String),
22}
23
24#[derive(Clone, Debug, Deserialize, Serialize)]
25pub struct CreationContext {
26    pub creator: Creator,
27    #[serde(rename = "expectedPrice")]
28    pub expected_price: Option<u64>,
29}
30
31#[derive(Clone, Debug, Deserialize)]
32pub struct ModerationResult {
33    #[serde(rename = "moderationState")]
34    pub state: String,
35}
36
37#[derive(Debug, Deserialize)]
38pub struct AssetUploadResponse {
39    pub path: String,
40    // TODO: cast to u64 please
41    #[serde(rename = "assetId")]
42    pub id: String,
43    #[serde(rename = "displayName")]
44    pub title: String,
45    #[serde(rename = "assetType")]
46    pub asset_type: AssetType,
47    pub state: String,
48    #[serde(rename = "revisionId")]
49    pub revision_id: String,
50    #[serde(rename = "revisionCreateTime")]
51    pub revision_creation_time: DateTime,
52    #[serde(rename = "creationContext")]
53    pub creation_context: CreationContext,
54    #[serde(rename = "moderationResult")]
55    pub moderation_result: ModerationResult,
56}
57
58#[derive(Debug, Deserialize)]
59pub struct AssetUploadStatus {
60    pub path: String,
61    #[serde(rename = "operationId")]
62    pub operation_id: String,
63    #[serde(rename = "done")]
64    pub complete: bool,
65    pub response: Option<AssetUploadResponse>,
66}
67
68// this api also takes in a patch request to update an exists asset "{URL}/assets/{id}"
69pub async fn upload(
70    client: &mut Client,
71    path: impl AsRef<Path>,
72    title: &str,
73    description: &str,
74    asset_type: AssetType,
75    creation_context: CreationContext,
76) -> Result<AssetUploadStatus, Error> {
77    let mut headers = client.requestor.default_headers.clone();
78    headers.insert("Accept", HeaderValue::from_str("*/*").unwrap());
79
80    #[derive(Clone, Debug, Deserialize, Serialize)]
81    struct Request<'a> {
82        #[serde(rename = "displayName")]
83        title: &'a str,
84        description: &'a str,
85        #[serde(rename = "assetType")]
86        asset_type: AssetType,
87        #[serde(rename = "creationContext")]
88        creation_context: CreationContext,
89    }
90
91    let request = serde_json::to_string(&Request {
92        title,
93        description,
94        asset_type,
95        creation_context,
96    })
97    .unwrap();
98
99    let result = client
100        .requestor
101        .client
102        .post(format!("{URL}/assets"))
103        .headers(headers)
104        .multipart(
105            Form::new()
106                .text("request", request)
107                .file("fileContent", &path)
108                .await
109                .unwrap(),
110        )
111        .send()
112        .await;
113
114    let response = client.validate_response(result).await?;
115    client
116        .requestor
117        .parse_json::<AssetUploadStatus>(response)
118        .await
119}
120
121pub async fn status(client: &mut Client, operation_id: &str) -> Result<AssetUploadStatus, Error> {
122    let result = client
123        .requestor
124        .client
125        .get(format!("{URL}/operations/{operation_id}"))
126        .headers(client.requestor.default_headers.clone())
127        .send()
128        .await;
129
130    let response = client.validate_response(result).await?;
131    client
132        .requestor
133        .parse_json::<AssetUploadStatus>(response)
134        .await
135}