chore: bump rust edition to 2024

This commit is contained in:
alyssa 2025-09-01 03:36:13 +00:00
parent 214f164fbc
commit ebb23286d8
41 changed files with 70 additions and 69 deletions

View file

@ -11,6 +11,7 @@
!Cargo.toml !Cargo.toml
!Cargo.lock !Cargo.lock
!rust-toolchain.toml
!PluralKit.sln !PluralKit.sln
!nuget.config !nuget.config
!ci/dotnet-version.sh !ci/dotnet-version.sh

View file

@ -4,7 +4,8 @@ WORKDIR /build
RUN apk add rustup build-base RUN apk add rustup build-base
# todo: arm64 target # todo: arm64 target
RUN rustup-init --default-host x86_64-unknown-linux-musl --default-toolchain nightly-2024-08-20 --profile default -y COPY rust-toolchain.toml .
RUN rustup-init --no-update-default-toolchain -y
ENV PATH=/root/.cargo/bin:$PATH ENV PATH=/root/.cargo/bin:$PATH
ENV RUSTFLAGS='-C link-arg=-s' ENV RUSTFLAGS='-C link-arg=-s'

View file

@ -1,7 +1,7 @@
[package] [package]
name = "api" name = "api"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
pluralkit_models = { path = "../models" } pluralkit_models = { path = "../models" }

View file

@ -4,9 +4,10 @@ use fred::interfaces::*;
use libpk::state::ShardState; use libpk::state::ShardState;
use pk_macros::api_endpoint; use pk_macros::api_endpoint;
use serde::Deserialize; use serde::Deserialize;
use serde_json::{json, Value}; use serde_json::{Value, json};
use std::collections::HashMap; use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(rename_all = "PascalCase")] #[serde(rename_all = "PascalCase")]
struct ClusterStats { struct ClusterStats {

View file

@ -1,11 +1,11 @@
use axum::{extract::State, response::IntoResponse, Extension, Json}; use axum::{Extension, Json, extract::State, response::IntoResponse};
use pk_macros::api_endpoint; use pk_macros::api_endpoint;
use serde_json::{json, Value}; use serde_json::{Value, json};
use sqlx::Postgres; use sqlx::Postgres;
use pluralkit_models::{PKSystem, PKSystemConfig, PrivacyLevel}; use pluralkit_models::{PKSystem, PKSystemConfig, PrivacyLevel};
use crate::{auth::AuthState, error::fail, ApiContext}; use crate::{ApiContext, auth::AuthState, error::fail};
#[api_endpoint] #[api_endpoint]
pub async fn get_system_settings( pub async fn get_system_settings(

View file

@ -1,16 +1,14 @@
#![feature(let_chains)]
use auth::{AuthState, INTERNAL_APPID_HEADER, INTERNAL_SYSTEMID_HEADER}; use auth::{AuthState, INTERNAL_APPID_HEADER, INTERNAL_SYSTEMID_HEADER};
use axum::{ use axum::{
Extension, Router,
body::Body, body::Body,
extract::{Request as ExtractRequest, State}, extract::{Request as ExtractRequest, State},
http::Uri, http::Uri,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
routing::{delete, get, patch, post}, routing::{delete, get, patch, post},
Extension, Router,
}; };
use hyper_util::{ use hyper_util::{
client::legacy::{connect::HttpConnector, Client}, client::legacy::{Client, connect::HttpConnector},
rt::TokioExecutor, rt::TokioExecutor,
}; };
use tracing::info; use tracing::info;

View file

@ -10,7 +10,7 @@ use subtle::ConstantTimeEq;
use tracing::error; use tracing::error;
use crate::auth::AuthState; use crate::auth::AuthState;
use crate::{util::json_err, ApiContext}; use crate::{ApiContext, util::json_err};
pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -> Response { pub async fn auth(State(ctx): State<ApiContext>, mut req: Request, next: Next) -> Response {
let mut authed_system_id: Option<i32> = None; let mut authed_system_id: Option<i32> = None;

View file

@ -2,7 +2,7 @@ use std::time::Instant;
use axum::{extract::MatchedPath, extract::Request, middleware::Next, response::Response}; use axum::{extract::MatchedPath, extract::Request, middleware::Next, response::Response};
use metrics::{counter, histogram}; use metrics::{counter, histogram};
use tracing::{info, span, warn, Instrument, Level}; use tracing::{Instrument, Level, info, span, warn};
use crate::{auth::AuthState, util::header_or_unknown}; use crate::{auth::AuthState, util::header_or_unknown};

View file

@ -6,11 +6,11 @@ use axum::{
routing::url_params::UrlParams, routing::url_params::UrlParams,
}; };
use sqlx::{types::Uuid, Postgres}; use sqlx::{Postgres, types::Uuid};
use tracing::error; use tracing::error;
use crate::auth::AuthState; use crate::auth::AuthState;
use crate::{util::json_err, ApiContext}; use crate::{ApiContext, util::json_err};
use pluralkit_models::PKSystem; use pluralkit_models::PKSystem;
// move this somewhere else // move this somewhere else
@ -31,7 +31,7 @@ pub async fn params(State(ctx): State<ApiContext>, mut req: Request, next: Next)
StatusCode::BAD_REQUEST, StatusCode::BAD_REQUEST,
r#"{"message":"400: Bad Request","code": 0}"#.to_string(), r#"{"message":"400: Bad Request","code": 0}"#.to_string(),
) )
.into() .into();
} }
}; };

