feat: Update typegen enums and the UI
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -123,7 +123,9 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tap",
|
"tap",
|
||||||
"thiserror 2.0.17",
|
"thiserror 2.0.17",
|
||||||
|
"tokio",
|
||||||
"tokio-test",
|
"tokio-test",
|
||||||
|
"toml 0.9.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ serde = { version = "1.0.228", features = ["derive"] }
|
|||||||
serde_json = "1.0.145"
|
serde_json = "1.0.145"
|
||||||
tap = "1.0.1"
|
tap = "1.0.1"
|
||||||
thiserror = "2.0.17"
|
thiserror = "2.0.17"
|
||||||
|
tokio = { version = "1.48.0", features = ["fs"] }
|
||||||
|
toml = "0.9.8"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] }
|
||||||
tokio-test = "0.4.4"
|
tokio-test = "0.4.4"
|
||||||
|
|||||||
16
api/examples/items.rs
Normal file
16
api/examples/items.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use api::*;
|
||||||
|
#[tokio::main]
|
||||||
|
pub async fn main() {
|
||||||
|
let config = std::fs::read_to_string("config.toml").expect("Config.toml");
|
||||||
|
let config: JellyfinConfig =
|
||||||
|
toml::from_str(&config).expect("Failed to parse config.toml");
|
||||||
|
|
||||||
|
let mut jellyfin = JellyfinClient::new(config);
|
||||||
|
jellyfin.authenticate_with_cached_token(".session").await.expect("Auth");
|
||||||
|
let items = jellyfin.raw_items().await.expect("Items");
|
||||||
|
std::fs::write(
|
||||||
|
"items.json",
|
||||||
|
serde_json::to_string_pretty(&items).expect("Serialize items"),
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
102
api/src/lib.rs
102
api/src/lib.rs
@@ -1,6 +1,7 @@
|
|||||||
pub mod jellyfin;
|
pub mod jellyfin;
|
||||||
|
|
||||||
use ::tap::*;
|
use ::tap::*;
|
||||||
|
use reqwest::Method;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(thiserror::Error, Debug)]
|
#[derive(thiserror::Error, Debug)]
|
||||||
@@ -9,6 +10,8 @@ pub enum JellyfinApiError {
|
|||||||
ReqwestError(#[from] reqwest::Error),
|
ReqwestError(#[from] reqwest::Error),
|
||||||
#[error("Serialization/Deserialization error: {0}")]
|
#[error("Serialization/Deserialization error: {0}")]
|
||||||
SerdeError(#[from] serde_json::Error),
|
SerdeError(#[from] serde_json::Error),
|
||||||
|
#[error("IO error: {0}")]
|
||||||
|
IoError(#[from] std::io::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T, E = JellyfinApiError> = std::result::Result<T, E>;
|
type Result<T, E = JellyfinApiError> = std::result::Result<T, E>;
|
||||||
@@ -29,9 +32,9 @@ impl JellyfinClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save_token(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
pub async fn save_token(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||||
if let Some(token) = &self.access_token {
|
if let Some(token) = &self.access_token {
|
||||||
std::fs::write(path, token)
|
tokio::fs::write(path, token).await
|
||||||
} else {
|
} else {
|
||||||
Err(std::io::Error::new(
|
Err(std::io::Error::new(
|
||||||
std::io::ErrorKind::Other,
|
std::io::ErrorKind::Other,
|
||||||
@@ -40,17 +43,20 @@ impl JellyfinClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_token(&mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
pub async fn load_token(&mut self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
|
||||||
let token = std::fs::read_to_string(path)?;
|
let token = tokio::fs::read_to_string(path).await?;
|
||||||
self.access_token = Some(token);
|
self.access_token = Some(token);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn post_builder(&self, uri: impl AsRef<str>) -> reqwest::RequestBuilder {
|
pub fn request_builder(
|
||||||
|
&self,
|
||||||
|
method: reqwest::Method,
|
||||||
|
uri: impl AsRef<str>,
|
||||||
|
) -> reqwest::RequestBuilder {
|
||||||
let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
|
let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
|
||||||
self.client.post(&url)
|
self.client.request(method, &url)
|
||||||
.header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
|
.header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
|
||||||
.header("Content-Type", "application/json")
|
|
||||||
.pipe(|builder| {
|
.pipe(|builder| {
|
||||||
if let Some(token) = &self.access_token {
|
if let Some(token) = &self.access_token {
|
||||||
builder.header("X-MediaBrowser-Token", token)
|
builder.header("X-MediaBrowser-Token", token)
|
||||||
@@ -60,18 +66,18 @@ impl JellyfinClient {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_builder(&self, uri: impl AsRef<str>) -> reqwest::RequestBuilder {
|
// pub fn get_builder(&self, uri: impl AsRef<str>) -> reqwest::RequestBuilder {
|
||||||
let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
|
// let url = format!("{}/{}", self.config.server_url.as_str(), uri.as_ref());
|
||||||
self.client.get(&url)
|
// self.client.get(&url)
|
||||||
.header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
|
// .header("X-Emby-Authorization", format!("MediaBrowser Client=\"Jello\", Device=\"Jello\", DeviceId=\"{}\", Version=\"1.0.0\"", self.config.device_id))
|
||||||
.pipe(|builder| {
|
// .pipe(|builder| {
|
||||||
if let Some(token) = &self.access_token {
|
// if let Some(token) = &self.access_token {
|
||||||
builder.header("X-MediaBrowser-Token", token)
|
// builder.header("X-MediaBrowser-Token", token)
|
||||||
} else {
|
// } else {
|
||||||
builder
|
// builder
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub async fn post<T: Serialize + ?Sized, U: serde::de::DeserializeOwned>(
|
pub async fn post<T: Serialize + ?Sized, U: serde::de::DeserializeOwned>(
|
||||||
&self,
|
&self,
|
||||||
@@ -79,7 +85,7 @@ impl JellyfinClient {
|
|||||||
body: &T,
|
body: &T,
|
||||||
) -> Result<U> {
|
) -> Result<U> {
|
||||||
let text = self
|
let text = self
|
||||||
.post_builder(uri)
|
.request_builder(reqwest::Method::POST, uri)
|
||||||
.json(body)
|
.json(body)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
@@ -92,7 +98,7 @@ impl JellyfinClient {
|
|||||||
|
|
||||||
pub async fn get<U: serde::de::DeserializeOwned>(&self, uri: impl AsRef<str>) -> Result<U> {
|
pub async fn get<U: serde::de::DeserializeOwned>(&self, uri: impl AsRef<str>) -> Result<U> {
|
||||||
let text = self
|
let text = self
|
||||||
.get_builder(uri)
|
.request_builder(reqwest::Method::GET, uri)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status()?
|
||||||
@@ -103,25 +109,34 @@ impl JellyfinClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn authenticate(&mut self) -> Result<jellyfin::AuthenticationResult> {
|
pub async fn authenticate(&mut self) -> Result<jellyfin::AuthenticationResult> {
|
||||||
let out = self
|
let auth_result: jellyfin::AuthenticationResult = self
|
||||||
.post_builder("Users/AuthenticateByName")
|
.post(
|
||||||
.json(&jellyfin::AuthenticateUserByName {
|
"Users/AuthenticateByName",
|
||||||
|
&jellyfin::AuthenticateUserByName {
|
||||||
username: Some(self.config.username.clone()),
|
username: Some(self.config.username.clone()),
|
||||||
pw: Some(self.config.password.clone()),
|
pw: Some(self.config.password.clone()),
|
||||||
})
|
},
|
||||||
.send()
|
)
|
||||||
.await?
|
|
||||||
.error_for_status()?
|
|
||||||
.text()
|
|
||||||
.await?;
|
.await?;
|
||||||
let auth_result: jellyfin::AuthenticationResult = serde_json::from_str(&out)?;
|
|
||||||
self.access_token = auth_result.access_token.clone();
|
self.access_token = auth_result.access_token.clone();
|
||||||
Ok(auth_result)
|
Ok(auth_result)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn items(&self) -> Result<jellyfin::BaseItemDtoQueryResult> {
|
pub async fn authenticate_with_cached_token(
|
||||||
|
&mut self,
|
||||||
|
path: impl AsRef<std::path::Path>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
if !self.load_token(path).await.is_ok() {
|
||||||
|
self.authenticate().await?;
|
||||||
|
self.save_token(path).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn raw_items(&self) -> Result<jellyfin::BaseItemDtoQueryResult> {
|
||||||
let text = &self
|
let text = &self
|
||||||
.get_builder("/Items")
|
.request_builder(Method::GET, "Items")
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.error_for_status()?
|
.error_for_status()?
|
||||||
@@ -130,8 +145,31 @@ impl JellyfinClient {
|
|||||||
let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?;
|
let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?;
|
||||||
Ok(out)
|
Ok(out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn items(
|
||||||
|
&self,
|
||||||
|
root: impl Into<Option<String>>,
|
||||||
|
) -> Result<Vec<jellyfin::BaseItemDto>> {
|
||||||
|
let text = &self
|
||||||
|
.request_builder(Method::GET, "Items")
|
||||||
|
.query(&[("parentId", root.into())])
|
||||||
|
.send()
|
||||||
|
.await?
|
||||||
|
.error_for_status()?
|
||||||
|
.text()
|
||||||
|
.await?;
|
||||||
|
let out: jellyfin::BaseItemDtoQueryResult = serde_json::from_str(&text)?;
|
||||||
|
Ok(out.items)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pub trait Item {
|
||||||
|
// fn id(&self) -> &str;
|
||||||
|
// fn name(&self) -> &str;
|
||||||
|
// fn type_(&self) -> jellyfin::BaseItemKind;
|
||||||
|
// fn media_type(&self) -> &str;
|
||||||
|
// }
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||||
pub struct JellyfinConfig {
|
pub struct JellyfinConfig {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|||||||
@@ -4418,13 +4418,13 @@
|
|||||||
"description": "Configuration.",
|
"description": "Configuration.",
|
||||||
"content": {
|
"content": {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
"schema": { }
|
"schema": {}
|
||||||
},
|
},
|
||||||
"text/json": {
|
"text/json": {
|
||||||
"schema": { }
|
"schema": {}
|
||||||
},
|
},
|
||||||
"application/*+json": {
|
"application/*+json": {
|
||||||
"schema": { }
|
"schema": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": true
|
"required": true
|
||||||
@@ -54485,7 +54485,7 @@
|
|||||||
"nullable": true
|
"nullable": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"additionalProperties": { }
|
"additionalProperties": {}
|
||||||
},
|
},
|
||||||
"ProcessPriorityClass": {
|
"ProcessPriorityClass": {
|
||||||
"enum": [
|
"enum": [
|
||||||
@@ -60446,3 +60446,4 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
23
src/main.rs
23
src/main.rs
@@ -1,12 +1,8 @@
|
|||||||
mod errors;
|
mod errors;
|
||||||
|
mod ui;
|
||||||
use api::{JellyfinClient, JellyfinConfig};
|
use api::{JellyfinClient, JellyfinConfig};
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
use gpui::{
|
|
||||||
App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, div,
|
|
||||||
prelude::*, px, rgb, size,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
pub async fn main() -> Result<()> {
|
pub async fn main() -> Result<()> {
|
||||||
dotenvy::dotenv()
|
dotenvy::dotenv()
|
||||||
@@ -22,17 +18,12 @@ pub async fn main() -> Result<()> {
|
|||||||
"jello".to_string(),
|
"jello".to_string(),
|
||||||
);
|
);
|
||||||
let mut jellyfin = api::JellyfinClient::new(config);
|
let mut jellyfin = api::JellyfinClient::new(config);
|
||||||
authenticate(&mut jellyfin).await?;
|
jellyfin
|
||||||
|
.authenticate_with_cached_token(".session")
|
||||||
|
.await
|
||||||
|
.change_context(Error)?;
|
||||||
|
|
||||||
|
ui::ui(jellyfin);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn authenticate(client: &mut JellyfinClient) -> Result<()> {
|
|
||||||
if std::path::PathBuf::from(".session").exists() {
|
|
||||||
client.load_token(".session").change_context(Error)?;
|
|
||||||
} else {
|
|
||||||
client.authenticate().await.change_context(Error)?;
|
|
||||||
client.save_token(".session").change_context(Error)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|||||||
165
src/ui.rs
165
src/ui.rs
@@ -0,0 +1,165 @@
|
|||||||
|
mod movies;
|
||||||
|
mod player;
|
||||||
|
mod series;
|
||||||
|
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use gpui::{
|
||||||
|
App, Application, Bounds, Context, SharedString, Window, WindowBounds, WindowOptions, actions,
|
||||||
|
div, prelude::*, px, rgb, size,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AppState {
|
||||||
|
pub title: SharedString,
|
||||||
|
pub items: BTreeMap<SharedString, Item>,
|
||||||
|
pub current_item: Option<SharedString>,
|
||||||
|
pub errors: Vec<String>,
|
||||||
|
pub jellyfin_client: api::JellyfinClient,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Item {
|
||||||
|
pub id: SharedString,
|
||||||
|
pub name: SharedString,
|
||||||
|
pub item_type: SharedString,
|
||||||
|
pub media_type: SharedString,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Render for AppState {
|
||||||
|
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.size_full()
|
||||||
|
.justify_center()
|
||||||
|
.text_color(rgb(0xffffff))
|
||||||
|
.child(Self::header())
|
||||||
|
.child(Self::body(self, window, cx))
|
||||||
|
.child(Self::footer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
actions!(jello_actions, [OpenItem, OnLoadItem,]);
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
fn new(title: impl AsRef<str>, jellyfin_client: api::JellyfinClient) -> Self {
|
||||||
|
AppState {
|
||||||
|
title: SharedString::new(title.as_ref()),
|
||||||
|
items: BTreeMap::new(),
|
||||||
|
current_item: None,
|
||||||
|
errors: Vec::new(),
|
||||||
|
jellyfin_client,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fn on_open_item(&mut self, _: &OpenItem, _: &mut Window, cx: &mut Context<Self>) {
|
||||||
|
// self.current_item = Some(item.0.clone());
|
||||||
|
// }
|
||||||
|
|
||||||
|
fn header() -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.w_full()
|
||||||
|
.justify_end()
|
||||||
|
.h_20()
|
||||||
|
.border_10()
|
||||||
|
.bg(rgb(0x333333))
|
||||||
|
.child(Self::button("Refresh"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn footer() -> impl IntoElement {
|
||||||
|
div().flex().flex_row().w_full().h_20().bg(rgb(0x333333))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn body(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_row()
|
||||||
|
.size_full()
|
||||||
|
.child(Self::content(self, window, cx))
|
||||||
|
.child(Self::sidebar(self, window, cx))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn button(label: &str) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.justify_center()
|
||||||
|
.items_center()
|
||||||
|
.bg(rgb(0xff00ff))
|
||||||
|
.text_color(rgb(0xffffff))
|
||||||
|
.border_5()
|
||||||
|
.rounded_lg()
|
||||||
|
.child(label.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn content(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.debug_below()
|
||||||
|
.w_3_4()
|
||||||
|
// .flex()
|
||||||
|
// .flex_wrap()
|
||||||
|
.bg(rgb(0x111111))
|
||||||
|
.justify_start()
|
||||||
|
.items_start()
|
||||||
|
.overflow_hidden()
|
||||||
|
.child(
|
||||||
|
div()
|
||||||
|
.size_full()
|
||||||
|
.flex()
|
||||||
|
.flex_wrap()
|
||||||
|
.justify_start()
|
||||||
|
.items_start()
|
||||||
|
.content_start()
|
||||||
|
.gap_y_10()
|
||||||
|
.gap_x_10()
|
||||||
|
.border_t_10()
|
||||||
|
.p_5()
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card())
|
||||||
|
.child(Self::card()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sidebar(&mut self, window: &mut Window, cx: &mut Context<AppState>) -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.w_1_4()
|
||||||
|
.min_w_1_6()
|
||||||
|
.bg(rgb(0x222222))
|
||||||
|
.child(div().size_full().bg(gpui::yellow()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn card() -> impl IntoElement {
|
||||||
|
div()
|
||||||
|
.flex()
|
||||||
|
.flex_col()
|
||||||
|
.w_48()
|
||||||
|
.h_64()
|
||||||
|
.p_10()
|
||||||
|
.bg(rgb(0xff00ff))
|
||||||
|
.rounded_lg()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(jellyfin_client: api::JellyfinClient) {
|
||||||
|
Application::new().run(|cx: &mut App| {
|
||||||
|
let bounds = Bounds::centered(None, size(px(500.0), px(500.0)), cx);
|
||||||
|
cx.open_window(
|
||||||
|
WindowOptions {
|
||||||
|
window_bounds: Some(WindowBounds::Windowed(bounds)),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
|_, cx| cx.new(|_| AppState::new("Jello Media Browser", jellyfin_client)),
|
||||||
|
)
|
||||||
|
.expect("Failed to open window");
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -185,14 +185,20 @@ fn main() {
|
|||||||
.expect("Possible oneOf")
|
.expect("Possible oneOf")
|
||||||
.iter()
|
.iter()
|
||||||
.map(|variant| {
|
.map(|variant| {
|
||||||
// let variant_name = modify_keyword(&ref_name.to_pascal_case());
|
let og_variant = variant.clone();
|
||||||
syn::Ident::new(&variant.to_pascal_case(), proc_macro2::Span::call_site())
|
let name =
|
||||||
|
syn::Ident::new(&variant.to_pascal_case(), proc_macro2::Span::call_site());
|
||||||
|
syn::parse_quote! {
|
||||||
|
#[serde(rename = #og_variant)]
|
||||||
|
#name
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.collect::<Vec<syn::Ident>>();
|
.collect::<Vec<syn::Variant>>();
|
||||||
let key = modify_keyword(key);
|
let key = modify_keyword(key);
|
||||||
let key = syn::Ident::new(&key.to_pascal_case(), proc_macro2::Span::call_site());
|
let key = syn::Ident::new(&key.to_pascal_case(), proc_macro2::Span::call_site());
|
||||||
let tokens = quote::quote! {
|
let tokens = quote::quote! {
|
||||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
#[serde(rename_all = "PascalCase")]
|
||||||
pub enum #key {
|
pub enum #key {
|
||||||
#(#variants),*
|
#(#variants),*
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user