From ca1b36218f88842d89e29fff8a96b27109eb7362 Mon Sep 17 00:00:00 2001 From: Youwen Wu Date: Wed, 26 Jun 2024 01:46:51 -0700 Subject: [PATCH] initial commit --- .gitignore | 2 + .prettierrc.toml | 2 + Cargo.lock | 338 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 10 ++ README.md | 148 +++++++++++++++++++++ src/cli.rs | 30 +++++ src/dartfile.rs | 196 +++++++++++++++++++++++++++ src/dartgun.rs | 18 +++ src/main.rs | 18 +++ 9 files changed, 762 insertions(+) create mode 100644 .gitignore create mode 100644 .prettierrc.toml create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/cli.rs create mode 100644 src/dartfile.rs create mode 100644 src/dartgun.rs create mode 100644 src/main.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b376f72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +dartgun-test/ diff --git a/.prettierrc.toml b/.prettierrc.toml new file mode 100644 index 0000000..ea5feee --- /dev/null +++ b/.prettierrc.toml @@ -0,0 +1,2 @@ +lineWidth = 80 +proseWrap = "always" diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..1c2128a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,338 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5db83dced34638ad474f39f250d7fea9598bdd239eaced1bdf45d597da0f433f" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7e204572485eb3fbf28f871612191521df159bc3e15a9f5064c66dba3a8c05f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c780290ccf4fb26629baa7a1081e68ced113f1d3ec302fa5948f1c381ebf06c6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "dartgun-rs" +version = "0.1.0" +dependencies = [ + "clap", + "toml", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" +dependencies = [ + "serde", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f21c7aaf97f1bd9ca9d4f9e73b0a6c74bd5afef56f2bc931943a6e1c37e04e38" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "winnow" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..553306f --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "dartgun-rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +clap = { version = "4.5.7", features = ["derive"] } +toml = "0.8.14" diff --git a/README.md b/README.md new file mode 100644 index 0000000..02e131c --- /dev/null +++ b/README.md @@ -0,0 +1,148 @@ +# dartgun + +the stupid dotfile manager. + +## Impetus + +Managing dot files are annoying. They're some of the most essential parts of a +developer's system, yet most operating systems (distros) don't provide a way to +easily manage them. There's the `~/.config` directory, which in theory holds all +of your configuration files. If this were true, you could simply `git init` +inside `~/.config` and have a versioned dotfile repo that you could backup and +deploy to new machines. However, configuration files often end up all over a +system. There's [NixOS](https://nixos.org/), but not everyone can dedicate 40 +hours a week to configuring their OS. + +Advanced solutions dotfile helpers, but most users don't really need much more +than a set of bash scripts which copies their dotfiles around their system. + +Dartgun essentially does this in a more systematic manner, and seeks to be just +as still simple to set up and manage. Everything lives in one folder which can +be versioned by git. Dartgun will put your dotfiles in the correct places by +symlinking from the central dotfile directory to the specified locations. + +The primary goal is to provide an easy way to manage your dotfiles in a +centralized area and sync them between different systems. A secondary goal is to +help automatically set up a new system with the configuration files in the +correct places. However, automatically installing additional software and +dependencies is outside of the scope of the project. + +## Non-goals + +Dartgun is specifically designed to have a minimum amount of features to make it +as easy to adopt as possible. If you're reading this, I'll assume that you are +already looking for a dotfile manager. Therefore, it might be easier to list +things that Dartgun is _not_ designed to do. If you do not need any of these +features, then Dartgun might the right dotfile manager for you. + +Dartgun is not for: + +- Managing a fleet of computers +- Automatically deploying servers +- Deterministically setting up an entire OS from configuration files (see NixOS + for that) +- Automatically installing packages or software alongside dotfiles; it only + manages the files, the user is still responsible for ensuring software is + available +- Power users who want deep customizability and feature sets to help fully + automate system configuration + +## Goals + +- Easily version dotfiles with git by keeping everything in one central + directory +- Copy dotfiles to a new machine quickly and transparently +- Sync dotfiles between different machines +- Keep configuration effort to a minimum + +## How it works + +Dartgun allows you to centralize all of your dotfiles in a single directory. + +Dartgun is configured through the `dartgun.toml` file. You should set your +machine name in this file like so: + +```toml +machine = "youwens-mac" +``` + +Also, it's common to have programs that are not installed on every computer. +Therefore, each dotfile will specify which application it is for, and whether or +not it should be applied by default. See the dotfile configuration below for how +to configure this. + +You can specify which applications are in which machines with the `apps.toml` +file. + +```toml +[youwens-mac] +available = ["neovim", "hyprland", "zsh"] +``` + +### Why symlinking? + +Any configuration updates made will always sync back to the dartgun directory so +they can be tracked by git. Likewise, any remote updates pulled in will also be +automatically reflected in the system for free. + +### Why no Windows support? + +Windows symlinks work a little differently from Unix symlinks. The difference is +not massive and I plan to look into supporting Windows at a later date. + +## Usage guide + +Begin by creating a folder to place your dotfiles in. I'll refer to it as the +"dartgun folder" for the rest of these instructions. Mine is located at +`~/.dartgun`, but it can be called whatever you want and located wherever you +want it. + +Place your dotfiles in the dartgun folder. You can organize them however you +want. The primary configuration is done in the `dartgun.json` file, located in +root of the dartgun folder. In here, you specify where each file or folder in +the directory goes. + +For example, I can tell the `.dartgun/nvim` folder to go to `~/.config/nvim` +with + +```json +{ + darts = [ + { + location: "./nvim", + destination: "~/.config/nvim", + strategy: "hardlink", + machines: [youwens-mac, youwens-archlinux], + for: "neovim" + } + ] +} +``` + +``` +location: the relative path, from the dartgun folder, where either the directory or file to be copied is located +destination: the destination path to which the file should be copied to. must be an absolute path +strategy: which strategy to use to copy the file. accepts 'hardlink', 'symlink', or 'copy' +machines: which machine names this file will be copied on +for: the application which the dotfile is for +``` + +Note that you can choose how you want to approach the machine configuration. You +can either have a specific machine key for each computer you own, or specify +machines by platform (eg. "arch", "mac", "fedora"). You can get as fine-grained +or generic as you'd like. + +Then, run `dartgun blast` to apply your dotfiles to the locations. For nested +destination directories, it will create all of the directories in the chain if +they do not exist already. You may have to run `sudo dartgun blast` if you are +trying to copy files to restricted locations. + +For symlinked and hardlinked directories, you can simply sync your dotfiles by +updating them in the dartgun folder, for example by running `git pull` after +you've set up git versioning. + +If you use the `copy` strategy, then you need to re-run `dartgun blast` each +time you update the files in your dartgun folder. generally, we do not recommend +the copy method unless you have to for some reason, since it may lead to desyncs +between the dartgun folder and your actual system, while this is impossible with +hardlinking or symlinking. diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..e1fd41b --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,30 @@ +use std::path::Path; + +use crate::dartfile; +use clap::Parser; + +/// dartgun's command line interface + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Cli { + action: Option, +} + +fn fire() { + println!("Attempting to find and read `dartfile.toml`."); + let gun = dartfile::parse(Path::new("./dartgun.toml")); + println!("Writing symlinks..."); + match gun.create_symlinks() { + Ok(_) => println!("Symlinks created successfully!"), + Err(err) => println!("Something went wrong while creating symlinks: {}", err), + } +} + +pub fn run_cli() { + let cli = Cli::parse(); + match cli.action.as_deref() { + Some("fire") => fire(), + _ => println!("No action specified. Run `dartgun -h` for options."), + }; +} diff --git a/src/dartfile.rs b/src/dartfile.rs new file mode 100644 index 0000000..de4ab13 --- /dev/null +++ b/src/dartfile.rs @@ -0,0 +1,196 @@ +use std::fs; +use std::path::{Path, PathBuf}; +use toml::Table; + +/// The dartfile module handles parsing the `dotfile.toml` into +/// a well-typed and self-validating data structure. + +/// The raw form of a dotfile entry. Paths are stored as strings. +/// +/// Should not be used outside of module internals. Its primary purpose +/// is to provide an intermediary between the raw parsed TOML and +/// the strongly typed Dotfile struct +#[derive(Debug)] +struct DotfileRaw { + location: String, + destination: String, + strategy: String, + machines: Vec, + applies_to: String, +} + +impl DotfileRaw { + fn determine_strategy(&self) -> Result { + match self.strategy.as_str() { + "hardlink" => Ok(Strategy::Hardlink), + "symlink" => Ok(Strategy::Symlink), + _ => Err(String::from( + "Strategy must be either 'symlink' or 'hardlink'", + )), + } + } + + fn to_dotfile(&self) -> Result { + let location_path = PathBuf::from(&self.location); + let destination_path = PathBuf::from(&self.destination); + + Ok(Dotfile { + location: location_path, + destination: destination_path, + strategy: self.determine_strategy()?, + machines: self.machines.clone(), + applies_to: self.applies_to.clone(), + }) + } + // TODO: improve error handling for parsing from raw TOML + + /// Create a new `DotfileRaw` from a `toml::Table`, which is a `Vec`. + /// Will panic if the table does not contain the fields specified. + fn from_table(table: Table) -> DotfileRaw { + let location = table + .get("location") + .ok_or("Missing 'location' field.") + .unwrap() + .as_str() + .unwrap() + .to_string(); + let destination = table + .get("destination") + .ok_or("Missing 'destination' field.") + .unwrap() + .as_str() + .unwrap() + .to_string(); + let strategy = table + .get("strategy") + .ok_or("Missing 'strategy' field.") + .unwrap() + .as_str() + .unwrap() + .to_string(); + let machines = table + .get("machines") + .ok_or("Missing 'machines' field.") + .unwrap() + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + let applies_to = table + .get("applies_to") + .ok_or("Missing 'applies_to' field.") + .unwrap() + .as_str() + .unwrap() + .to_string(); + + DotfileRaw { + location, + destination, + strategy, + machines, + applies_to, + } + } +} + +/// The configuration object parsed from the `config` field in `dartgun.toml` +#[derive(Debug)] +pub struct Config { + machine: String, + available: Vec, +} + +impl Config { + // TODO: improve error handling for parsing config + + /// Generate a `Config` from a raw `dartfile.toml`. + /// Will panic! if the format is invalid. + fn from_table(table: Table) -> Config { + let machine = table.get("machine").unwrap().as_str().unwrap().to_string(); + let available = table + .get("available") + .unwrap() + .as_array() + .unwrap() + .iter() + .map(|x| x.as_str().unwrap().to_string()) + .collect(); + Config { available, machine } + } +} + +/// A strongly typed Dartfile (aka `dartgun.toml`). Users of this +/// struct can assume that the dartfile is at least semantically valid +#[derive(Debug)] +pub struct Dartfile { + pub config: Config, + pub dots: Vec, +} + +impl Dartfile { + /// Validates the Dartfile by checking each Dotfile entry to ensure + /// the paths specified by `location` are accessible. + pub fn validate(&self) -> Result<(), String> { + for dotfile in self.dots.iter() { + if dotfile.validate().is_err() { + return Err("Invalid dotfile.".to_string()); + } + } + Ok(()) + } +} + +/// Represents which strategy to use when deploying dotfiles across system. +#[derive(Debug)] +pub enum Strategy { + Hardlink, + Symlink, +} + +/// A strongly-typed Dotfile entry +#[derive(Debug)] +pub struct Dotfile { + pub location: PathBuf, + pub destination: PathBuf, + pub strategy: Strategy, + pub machines: Vec, + pub applies_to: String, +} + +impl Dotfile { + /// Validates the entry by checking whether the `location` paths + /// specified are accessible. + pub fn validate(&self) -> Result<(), String> { + let path_exists = self.location.try_exists(); + match path_exists { + Ok(true) => Ok(()), + Ok(false) => Err("Could not follow broken symlink.".to_string()), + Err(_) => Err("An error occurred. Does the path exist?".to_string()), + } + } +} + +/// Takes a path to a `dartgun.toml` and produces a well-typed Dartfile object. +/// Currently crashes on any parse errors, but this behavior will likely change in the future. +pub fn parse(path: &Path) -> Dartfile { + let raw_data = fs::read_to_string(path).expect("Couldn't read the file."); + let value: Table = raw_data.parse::().expect("Couldn't parse the TOML."); + + let config_raw = value.get("config").unwrap().as_table().unwrap(); + let dots_raw = value.get("dots").unwrap().as_array().unwrap(); + + let config = Config::from_table(config_raw.clone()); + let dots = dots_raw + .iter() + .map(|x| { + match DotfileRaw::from_table(x.as_table().unwrap().clone()).to_dotfile() { + Ok(dotfile) => dotfile, + Err(_) => panic!("An error has occurred parsing the `dartgun.toml` file. Please make sure it is in the correct format.") + } + }) + .collect::>(); + + Dartfile { config, dots } +} diff --git a/src/dartgun.rs b/src/dartgun.rs new file mode 100644 index 0000000..f480716 --- /dev/null +++ b/src/dartgun.rs @@ -0,0 +1,18 @@ +/// Utilities for translating Dartfiles into actual actions on the system. +use crate::dartfile::{Dartfile, Dotfile}; +use std::os::unix::fs::symlink; + +impl Dotfile { + pub fn create_symlink(&self) -> Result<(), std::io::Error> { + symlink(self.location.canonicalize()?, &self.destination) + } +} + +impl Dartfile { + pub fn create_symlinks(&self) -> Result<(), std::io::Error> { + for dot in self.dots.iter() { + dot.create_symlink()? + } + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f723355 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,18 @@ +use std::path::Path; + +use dartfile::parse; + +mod cli; +mod dartfile; +mod dartgun; + +fn main() { + let test_path = Path::new("./dartgun.toml"); + let test_dotfile = parse(test_path); + println!("{:?}", parse(test_path)); + match test_dotfile.validate() { + Ok(_) => println!("Dotfile seems valid!"), + Err(_) => println!("Dotfile is invalid!"), + } + cli::run_cli(); +}