View file

@ -3,7 +3,7 @@ use axum::{
http::{HeaderValue, StatusCode}, http::{HeaderValue, StatusCode},
response::IntoResponse, response::IntoResponse,
}; };
use serde_json::{json, to_string, Value}; use serde_json::{Value, json, to_string};
use tracing::error; use tracing::error;
pub fn header_or_unknown(header: Option<&HeaderValue>) -> &str { pub fn header_or_unknown(header: Option<&HeaderValue>) -> &str {

View file

@ -1,7 +1,7 @@
[package] [package]
name = "avatars" name = "avatars"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[[bin]] [[bin]]
name = "avatar_cleanup" name = "avatar_cleanup"

View file

@ -8,10 +8,10 @@ use anyhow::Context;
use axum::extract::State; use axum::extract::State;
use axum::routing::get; use axum::routing::get;
use axum::{ use axum::{
Json, Router,
http::StatusCode, http::StatusCode,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
routing::post, routing::post,
Json, Router,
}; };
use libpk::_config::AvatarsConfig; use libpk::_config::AvatarsConfig;
use libpk::db::repository::avatars as db; use libpk::db::repository::avatars as db;
@ -153,7 +153,7 @@ async fn verify(
) )
.await?; .await?;
let encoded = process::process_async(result.data, req.kind).await?; process::process_async(result.data, req.kind).await?;
Ok(()) Ok(())
} }

View file

@ -4,7 +4,7 @@ use std::io::Cursor;
use std::time::Instant; use std::time::Instant;
use tracing::{debug, error, info, instrument}; use tracing::{debug, error, info, instrument};
use crate::{hash::Hash, ImageKind, PKAvatarError}; use crate::{ImageKind, PKAvatarError, hash::Hash};
const MAX_DIMENSION: u32 = 4000; const MAX_DIMENSION: u32 = 4000;

View file

@ -62,7 +62,7 @@ pub async fn pull(
let size = match response.content_length() { let size = match response.content_length() {
None => return Err(PKAvatarError::MissingHeader("Content-Length")), None => return Err(PKAvatarError::MissingHeader("Content-Length")),
Some(size) if size > MAX_SIZE => { Some(size) if size > MAX_SIZE => {
return Err(PKAvatarError::ImageFileSizeTooLarge(size, MAX_SIZE)) return Err(PKAvatarError::ImageFileSizeTooLarge(size, MAX_SIZE));
} }
Some(size) => size, Some(size) => size,
}; };
@ -162,7 +162,7 @@ pub fn parse_url(url: &str) -> anyhow::Result<ParsedUrl> {
attachment_id: 0, attachment_id: 0,
filename: "".to_string(), filename: "".to_string(),
full_url: url.to_string(), full_url: url.to_string(),
}) });
} }
_ => anyhow::bail!("not a discord cdn url"), _ => anyhow::bail!("not a discord cdn url"),
} }

