Skip to content

Commit

Permalink
wip: add enum dispatch
Browse files Browse the repository at this point in the history
  • Loading branch information
cilki committed May 5, 2024
1 parent 219e2da commit cdbfa38
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 57 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ repository = "https://github.com/cilki/wsx/"
[dependencies]
anyhow = "1.0.82"
cmd_lib = "1.3.0"
enum_dispatch = "0.3.13"
git-repository = { version = "0", optional = true }
home = "0.5.9"
log = "0.4.17"
Expand Down
10 changes: 0 additions & 10 deletions src/api/mod.rs

This file was deleted.

11 changes: 8 additions & 3 deletions src/cmd/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,30 @@ pub mod drop;
pub mod open;

/// Represents a pattern that matches one or more repositories. It has the
/// format: [workspace]:[provider]/[path].
/// format: [workspace]:[remote]/[path].
#[derive(Debug, Eq, PartialEq)]
pub struct RepoPattern {
/// The workspace name
pub workspace: Option<String>,

/// The remote name
pub remote: Option<String>,

/// The repo path
pub path: String,
}

impl RepoPattern {
pub fn parse(path: &str) -> Result<Self> {
match Regex::new(r"^([^/]+:)?(.*)$")?.captures(path) {
Some(captures) => Ok(Self {
match Regex::new(r"^([^/]+:)?([^/]+)?(.*)$")?.captures(path) {
Some(captures) => {captures.len();
Ok(Self {
workspace: captures
.get(1)
.map(|m| m.as_str().to_string())
.map(|s| s[..s.len() - 1].to_string()),
path: captures.get(2).unwrap().as_str().to_string(),
remote: todo!(), },
}),
None => bail!("Invalid repository path pattern"),
}
Expand Down
68 changes: 50 additions & 18 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
use crate::cmd::RepoPattern;
use anyhow::bail;
use anyhow::Result;
use cmd_lib::run_fun;
use remote::Remote;
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha512};
use std::path::Path;
use std::path::PathBuf;
use tracing::debug;

pub mod api;
pub mod cmd;
pub mod remote;

/// Represents the user's config file
#[derive(Debug, Serialize, Deserialize)]
Expand All @@ -17,23 +19,58 @@ pub struct Config {

/// The cache directory for all workspaces
#[serde(flatten)]
pub cache: Option<Cache>,
pub cache: Option<RepoCache>,
}

impl Default for Config {
// Place the cache according to platform

fn default() -> Self {
let home = home::home_dir().expect("the home directory exists");

Self {
workspace: vec![],
cache: None,
workspace: vec![Workspace {
name: Some("default".into()),
path: home.join("workspace").display().to_string(),
remotes: vec![],
}],
cache: Some(RepoCache {
path: home.join(".cache/wsx").display().to_string(),
}),
}
}
}

impl Config {
/// Load the application config from the filesystem or provide a default if
/// none exists.
pub fn load() -> Result<Self> {
let config_path = match home::home_dir() {
Some(home) => format!("{}/.config/wsx.toml", home.display()),
None => bail!("Home directory not found"),
};
debug!(config_path = %config_path, "Searching for configuration file");

let config: Config = match std::fs::metadata(&config_path) {
Ok(_) => toml::from_str(&std::fs::read_to_string(config_path)?)?,
Err(_) => Config::default(),
};
debug!(config = ?config, "Loaded configuration");

// Make sure all necessary directories exist
if let Some(cache) = config.cache.as_ref() {
std::fs::create_dir_all(&cache.path)?;
}
for workspace in config.workspace.iter() {
std::fs::create_dir_all(&workspace.path)?;
}

Ok(config)
}

/// Resolve a repository pattern against local repositories.
pub fn resolve_local(&self, pattern: &RepoPattern) -> Vec<PathBuf> {
// Either find the specified workspace or choose the first available
let workspace: &Workspace = match &pattern.workspace {
Some(workspace_name) => self
.workspace
Expand All @@ -46,26 +83,26 @@ impl Config {
None => self.workspace.first().unwrap(),
};

let (provider, path) = match pattern.maybe_provider() {
let (remote, path) = match pattern.maybe_provider() {
Some((provider, path)) => {
debug!("{} {}", provider, path);
(
workspace
.provider
.remotes
.iter()
.find(|&p| p.name == provider)
.unwrap(),
path,
)
}
None => (workspace.provider.first().unwrap(), pattern.path.clone()),
None => (workspace.remotes.first().unwrap(), pattern.path.clone()),
};

find_git_dir(&format!("{}/{}/{}", workspace.path, provider.name, path)).unwrap()
find_git_dir(&format!("{}/{}/{}", workspace.path, remote.name, path)).unwrap()
}
}

/// Recursively find git repositories.
/// Recursively find "top-level" git repositories.
fn find_git_dir(path: &str) -> Result<Vec<PathBuf>> {
debug!("Searching for git repositories in: {}", path);
let mut found: Vec<PathBuf> = Vec::new();
Expand Down Expand Up @@ -100,7 +137,7 @@ pub struct Workspace {
pub path: String,

/// A list of providers for the workspace
pub provider: Vec<Provider>,
pub remotes: Vec<Remote>,
}

impl Workspace {
Expand All @@ -118,20 +155,15 @@ impl Workspace {
}
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Provider {
/// The provider's name for use in repo paths
pub name: String,
}

/// Caches repositories that are dropped from a `Workspace` in a separate directory.
/// Entries in this cache are bare repositories for space efficiency.
#[derive(Debug, Serialize, Deserialize)]
pub struct Cache {
pub struct RepoCache {
pub path: String,
// TODO cache parameters?
}

impl Cache {
impl RepoCache {
/// Move the given repository into the cache.
pub fn cache(&self, repo_path: String) -> Result<()> {
// Make sure the cache directory exists first
Expand Down
22 changes: 3 additions & 19 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,7 @@ fn main() -> Result<()> {
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.init();

// Locate configuration file
let config_path = match home::home_dir() {
Some(home) => format!("{}/.config/wsx.toml", home.display()),
None => todo!(),
};

// Load configuration file
let config: Config = match std::fs::metadata(&config_path) {
Ok(_) => {
debug!(config_path = %config_path, "Loading configuration file");
toml::from_str(&std::fs::read_to_string(config_path)?)?
}
Err(_) => {
debug!("Using default config");
Config::default()
}
};

debug!(config = ?config, "Using configuration");
let config = Config::load()?;

match std::env::var("_ARGCOMPLETE_") {
Ok(shell_type) => {
Expand All @@ -51,6 +33,7 @@ fn main() -> Result<()> {

match args.subcommand()? {
Some(command) => match command.as_str() {
"clone" => wsx::cmd::open::run_open(&config, args.opt_free_from_str()?),
"drop" => wsx::cmd::drop::run_drop(&config, args.opt_free_from_str()?),
"help" => print_help(),
_ => wsx::cmd::open::run_open(&config, Some(command)),
Expand All @@ -68,6 +51,7 @@ fn print_help() -> Result<()> {
);
println!("");
println!("wsx <repo pattern> - Clone one or more repositories");
println!("wsx clone <repo pattern> - Clone one or more repositories");
println!("wsx drop [repo pattern] - Drop one or more repositories");
Ok(())
}
Expand Down
13 changes: 6 additions & 7 deletions src/api/github.rs → src/remote/github.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
use crate::api::Provider;
use crate::Provider;
use super::ListRepos;
use anyhow::Result;
use serde::{Deserialize, Serialize};
use std::fmt;

pub struct GithubProvider {
pub config: Provider,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GithubRemote {}

impl fmt::Display for GithubProvider {
impl fmt::Display for GithubRemote {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Github",)
}
}

impl Provider for GithubProvider {
impl ListRepos for GithubRemote {
fn list_repo_paths(&self) -> Result<Vec<String>> {
let mut paths: Vec<String> = Vec::new();

Expand Down
File renamed without changes.
21 changes: 21 additions & 0 deletions src/remote/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use self::github::GithubRemote;
use anyhow::Result;
use enum_dispatch::enum_dispatch;
use serde::{Deserialize, Serialize};
use std::fmt::Display;

pub mod github;
pub mod gitlab;

#[enum_dispatch]
pub trait ListRepos {
/// List all repository paths available to the provider.
fn list_repo_paths(&self) -> Result<Vec<String>>;
}

#[derive(Debug, Serialize, Deserialize)]
#[enum_dispatch(ListRepos)]
pub enum Remote {
Github(GithubRemote),
// Gitlab(GitlabRemote),
}

0 comments on commit cdbfa38

Please sign in to comment.