Skip to content

Commit

Permalink
Merge pull request #46 from BrowserSync/export_cmd
Browse files Browse the repository at this point in the history
Export cmd
  • Loading branch information
shakyShane authored Dec 18, 2024
2 parents debcc1f + be049ca commit 7dc4925
Show file tree
Hide file tree
Showing 39 changed files with 1,064 additions and 618 deletions.
19 changes: 18 additions & 1 deletion Cargo.lock

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

3 changes: 3 additions & 0 deletions crates/bsnext_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ bsnext_utils = { path = "../bsnext_utils" }

[dependencies]
bsnext_input = { path = "../bsnext_input" }
bsnext_fs_helpers = { path = "../bsnext_fs_helpers" }
bsnext_fs = { path = "../bsnext_fs" }
bsnext_resp = { path = "../bsnext_resp" }
bsnext_client = { path = "../bsnext_client" }
Expand All @@ -23,6 +24,7 @@ hyper-util = { version = "0.1.1", features = ["client-legacy"] }

mime_guess = { workspace = true }
insta = { workspace = true }
clap = { workspace = true }
axum = { workspace = true }
http-body-util = { workspace = true }
tokio = { workspace = true }
Expand All @@ -33,6 +35,7 @@ tracing = { workspace = true }
actix = { workspace = true }
actix-rt = { workspace = true }
anyhow = { workspace = true }
thiserror = { workspace = true }
futures = { workspace = true }
futures-util = { workspace = true }
serde = { workspace = true }
Expand Down
184 changes: 184 additions & 0 deletions crates/bsnext_core/src/export.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
use crate::runtime_ctx::RuntimeCtx;
use crate::server::router::common::{into_state, uri_to_res_parts};
use bsnext_fs_helpers::{FsWriteError, WriteMode};
use bsnext_input::route::{Route, RouteKind};
use bsnext_input::server_config::ServerConfig;
use futures_util::future::join_all;
use http::response::Parts;
use std::clone::Clone;
use std::fs;
use std::path::{Path, PathBuf};
use std::time::Duration;

#[derive(Debug)]
pub enum ExportEvent {
DryRunDirCreate(PathBuf),
DryRunFileCreate(PathBuf),
DidCreateFile(PathBuf),
DidCreateDir(PathBuf),
Failed { error: ExportError },
}

#[derive(Debug, thiserror::Error)]
pub enum ExportError {
#[error("{0}")]
Fs(FsWriteError),
}

#[derive(Debug, clap::Parser)]
pub struct ExportCommand {
/// The folder to export the files to. For current, provide '.'
#[arg(long = "dir")]
pub out_dir: PathBuf,
/// When provided, just prints what might happen instead of actually causing side effects
#[arg(long)]
pub dry_run: bool,
/// when providing, will overwrite existing files
#[arg(long)]
pub force: bool,
}

#[derive(Debug)]
enum ExportType {
Write {
export_result: ExportResult,
filepath: PathBuf,
},
Excluded {
reason: ExcludeReason,
},
}

#[derive(Debug)]
enum ExcludeReason {
BadRequest,
}

#[derive(Debug)]
struct ExportRequest<'a> {
pub url_path: &'a str,
pub filepath: PathBuf,
}

type ExportResult = (Parts, String, Duration);

pub async fn export_one_server(
cwd: &PathBuf,
server: ServerConfig,
cmd: &ExportCommand,
write_mode: WriteMode,
) -> Result<Vec<ExportEvent>, ExportError> {
let routes = server.combined_routes();
let state = into_state(server);

let raw_entries = routes.iter().filter_map(only_raw_entries);
let raw_entry_paths = raw_entries.clone().map(|r| r.filepath);
let async_requests = raw_entries.map(|req| uri_to_res_parts(state.clone(), req.url_path));

let ctx = RuntimeCtx::new(cwd);
// let job_count = raw_entry_paths.len();

let results = join_all(async_requests)
.await
.into_iter()
.zip(raw_entry_paths)
.map(to_export_type)
.try_fold(vec![], move |mut acc, ref ex_type| match cmd.dry_run {
true => match print_sink(ex_type, &cmd.out_dir, &ctx) {
Ok(evts) => {
acc.extend(evts);
Ok(acc)
}
Err(e) => Err(e),
},
false => match fs_sink(ex_type, &cmd.out_dir, &ctx, &write_mode) {
Ok(evts) => {
acc.extend(evts);
Ok(acc)
}
Err(e) => Err(e),
},
});

match results {
Ok(events) => Ok(events),
Err(e) => Ok(vec![ExportEvent::Failed { error: e }]),
}
}