View file

@ -1,7 +1,7 @@
[package] [package]
name = "dispatch" name = "dispatch"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }

View file

@ -1,7 +1,7 @@
use std::time::Instant; use std::time::Instant;
use axum::{extract::MatchedPath, extract::Request, middleware::Next, response::Response}; use axum::{extract::MatchedPath, extract::Request, middleware::Next, response::Response};
use tracing::{info, span, warn, Instrument, Level}; use tracing::{Instrument, Level, info, span, warn};
// log any requests that take longer than 2 seconds // log any requests that take longer than 2 seconds
// todo: change as necessary // todo: change as necessary

View file

@ -5,17 +5,16 @@ use hickory_client::{
rr::{DNSClass, Name, RData, RecordType}, rr::{DNSClass, Name, RData, RecordType},
udp::UdpClientStream, udp::UdpClientStream,
}; };
use reqwest::{redirect::Policy, StatusCode}; use reqwest::{StatusCode, redirect::Policy};
use std::{ use std::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4}, net::{Ipv4Addr, SocketAddr, SocketAddrV4},
sync::Arc, sync::Arc,
time::Duration, time::Duration,
}; };
use tokio::{net::UdpSocket, sync::RwLock}; use tokio::{net::UdpSocket, sync::RwLock};
use tracing::{debug, error, info}; use tracing::{debug, error};
use tracing_subscriber::EnvFilter;
use axum::{extract::State, http::Uri, routing::post, Json, Router}; use axum::{Json, Router, extract::State, http::Uri, routing::post};
mod logger; mod logger;
@ -128,7 +127,7 @@ async fn dispatch(
match res { match res {
Ok(res) if res.status() != 200 => { Ok(res) if res.status() != 200 => {
return DispatchResponse::InvalidResponseCode(res.status()).to_string() return DispatchResponse::InvalidResponseCode(res.status()).to_string();
} }
Err(error) => { Err(error) => {
error!(?error, url = req.url.clone(), "failed to fetch"); error!(?error, url = req.url.clone(), "failed to fetch");

View file

@ -1,7 +1,7 @@
[package] [package]
name = "gateway" name = "gateway"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }

View file

@ -1,18 +1,18 @@
use axum::{ use axum::{
Router,
extract::{ConnectInfo, Path, State}, extract::{ConnectInfo, Path, State},
http::StatusCode, http::StatusCode,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
routing::{delete, get, post}, routing::{delete, get, post},
Router,
}; };
use libpk::runtime_config::RuntimeConfig; use libpk::runtime_config::RuntimeConfig;
use serde_json::{json, to_string}; use serde_json::{json, to_string};
use tracing::{error, info}; use tracing::{error, info};
use twilight_model::id::{marker::ChannelMarker, Id}; use twilight_model::id::{Id, marker::ChannelMarker};
use crate::{ use crate::{
discord::{ discord::{
cache::{dm_channel, DiscordCache, DM_PERMISSIONS}, cache::{DM_PERMISSIONS, DiscordCache, dm_channel},
gateway::cluster_config, gateway::cluster_config,
shard_state::ShardStateManager, shard_state::ShardStateManager,
}, },

View file

@ -4,18 +4,18 @@ use serde::Serialize;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use twilight_cache_inmemory::{ use twilight_cache_inmemory::{
InMemoryCache, ResourceType,
model::CachedMember, model::CachedMember,
permission::{MemberRoles, RootError}, permission::{MemberRoles, RootError},
traits::CacheableChannel, traits::CacheableChannel,
InMemoryCache, ResourceType,
}; };
use twilight_gateway::Event; use twilight_gateway::Event;
use twilight_model::{ use twilight_model::{
channel::{Channel, ChannelType}, channel::{Channel, ChannelType},
guild::{Guild, Member, Permissions}, guild::{Guild, Member, Permissions},
id::{ id::{
marker::{ChannelMarker, GuildMarker, MessageMarker, UserMarker},
Id, Id,
marker::{ChannelMarker, GuildMarker, MessageMarker, UserMarker},
}, },
}; };
use twilight_util::permission_calculator::PermissionCalculator; use twilight_util::permission_calculator::PermissionCalculator;

View file

@ -6,17 +6,17 @@ use std::sync::Arc;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use twilight_gateway::{ use twilight_gateway::{
create_iterator, CloseFrame, ConfigBuilder, Event, EventTypeFlags, Message, Shard, ShardId, CloseFrame, ConfigBuilder, Event, EventTypeFlags, Message, Shard, ShardId, create_iterator,
}; };
use twilight_model::gateway::{ use twilight_model::gateway::{
Intents,
payload::outgoing::update_presence::UpdatePresencePayload, payload::outgoing::update_presence::UpdatePresencePayload,
presence::{Activity, ActivityType, Status}, presence::{Activity, ActivityType, Status},
Intents,
}; };
use crate::{ use crate::{
discord::identify_queue::{self, RedisQueue},
RUNTIME_CONFIG_KEY_EVENT_TARGET, RUNTIME_CONFIG_KEY_EVENT_TARGET,
discord::identify_queue::{self, RedisQueue},
}; };
use super::cache::DiscordCache; use super::cache::DiscordCache;

View file

@ -3,7 +3,7 @@
// - interaction: (custom_id where not_includes "help-menu") // - interaction: (custom_id where not_includes "help-menu")
use std::{ use std::{
collections::{hash_map::Entry, HashMap}, collections::{HashMap, hash_map::Entry},
net::{IpAddr, SocketAddr}, net::{IpAddr, SocketAddr},
time::Duration, time::Duration,
}; };
@ -15,8 +15,8 @@ use twilight_gateway::Event;
use twilight_model::{ use twilight_model::{
application::interaction::InteractionData, application::interaction::InteractionData,
id::{ id::{
marker::{ChannelMarker, MessageMarker, UserMarker},
Id, Id,
marker::{ChannelMarker, MessageMarker, UserMarker},
}, },
}; };
@ -103,7 +103,13 @@ impl EventAwaiter {
} }
} }
} }
info!("ran event_awaiter cleanup loop, took {}us, {} reactions, {} messages, {} interactions", Instant::now().duration_since(now).as_micros(), counts.0, counts.1, counts.2); info!(
"ran event_awaiter cleanup loop, took {}us, {} reactions, {} messages, {} interactions",
Instant::now().duration_since(now).as_micros(),
counts.0,
counts.1,
counts.2
);
} }
} }

