Skip to content

Commit

Permalink
chore: add more lints, fix some clippy warnings
Browse files Browse the repository at this point in the history
Most notably, it adds `missing_docs`, `missing_crate_level_docs`,
and `missing_doc_code_examples`, which generates a ton of warnings for
now. They should all be resolved before the 0.1 release.
  • Loading branch information
m4tx committed Jan 27, 2025
1 parent 68f79b7 commit 4c3bed4
Show file tree
Hide file tree
Showing 34 changed files with 840 additions and 109 deletions.
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ edition = "2021"
license = "MIT OR Apache-2.0"
version = "0.1.0"

[workspace.lints.rust]
missing_debug_implementations = "warn"
missing_copy_implementations = "warn"
trivial_casts = "warn"
trivial_numeric_casts = "warn"
unreachable_pub = "warn"
unsafe_code = "warn"
unstable_features = "warn"
unused_import_braces = "warn"
unused_qualifications = "warn"
rust_2018_idioms = { level = "warn", priority = -1 }

[workspace.lints.clippy]
all = "deny"
pedantic = "warn"
Expand Down
4 changes: 3 additions & 1 deletion cot-cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(unreachable_pub)] // triggers false positives because we have both a binary and library

mod migration_generator;
mod new_project;
mod utils;
Expand Down Expand Up @@ -81,7 +83,7 @@ fn main() -> anyhow::Result<()> {
} else {
CotSource::PublishedCrate
};
new_project(&path, &project_name, cot_source)
new_project(&path, &project_name, &cot_source)
.with_context(|| "unable to create project")?;
}
Commands::MakeMigrations {
Expand Down
12 changes: 6 additions & 6 deletions cot-cli/src/migration_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use petgraph::graph::DiGraph;
use petgraph::visit::EdgeRef;
use proc_macro2::TokenStream;
use quote::{format_ident, quote, ToTokens};
use syn::{parse_quote, Attribute, Meta};
use syn::{parse_quote, Meta};
use tracing::{debug, info, trace};

use crate::utils::find_cargo_toml;
Expand Down Expand Up @@ -244,7 +244,7 @@ impl MigrationGenerator {
Ok(())
}

fn args_from_attr(path: &Path, attr: &Attribute) -> Result<ModelArgs, ParsingError> {
fn args_from_attr(path: &Path, attr: &syn::Attribute) -> Result<ModelArgs, ParsingError> {
match attr.meta {
Meta::Path(_) => {
// Means `#[model]` without any arguments
Expand Down Expand Up @@ -496,7 +496,7 @@ impl MigrationGenerator {
}

#[must_use]
fn model_to_migration_model(model: &ModelInSource) -> proc_macro2::TokenStream {
fn model_to_migration_model(model: &ModelInSource) -> TokenStream {
let mut model_source = model.model_item.clone();
model_source.vis = syn::Visibility::Inherited;
model_source.ident = format_ident!("_{}", model_source.ident);
Expand Down Expand Up @@ -885,7 +885,7 @@ pub struct MigrationAsSource {

impl MigrationAsSource {
#[must_use]
pub fn new(name: String, content: String) -> Self {
pub(crate) fn new(name: String, content: String) -> Self {
Self { name, content }
}
}
Expand All @@ -902,11 +902,11 @@ fn is_model_attr(attr: &syn::Attribute) -> bool {
}

trait Repr {
fn repr(&self) -> proc_macro2::TokenStream;
fn repr(&self) -> TokenStream;
}

impl Repr for Field {
fn repr(&self) -> proc_macro2::TokenStream {
fn repr(&self) -> TokenStream {
let column_name = &self.column_name;
let ty = &self.ty;
let mut tokens = quote! {
Expand Down
4 changes: 2 additions & 2 deletions cot-cli/src/new_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl CotSource<'_> {
CotSource::Path(path) => {
format!(
"path = \"{}\"",
path.display().to_string().replace("\\", "\\\\")
path.display().to_string().replace('\\', "\\\\")
)
}
CotSource::PublishedCrate => format!("version = \"{}\"", env!("CARGO_PKG_VERSION")),
Expand All @@ -48,7 +48,7 @@ impl CotSource<'_> {
pub fn new_project(
path: &Path,
project_name: &str,
cot_source: CotSource<'_>,
cot_source: &CotSource<'_>,
) -> anyhow::Result<()> {
print_status_msg("Creating", &format!("Cot project `{project_name}`"));

Expand Down
2 changes: 1 addition & 1 deletion cot-cli/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub(crate) fn print_status_msg(status: &str, message: &str) {
eprintln!("{status_style}{status:>12}{status_style:#} {message}");
}

pub fn find_cargo_toml(starting_dir: &Path) -> Option<PathBuf> {
pub(crate) fn find_cargo_toml(starting_dir: &Path) -> Option<PathBuf> {
let mut current_dir = starting_dir;

loop {
Expand Down
2 changes: 1 addition & 1 deletion cot-cli/tests/new_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn new_project_compile_test() {
new_project(
&project_path,
"my_project",
CotSource::Path(&cot_workspace_path),
&CotSource::Path(&cot_workspace_path),
)
.unwrap();

Expand Down
19 changes: 11 additions & 8 deletions cot-codegen/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ enum ItemToken {
}

impl Parse for ItemToken {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lookahead = input.lookahead1();
let op = OpParser::from_lookahead(&lookahead, input)?;
if let Some(op) = op {
Expand Down Expand Up @@ -69,7 +69,7 @@ impl FieldParser {
}

impl Parse for FieldParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(FieldParser {
field_token: input.parse()?,
name: input.parse()?,
Expand All @@ -91,7 +91,7 @@ impl ReferenceParser {
}

impl Parse for ReferenceParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(ReferenceParser {
reference_token: input.parse()?,
expr: input.parse()?,
Expand All @@ -113,7 +113,7 @@ impl MemberAccessParser {
}

impl Parse for MemberAccessParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
dot: input.parse()?,
member_name: input.parse()?,
Expand All @@ -134,7 +134,7 @@ impl FunctionCallParser {
}

impl Parse for FunctionCallParser {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let args_content;
syn::parenthesized!(args_content in input);
Ok(Self {
Expand All @@ -156,7 +156,10 @@ enum OpParser {
}

impl OpParser {
fn from_lookahead(lookahead: &Lookahead1, input: ParseStream) -> syn::Result<Option<Self>> {
fn from_lookahead(
lookahead: &Lookahead1<'_>,
input: ParseStream<'_>,
) -> syn::Result<Option<Self>> {
let result = if lookahead.peek(Token![*]) {
OpParser::Mul(input.parse()?)
} else if lookahead.peek(Token![/]) {
Expand Down Expand Up @@ -290,7 +293,7 @@ impl Expr {
syn::parse2::<Expr>(input)
}

fn parse_impl(input: ParseStream, min_binding_priority: u8) -> syn::Result<Self> {
fn parse_impl(input: ParseStream<'_>, min_binding_priority: u8) -> syn::Result<Self> {
// Implementation of Pratt parsing algorithm

let mut lhs = if input.peek(syn::token::Paren) {
Expand Down Expand Up @@ -476,7 +479,7 @@ enum ExprAsTokensMode {
}

impl Parse for Expr {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Self::parse_impl(input, 0)
}
}
Expand Down
27 changes: 10 additions & 17 deletions cot-codegen/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,13 @@ impl FieldOpts {
arg: &syn::AngleBracketedGenericArguments,
type_to_find: &str,
) -> Option<syn::Type> {
arg.args
.iter()
.filter_map(|arg| {
if let syn::GenericArgument::Type(ty) = arg {
Self::find_type_resolved(ty, type_to_find)
} else {
None
}
})
.next()
arg.args.iter().find_map(|arg| {
if let syn::GenericArgument::Type(ty) = arg {
Self::find_type_resolved(ty, type_to_find)
} else {
None
}
})
}

/// Convert the field options into a field.
Expand Down Expand Up @@ -283,21 +280,17 @@ impl TryFrom<syn::Type> for ForeignKeySpec {
type Error = syn::Error;

fn try_from(ty: syn::Type) -> Result<Self, Self::Error> {
let type_path = if let syn::Type::Path(type_path) = &ty {
type_path
} else {
let syn::Type::Path(type_path) = &ty else {
panic!("Expected a path type for a foreign key");
};

let args = if let syn::PathArguments::AngleBracketed(args) = &type_path
let syn::PathArguments::AngleBracketed(args) = &type_path
.path
.segments
.last()
.expect("type path must have at least one segment")
.arguments
{
args
} else {
else {
return Err(syn::Error::new(
ty.span(),
"expected ForeignKey to have angle-bracketed generic arguments",
Expand Down
30 changes: 30 additions & 0 deletions cot-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,36 @@ pub fn dbtest(_args: TokenStream, input: TokenStream) -> TokenStream {
.into()
}

/// An attribute macro that defines an entry point to a Cot-powered app.
///
/// This macro is meant to wrap a function returning
/// [`cot::Result<cot::CotProject>`]. It should initialize a CotProject and
/// return it, while the macro takes care of initializing an async runtime,
/// creating a CLI and running the app.
///
/// # Examples
///
/// ```no_run
/// use cot::{CotApp, CotProject};
///
/// struct HelloApp;
///
/// impl CotApp for HelloApp {
/// fn name(&self) -> &'static str {
/// env!("CARGO_PKG_NAME")
/// }
/// }
///
/// #[cot::main]
/// async fn main() -> cot::Result<CotProject> {
/// let cot_project = CotProject::builder()
/// .register_app_with_views(HelloApp, "")
/// .build()
/// .await?;
///
/// Ok(cot_project)
/// }
/// ```
#[proc_macro_attribute]
pub fn main(_args: TokenStream, input: TokenStream) -> TokenStream {
let fn_input = parse_macro_input!(input as ItemFn);
Expand Down
4 changes: 2 additions & 2 deletions cot-macros/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ use syn::Token;
use crate::cot_ident;

#[derive(Debug)]
pub struct Query {
pub(crate) struct Query {
model_name: syn::Type,
_comma: Token![,],
expr: Expr,
}

impl Parse for Query {
fn parse(input: ParseStream) -> syn::Result<Self> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
model_name: input.parse()?,
_comma: input.parse()?,
Expand Down
46 changes: 43 additions & 3 deletions cot/src/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,17 +411,19 @@ impl DatabaseField for PasswordHash {
#[cfg(feature = "db")]
impl FromDbValue for PasswordHash {
#[cfg(feature = "sqlite")]
fn from_sqlite(value: crate::db::impl_sqlite::SqliteValueRef) -> cot::db::Result<Self> {
fn from_sqlite(value: crate::db::impl_sqlite::SqliteValueRef<'_>) -> cot::db::Result<Self> {
PasswordHash::new(value.get::<String>()?).map_err(cot::db::DatabaseError::value_decode)
}

#[cfg(feature = "postgres")]
fn from_postgres(value: crate::db::impl_postgres::PostgresValueRef) -> cot::db::Result<Self> {
fn from_postgres(
value: crate::db::impl_postgres::PostgresValueRef<'_>,
) -> cot::db::Result<Self> {
PasswordHash::new(value.get::<String>()?).map_err(cot::db::DatabaseError::value_decode)
}

#[cfg(feature = "mysql")]
fn from_mysql(value: crate::db::impl_mysql::MySqlValueRef) -> crate::db::Result<Self>
fn from_mysql(value: crate::db::impl_mysql::MySqlValueRef<'_>) -> crate::db::Result<Self>
where
Self: Sized,
{
Expand Down Expand Up @@ -708,6 +710,7 @@ async fn session_auth_hash_valid(
Ok(false)
}

/// An authentication backend.
#[async_trait]
pub trait AuthBackend: Send + Sync {
async fn authenticate(
Expand All @@ -716,13 +719,50 @@ pub trait AuthBackend: Send + Sync {
credentials: &(dyn Any + Send + Sync),
) -> Result<Option<Box<dyn User + Send + Sync>>>;

/// Get a user by ID.
///
/// This method returns a user object by its ID. If the user is not found,
/// it should return `None`.
///
/// # Errors
///
/// Returns an error if the user object cannot be fetched.
///
/// # Examples
///
/// ```
/// use cot::auth::UserId;
/// use cot::request::{Request, RequestExt};
///
/// async fn view_user_profile(request: &Request) {
/// let user = request
/// .project_config()
/// .auth_backend()
/// .get_by_id(request, UserId::Int(1))
/// .await;
///
/// match user {
/// Ok(Some(user)) => {
/// println!("User ID: {:?}", user.id());
/// println!("Username: {:?}", user.username());
/// }
/// Ok(None) => {
/// println!("User not found");
/// }
/// Err(error) => {
/// eprintln!("Error: {}", error);
/// }
/// }
/// }
/// ```
async fn get_by_id(
&self,
request: &Request,
id: UserId,
) -> Result<Option<Box<dyn User + Send + Sync>>>;
}

/// A no-op authentication backend.
#[derive(Debug, Copy, Clone)]
pub struct NoAuthBackend;

Expand Down
Loading

0 comments on commit 4c3bed4

Please sign in to comment.