Skip to content

Commit

Permalink
feat: add an extensible CLI to Cot-powered projects
Browse files Browse the repository at this point in the history
  • Loading branch information
m4tx committed Jan 20, 2025
1 parent 4c471ee commit 2d65c89
Show file tree
Hide file tree
Showing 24 changed files with 665 additions and 49 deletions.
27 changes: 25 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"examples/sessions",
"examples/admin",
"examples/json",
"examples/custom-task",
]
resolver = "2"

Expand All @@ -32,7 +33,7 @@ backtrace = "0.3"
bytes = "1.9"
cargo_toml = "0.20"
chrono = { version = "0.4", default-features = false }
clap = "4"
clap = { version = "4", features = ["deprecated"] }
clap-verbosity-flag = { version = "3", default-features = false }
convert_case = "0.6"
cot = { path = "cot" }
Expand Down
2 changes: 1 addition & 1 deletion cot-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ anstyle.workspace = true
anyhow.workspace = true
cargo_toml.workspace = true
chrono.workspace = true
clap = { workspace = true, features = ["derive", "env"] }
clap = { workspace = true, features = ["derive", "env", "wrap_help"] }
clap-verbosity-flag = { workspace = true, features = ["tracing"] }
convert_case.workspace = true
darling.workspace = true
Expand Down
78 changes: 69 additions & 9 deletions cot-cli/src/new_project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,44 @@ const PROJECT_FILES: [(&str, &str); 6] = [
project_file!("templates/index.html"),
];

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CotSource {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum CotSource<'a> {
Git,
#[allow(dead_code)] // used in integration tests
Path(&'a Path),
PublishedCrate,
}

pub fn new_project(path: &Path, project_name: &str, cot_source: CotSource) -> anyhow::Result<()> {
impl<'a> CotSource<'a> {
fn as_cargo_toml_source(&self) -> String {
match self {
CotSource::Git => {
"package = \"cot\", git = \"https://github.com/cot-rs/cot.git\"".to_owned()
}
CotSource::Path(path) => {
format!(
"path = \"{}\"",
path.display().to_string().replace("\\", "\\\\")
)
}
CotSource::PublishedCrate => format!("version = \"{}\"", env!("CARGO_PKG_VERSION")),
}
}
}

pub fn new_project(
path: &Path,
project_name: &str,
cot_source: CotSource<'_>,
) -> anyhow::Result<()> {
print_status_msg("Creating", &format!("Cot project `{project_name}`"));

if path.exists() {
anyhow::bail!("destination `{}` already exists", path.display());
}

let app_name = format!("{}App", project_name.to_case(Case::Pascal));
let cot_source = match cot_source {
CotSource::Git => {
"package = \"cot\", git = \"https://github.com/cot-rs/cot.git\"".to_owned()
}
CotSource::PublishedCrate => format!("version = \"{}\"", env!("CARGO_PKG_VERSION")),
};
let cot_source = cot_source.as_cargo_toml_source();

for (file_name, content) in PROJECT_FILES {
let file_path = path.join(file_name);
Expand All @@ -62,3 +80,45 @@ pub fn new_project(path: &Path, project_name: &str, cot_source: CotSource) -> an

Ok(())
}

#[cfg(test)]
mod tests {
use std::path::Path;

use super::*;

#[test]
fn as_cargo_toml_source_git() {
let source = CotSource::Git;
assert_eq!(
source.as_cargo_toml_source(),
"package = \"cot\", git = \"https://github.com/cot-rs/cot.git\""
);
}

#[test]
fn as_cargo_toml_source_path() {
let path = Path::new("/some/local/path");
let source = CotSource::Path(path);
assert_eq!(source.as_cargo_toml_source(), "path = \"/some/local/path\"");
}

#[test]
fn as_cargo_toml_source_path_windows() {
let path = Path::new("C:\\some\\local\\path");
let source = CotSource::Path(path);
assert_eq!(
source.as_cargo_toml_source(),
"path = \"C:\\\\some\\\\local\\\\path\""
);
}

#[test]
fn as_cargo_toml_source_published_crate() {
let source = CotSource::PublishedCrate;
assert_eq!(
source.as_cargo_toml_source(),
format!("version = \"{}\"", env!("CARGO_PKG_VERSION"))
);
}
}
1 change: 1 addition & 0 deletions cot-cli/src/project_template/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ impl CotApp for {{ app_name }} {
async fn main() -> cot::Result<CotProject> {
let project = CotProject::builder()
.config(ProjectConfig::builder().build())
.with_cli(cot::cli::metadata!())
.register_app_with_views({{ app_name }}, "")
.middleware_with_context(StaticFilesMiddleware::from_app_context)
.middleware(LiveReloadMiddleware::new())
Expand Down
11 changes: 9 additions & 2 deletions cot-cli/tests/new_project.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::env;
use std::path::Path;
use std::path::{Path, PathBuf};
use std::process::Command;

use cot_cli::new_project::{new_project, CotSource};
Expand All @@ -10,7 +10,14 @@ fn new_project_compile_test() {
let temp_dir = tempfile::tempdir().unwrap();
let project_path = temp_dir.path().join("my_project");

new_project(&project_path, "my_project", CotSource::Git).unwrap();
let cot_cli_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let cot_workspace_path = cot_cli_path.parent().unwrap().join("cot");
new_project(
&project_path,
"my_project",
CotSource::Path(&cot_workspace_path),
)
.unwrap();

let output = cargo(&project_path)
.arg("build")
Expand Down
8 changes: 6 additions & 2 deletions cot-macros/src/main_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ pub(super) fn fn_to_cot_main(main_function_decl: ItemFn) -> syn::Result<TokenStr
let result = quote! {
fn main() {
let body = async {
let project: #crate_name::CotProject = __cot_main().await.unwrap();
#crate_name::run_cli(project).await.unwrap();
let project: #crate_name::CotProject = __cot_main().await.expect(
"failed to build the Cot project"
);
#crate_name::run_cli(project).await.expect(
"failed to run the Cot project"
);

#new_main_decl
};
Expand Down
2 changes: 2 additions & 0 deletions cot/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ axum = { workspace = true, features = ["http1", "tokio"] }
backtrace.workspace = true
bytes.workspace = true
chrono.workspace = true
clap.workspace = true
derive_builder.workspace = true
derive_more = { workspace = true, features = ["debug", "deref", "display", "from"] }
fake = { workspace = true, optional = true, features = ["derive", "chrono"] }
Expand Down Expand Up @@ -52,6 +53,7 @@ async-stream.workspace = true
fake.workspace = true
futures.workspace = true
mockall.workspace = true
tempfile.workspace = true

[package.metadata.docs.rs]
all-features = true
Expand Down
6 changes: 3 additions & 3 deletions cot/src/auth/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::auth::{
SessionAuthHash, User, UserId,
};
use crate::config::SecretKey;
use crate::db::migrations::DynMigration;
use crate::db::migrations::SyncDynMigration;
use crate::db::{model, query, Auto, DatabaseBackend, LimitedString, Model};
use crate::request::{Request, RequestExt};
use crate::CotApp;
Expand Down Expand Up @@ -362,13 +362,13 @@ impl CotApp for DatabaseUserApp {
vec![Box::new(DefaultAdminModelManager::<DatabaseUser>::new())]
}

fn migrations(&self) -> Vec<Box<dyn DynMigration>> {
fn migrations(&self) -> Vec<Box<SyncDynMigration>> {
// TODO: this is way too complicated for the user-facing API
#[allow(trivial_casts)]
migrations::MIGRATIONS
.iter()
.copied()
.map(|x| Box::new(x) as Box<dyn DynMigration>)
.map(|x| Box::new(x) as Box<SyncDynMigration>)
.collect()
}
}
Expand Down
2 changes: 1 addition & 1 deletion cot/src/auth/db/migrations.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod m_0001_initial;

pub const MIGRATIONS: &[&dyn ::cot::db::migrations::DynMigration] = &[&m_0001_initial::Migration];
pub const MIGRATIONS: &[&::cot::db::migrations::SyncDynMigration] = &[&m_0001_initial::Migration];
Loading

0 comments on commit 2d65c89

Please sign in to comment.