initial commit
This commit is contained in:
134
src/main.rs
Normal file
134
src/main.rs
Normal 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()
|
||||
}
|
||||
Reference in New Issue
Block a user