initial commit

This commit is contained in:
aaron-jack-manning
2023-11-02 10:52:36 +11:00
commit 0a2a9a8637
5 changed files with 473 additions and 0 deletions

134
src/main.rs Normal file
View File

@@ -0,0 +1,134 @@
use std::io;
use std::fs;
use std::env;
use std::path;
use std::process;
use std::borrow::Cow;
#[derive(clap::Parser)]
struct Args {
manifest : path::PathBuf,
#[clap(default_value = "local")]
namespace : String,
}
#[derive(serde::Deserialize)]
struct Manifest<'a> {
package : Package<'a>,
}
#[derive(serde::Deserialize)]
#[allow(dead_code)]
struct Package<'a> {
/// The package's identifier in its namespace.
name : Cow<'a, str>,
/// The package's version as a full major-minor-patch triple. Package versioning should follow SemVer.
version : Cow<'a, str>,
/// The path to the main Typst file that is evaluated when the package is imported.
entrypoint : Cow<'a, path::Path>,
}
fn main() {
let args : Args = clap::Parser::parse();
let manifest = match fs::read_to_string(&args.manifest) {
Ok(file) => file,
Err(err) => {
eprintln!("Failed to read in manifest file: {}", err);
process::exit(1)
}
};
let manifest : Manifest<'_> = match toml::from_str(&manifest[..]) {
Ok(file) => file,
Err(err) => {
eprintln!("{}", err);
process::exit(1)
}
};
// TODO move version number parsing into the deserialization itself
if let None = read_version(&manifest.package.version[..]) {
eprintln!("Invalid version number");
process::exit(1)
}
// See https://github.com/typst/packages#local-packages for package locations
let data = match env::consts::OS {
"linux" => {
env::var("XDG_DATA_HOME").unwrap_or("~/.local/share".to_owned())
},
"macos" => {
"~/Library/Application Support".to_owned()
},
"windows" => {
env::var("APPDATA").unwrap()
},
os => {
eprintln!("{} is not a supported operating system", os);
process::exit(1)
}
};
let entrypoint =
args.manifest.parent().unwrap().join(manifest.package.entrypoint);
if !entrypoint.exists() {
eprintln!("Entrypoint file {:?} does not exist", entrypoint);
process::exit(1)
}
let package_folder = path::PathBuf::from(data)
.join("typst/packages")
.join(args.namespace)
.join(manifest.package.name.into_owned())
.join(manifest.package.version.into_owned());
if package_folder.exists() {
loop {
print!("Package of the corresponding name and version already exists, would you like to continue, overwriting the existing package (y/n)? ");
use io::Write;
io::stdout().flush().unwrap();
let mut input = String::new();
match io::stdin().read_line(&mut input) {
Ok(_) => match &input.trim().to_lowercase()[..] {
"y" => break,
"n" => return,
_ => continue,
},
Err(_) => continue,
}
}
}
if let Err(err) = copy_dir_all(args.manifest.parent().unwrap(), package_folder) {
eprintln!("Error occured while copying package folder to installation location: {}", err);
process::exit(1)
}
}
fn copy_dir_all(src: impl AsRef<path::Path>, dest: impl AsRef<path::Path>) -> io::Result<()> {
fs::create_dir_all(&dest)?;
for entry in fs::read_dir(src)? {
let entry = entry?;
if entry.file_type()?.is_dir() {
copy_dir_all(entry.path(), dest.as_ref().join(entry.file_name()))?;
} else {
fs::copy(entry.path(), dest.as_ref().join(entry.file_name()))?;
}
}
Ok(())
}
fn read_version(version : &str) -> Option<[u16; 3]> {
let version =
version.split(".")
.map(str::parse::<u16>)
.map(Result::ok)
.collect::<Option<Vec<u16>>>()?;
<[u16; 3]>::try_from(version).ok()
}