View file

@ -4,7 +4,7 @@ use axum::{
extract::MatchedPath, extract::Request, http::StatusCode, middleware::Next, response::Response, extract::MatchedPath, extract::Request, http::StatusCode, middleware::Next, response::Response,
}; };
use metrics::{counter, histogram}; use metrics::{counter, histogram};
use tracing::{info, span, warn, Instrument, Level}; use tracing::{Instrument, Level, info, span, warn};
// log any requests that take longer than 2 seconds // log any requests that take longer than 2 seconds
// todo: change as necessary // todo: change as necessary

View file

@ -1,4 +1,3 @@
#![feature(let_chains)]
#![feature(if_let_guard)] #![feature(if_let_guard)]
#![feature(duration_constructors)] #![feature(duration_constructors)]
@ -10,7 +9,7 @@ use libpk::{runtime_config::RuntimeConfig, state::ShardStateEvent};
use reqwest::{ClientBuilder, StatusCode}; use reqwest::{ClientBuilder, StatusCode};
use std::{sync::Arc, time::Duration, vec::Vec}; use std::{sync::Arc, time::Duration, vec::Vec};
use tokio::{ use tokio::{
signal::unix::{signal, SignalKind}, signal::unix::{SignalKind, signal},
sync::mpsc::channel, sync::mpsc::channel,
task::JoinSet, task::JoinSet,
}; };