fn fs_sink(
ex_type: &ExportType,
out_dir: &PathBuf,
ctx: &RuntimeCtx,
write_mode: &WriteMode,
) -> Result<Vec<ExportEvent>, ExportError> {
match ex_type {
ExportType::Write {
export_result,
filepath,
} => {
let filepath = ctx.cwd().join(out_dir).join(filepath);
write_one(export_result, &filepath, ctx, write_mode).map_err(ExportError::Fs)
}
ExportType::Excluded { reason: _ } => Ok(vec![]),
}
}

fn print_sink(
ex_type: &ExportType,
out_dir: &PathBuf,
ctx: &RuntimeCtx,
) -> Result<Vec<ExportEvent>, ExportError> {
let mut events = vec![];
match ex_type {
ExportType::Write { filepath, .. } => {
let path = ctx.cwd().join(out_dir).join(filepath);
events.push(ExportEvent::DryRunFileCreate(path));
}
ExportType::Excluded { reason } => {
todo!("Ignoring {:?}", reason)
}
}
Ok(events)
}

fn to_export_type((export_result, filepath): (ExportResult, PathBuf)) -> ExportType {
let (parts, _, _) = &export_result;
if parts.status.as_u16() == 200 {
ExportType::Write {
export_result,
filepath,
}
} else {
ExportType::Excluded {
reason: ExcludeReason::BadRequest,
}
}
}

fn only_raw_entries(route: &Route) -> Option<ExportRequest> {
match &route.kind {
RouteKind::Raw(..) => Some(ExportRequest {
filepath: route.as_filepath(),
url_path: route.url_path(),
}),
_ => None,
}
}

fn write_one(
export_result: &ExportResult,
filepath: &Path,
ctx: &RuntimeCtx,
write_mode: &WriteMode,
) -> Result<Vec<ExportEvent>, FsWriteError> {
let dir = filepath.parent();
let mut events = vec![];
if let Some(dir) = dir {
fs::create_dir_all(dir).map_err(FsWriteError::FailedDir)?;
}
let (_, ref body, _) = export_result;
let pb = bsnext_fs_helpers::fs_write_str(ctx.cwd(), filepath, body, write_mode)?;
events.push(ExportEvent::DidCreateFile(pb.clone()));
Ok(events)
}
3 changes: 2 additions & 1 deletion crates/bsnext_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ pub mod server;
pub mod servers_supervisor;

pub mod dir_loader;
mod handler_stack;
pub mod export;
pub mod handler_stack;
pub mod handlers;
pub mod meta;
pub mod not_found;
Expand Down
1 change: 0 additions & 1 deletion crates/bsnext_dto/src/server_stuff.rs

This file was deleted.

19 changes: 9 additions & 10 deletions crates/bsnext_fs/src/watcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,16 +128,15 @@ fn is_auto_excluded<P: AsRef<Path>>(cwd: &P, subject: &P) -> bool {
.map(OsStr::new)
.collect();
let rel = subject.as_ref().strip_prefix(cwd.as_ref());
return rel
.map(|p| match p.components().next() {
None => false,
Some(Component::Normal(str)) => excluded.contains(str),
Some(Component::Prefix(_)) => unreachable!("here? Prefix"),
Some(Component::RootDir) => unreachable!("here? RootDir"),
Some(Component::CurDir) => unreachable!("here? CurDir"),
Some(Component::ParentDir) => unreachable!("here? ParentDir"),
})
.unwrap_or(false);
rel.map(|p| match p.components().next() {
None => false,
Some(Component::Normal(str)) => excluded.contains(str),
Some(Component::Prefix(_)) => unreachable!("here? Prefix"),
Some(Component::RootDir) => unreachable!("here? RootDir"),
Some(Component::CurDir) => unreachable!("here? CurDir"),
Some(Component::ParentDir) => unreachable!("here? ParentDir"),
})
.unwrap_or(false)
}

#[test]
Expand Down
8 changes: 8 additions & 0 deletions crates/bsnext_fs_helpers/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "bsnext_fs_helpers"
version = "0.1.0"
edition = "2021"

[dependencies]
tracing = { workspace = true }
thiserror = { workspace = true }
Loading

0 comments on commit 7dc4925

Please sign in to comment.