View file

@ -1,7 +1,7 @@
[package] [package]
name = "gdpr_worker" name = "gdpr_worker"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
libpk = { path = "../libpk" } libpk = { path = "../libpk" }

View file

@ -1,12 +1,10 @@
#![feature(let_chains)]
use sqlx::prelude::FromRow; use sqlx::prelude::FromRow;
use std::{sync::Arc, time::Duration}; use std::{sync::Arc, time::Duration};
use tracing::{error, info, warn}; use tracing::{error, info, warn};
use twilight_http::api_error::{ApiError, GeneralApiError}; use twilight_http::api_error::{ApiError, GeneralApiError};
use twilight_model::id::{ use twilight_model::id::{
marker::{ChannelMarker, MessageMarker},
Id, Id,
marker::{ChannelMarker, MessageMarker},
}; };
// create table messages_gdpr_jobs (mid bigint not null references messages(mid) on delete cascade, channel bigint not null); // create table messages_gdpr_jobs (mid bigint not null references messages(mid) on delete cascade, channel bigint not null);

View file

@ -1,7 +1,7 @@
[package] [package]
name = "libpk" name = "libpk"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
anyhow = { workspace = true } anyhow = { workspace = true }

View file

@ -3,7 +3,7 @@ use lazy_static::lazy_static;
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
use twilight_model::id::{marker::UserMarker, Id}; use twilight_model::id::{Id, marker::UserMarker};
#[derive(Clone, Deserialize, Debug)] #[derive(Clone, Deserialize, Debug)]
pub struct ClusterSettings { pub struct ClusterSettings {
@ -151,11 +151,11 @@ lazy_static! {
// hacks // hacks
if let Ok(var) = std::env::var("NOMAD_ALLOC_INDEX") if let Ok(var) = std::env::var("NOMAD_ALLOC_INDEX")
&& std::env::var("pluralkit__discord__cluster__total_nodes").is_ok() { && std::env::var("pluralkit__discord__cluster__total_nodes").is_ok() {
std::env::set_var("pluralkit__discord__cluster__node_id", var); unsafe { std::env::set_var("pluralkit__discord__cluster__node_id", var); }
} }
if let Ok(var) = std::env::var("STATEFULSET_NAME_FOR_INDEX") if let Ok(var) = std::env::var("STATEFULSET_NAME_FOR_INDEX")
&& std::env::var("pluralkit__discord__cluster__total_nodes").is_ok() { && std::env::var("pluralkit__discord__cluster__total_nodes").is_ok() {
std::env::set_var("pluralkit__discord__cluster__node_id", var.split("-").last().unwrap()); unsafe { std::env::set_var("pluralkit__discord__cluster__node_id", var.split("-").last().unwrap()); }
} }
Arc::new(Config::builder() Arc::new(Config::builder()

View file

@ -52,7 +52,7 @@ pub async fn remove_deletion_queue(pool: &PgPool, attachment_id: u64) -> anyhow:
pub async fn pop_queue( pub async fn pop_queue(
pool: &PgPool, pool: &PgPool,
) -> anyhow::Result<Option<(Transaction<Postgres>, ImageQueueEntry)>> { ) -> anyhow::Result<Option<(Transaction<'_, Postgres>, ImageQueueEntry)>> {
let mut tx = pool.begin().await?; let mut tx = pool.begin().await?;
let res: Option<ImageQueueEntry> = sqlx::query_as("delete from image_queue where itemid = (select itemid from image_queue order by itemid for update skip locked limit 1) returning *") let res: Option<ImageQueueEntry> = sqlx::query_as("delete from image_queue where itemid = (select itemid from image_queue order by itemid for update skip locked limit 1) returning *")
.fetch_optional(&mut *tx).await?; .fetch_optional(&mut *tx).await?;

View file

@ -1,7 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use sqlx::{ use sqlx::{
types::chrono::{DateTime, Utc},
FromRow, FromRow,
types::chrono::{DateTime, Utc},
}; };
use uuid::Uuid; use uuid::Uuid;

View file

@ -1,9 +1,8 @@
#![feature(let_chains)]
use std::net::SocketAddr; use std::net::SocketAddr;
use metrics_exporter_prometheus::PrometheusBuilder; use metrics_exporter_prometheus::PrometheusBuilder;
use sentry::IntoDsn; use sentry::IntoDsn;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter}; use tracing_subscriber::{EnvFilter, layer::SubscriberExt, util::SubscriberInitExt};
use sentry_tracing::event_from_event; use sentry_tracing::event_from_event;

View file

@ -1,7 +1,7 @@
[package] [package]
name = "pk_macros" name = "pk_macros"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[lib] [lib]
proc-macro = true proc-macro = true

View file

@ -1,7 +1,7 @@
use quote::quote; use quote::quote;
use syn::{parse_macro_input, FnArg, ItemFn, Pat}; use syn::{FnArg, ItemFn, Pat, parse_macro_input};
fn pretty_print(ts: &proc_macro2::TokenStream) -> String { fn _pretty_print(ts: &proc_macro2::TokenStream) -> String {
let file = syn::parse_file(&ts.to_string()).unwrap(); let file = syn::parse_file(&ts.to_string()).unwrap();
prettyplease::unparse(&file) prettyplease::unparse(&file)
} }

View file

@ -1,6 +1,6 @@
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use quote::quote; use quote::quote;
use syn::{parse_macro_input, DeriveInput, Expr, Ident, Meta, Type}; use syn::{DeriveInput, Expr, Ident, Meta, Type, parse_macro_input};
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum ElemPatchability { enum ElemPatchability {

View file

@ -1,7 +1,7 @@
[package] [package]
name = "migrate" name = "migrate"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
libpk = { path = "../libpk" } libpk = { path = "../libpk" }

View file

@ -1,5 +1,3 @@
#![feature(let_chains)]
use tracing::info; use tracing::info;
include!(concat!(env!("OUT_DIR"), "/data.rs")); include!(concat!(env!("OUT_DIR"), "/data.rs"));

View file

@ -1,7 +1,7 @@
[package] [package]
name = "pluralkit_models" name = "pluralkit_models"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
chrono = { workspace = true, features = ["serde"] } chrono = { workspace = true, features = ["serde"] }

View file

@ -18,7 +18,7 @@ pub enum PrivacyLevel {
} }
// this sucks, put it somewhere else // this sucks, put it somewhere else
use sqlx::{postgres::PgTypeInfo, Database, Decode, Postgres, Type}; use sqlx::{Database, Decode, Postgres, Type, postgres::PgTypeInfo};
use std::error::Error; use std::error::Error;
_util::fake_enum_impls!(PrivacyLevel); _util::fake_enum_impls!(PrivacyLevel);

View file

@ -1,7 +1,7 @@
[package] [package]
name = "scheduled_tasks" name = "scheduled_tasks"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2024"
[dependencies] [dependencies]
libpk = { path = "../libpk" } libpk = { path = "../libpk" }

View file

@ -72,9 +72,7 @@
programs.nixfmt.enable = true; programs.nixfmt.enable = true;
}; };
nci.toolchainConfig = { nci.toolchainConfig = ./rust-toolchain.toml;
channel = "nightly";
};
nci.projects."pluralkit-services" = { nci.projects."pluralkit-services" = {
path = ./.; path = ./.;
export = false; export = false;

3
rust-toolchain.toml Normal file
View file

@ -0,0 +1,3 @@
[toolchain]
channel = "nightly-2025-08-22"
components = ["rust-src", "rustfmt", "rust-analyzer"]