feat: add more init variables
This commit is contained in:
parent
60fe830b58
commit
4c4d386cca
|
@ -0,0 +1,171 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/python
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=python
|
||||
|
||||
### Python ###
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
pip-delete-this-directory.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
htmlcov/
|
||||
.tox/
|
||||
.nox/
|
||||
.coverage
|
||||
.coverage.*
|
||||
.cache
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
cover/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
.pybuilder/
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
# For a library or package, you might want to ignore these files since the code is
|
||||
# intended to run in multiple environments; otherwise, check them in:
|
||||
# .python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# poetry
|
||||
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
||||
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
||||
# commonly ignored for libraries.
|
||||
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
||||
#poetry.lock
|
||||
|
||||
# pdm
|
||||
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
||||
#pdm.lock
|
||||
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
||||
# in version control.
|
||||
# https://pdm.fming.dev/#use-with-ide
|
||||
.pdm.toml
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
dmypy.json
|
||||
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# pytype static type analyzer
|
||||
.pytype/
|
||||
|
||||
# Cython debug symbols
|
||||
cython_debug/
|
||||
|
||||
# PyCharm
|
||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
||||
#.idea/
|
||||
|
||||
### Python Patch ###
|
||||
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
|
||||
poetry.toml
|
||||
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/python
|
171
src/init.rs
171
src/init.rs
|
@ -1,171 +0,0 @@
|
|||
use std::{collections::HashMap, env};
|
||||
|
||||
use anyhow::Result;
|
||||
use dialoguer::{theme::ColorfulTheme, Input};
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
use toml::Value;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all(serialize = "kebab-case"))]
|
||||
pub struct Pyproject {
|
||||
pub build_system: BuildSystem,
|
||||
pub project: Project,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all(serialize = "kebab-case"))]
|
||||
pub struct BuildSystem {
|
||||
pub requires: Vec<String>,
|
||||
pub build_backend: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum Contributor {
|
||||
Flat(String),
|
||||
Complex { name: String, email: String },
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all(serialize = "kebab-case"))]
|
||||
pub struct Project {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub description: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub readme: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub requires_python: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub license: String,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub authors: Vec<Contributor>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub maintainers: Vec<Contributor>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub keywords: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub classifiers: Vec<String>,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub additional_fields: HashMap<String, Value>,
|
||||
}
|
||||
|
||||
pub fn ask_user_inputs(minimum: bool) -> Result<Pyproject> {
|
||||
let mut project = Project::default();
|
||||
let mut build_system = BuildSystem::default();
|
||||
let theme = ColorfulTheme::default();
|
||||
|
||||
build_system.requires = Input::<String>::with_theme(&theme)
|
||||
.with_prompt("build dependencies (comma separated)")
|
||||
.default("setuptools,wheel".to_string())
|
||||
.interact_text()?
|
||||
.split(',')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.to_string())
|
||||
.collect();
|
||||
build_system.build_backend = Input::with_theme(&theme)
|
||||
.with_prompt("build back-end")
|
||||
.default("setuptools.build_meta".to_string())
|
||||
.interact_text()?;
|
||||
|
||||
project.name = Input::with_theme(&theme)
|
||||
.with_prompt("project name")
|
||||
.default(
|
||||
env::current_dir()?
|
||||
.file_name()
|
||||
.expect("invalid current directory")
|
||||
.to_str()
|
||||
.expect("invalid UTF-8 cwd name")
|
||||
.to_string(),
|
||||
)
|
||||
.interact_text()?;
|
||||
project.version = Input::with_theme(&theme)
|
||||
.with_prompt("version")
|
||||
.default("0.1.0".to_string())
|
||||
.interact_text()?;
|
||||
|
||||
if !minimum {
|
||||
project.description = Input::with_theme(&theme)
|
||||
.with_prompt("description")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
project.readme = Input::with_theme(&theme)
|
||||
.with_prompt("readme")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
project.requires_python = Input::with_theme(&theme)
|
||||
.with_prompt("minimum python version")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
project.license = Input::with_theme(&theme)
|
||||
.with_prompt("license")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
project.authors = Input::<String>::with_theme(&theme)
|
||||
.with_prompt(
|
||||
r#"authors (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
||||
)
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| parse_contributor(v))
|
||||
.collect();
|
||||
project.maintainers = Input::<String>::with_theme(&theme)
|
||||
.with_prompt(
|
||||
r#"maintainers (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
||||
)
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| parse_contributor(v))
|
||||
.collect();
|
||||
project.keywords = Input::<String>::with_theme(&theme)
|
||||
.with_prompt("keywords (e.g: KEYW1;KEYW2)")
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.to_string())
|
||||
.collect();
|
||||
project.classifiers = Input::<String>::with_theme(&theme)
|
||||
.with_prompt("classifiers (e.g: CLASS1;CLASS2)")
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.to_string())
|
||||
.collect();
|
||||
}
|
||||
|
||||
Ok(Pyproject {
|
||||
build_system,
|
||||
project,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_contributor(contributor: &str) -> Contributor {
|
||||
let name_regex = Regex::new(r#"name="([\w\s\-\.]*)""#).expect("invalid name regex expression");
|
||||
let email_regex =
|
||||
Regex::new(r#"email="([\w\s\-\.@]*)""#).expect("invalid email regex expression");
|
||||
|
||||
let name = name_regex.captures(contributor);
|
||||
let email = email_regex.captures(contributor);
|
||||
|
||||
if name.is_some() && email.is_some() {
|
||||
Contributor::Complex {
|
||||
name: name.unwrap().get(1).unwrap().as_str().to_string(),
|
||||
email: email.unwrap().get(1).unwrap().as_str().to_string(),
|
||||
}
|
||||
} else if let Some(name_match) = name {
|
||||
Contributor::Flat(name_match.get(1).unwrap().as_str().to_string())
|
||||
} else if let Some(email_match) = email {
|
||||
Contributor::Flat(email_match.get(1).unwrap().as_str().to_string())
|
||||
} else {
|
||||
Contributor::Flat(contributor.to_string())
|
||||
}
|
||||
}
|
98
src/main.rs
98
src/main.rs
|
@ -1,22 +1,45 @@
|
|||
use std::{fs::write, path::PathBuf};
|
||||
use std::{
|
||||
env,
|
||||
fs::{self, File},
|
||||
path::PathBuf,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
use init::ask_user_inputs;
|
||||
use toml::toml;
|
||||
use pyproject::Pyproject;
|
||||
|
||||
mod init;
|
||||
mod pyproject;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, clap::ValueEnum)]
|
||||
enum Layout {
|
||||
Src,
|
||||
Flat,
|
||||
}
|
||||
|
||||
#[derive(clap::Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Args {
|
||||
/// Output will be more verbose
|
||||
#[arg(short, long, default_value_t = false)]
|
||||
verbose: bool,
|
||||
|
||||
/// Initialize a new python directory with minimal fields for "pyproject.toml"
|
||||
/// Initialize with minimal fields for "pyproject.toml"
|
||||
#[arg(short, long, default_value_t = true)]
|
||||
minimum: bool,
|
||||
|
||||
/// Initialize folder with a git repository
|
||||
#[arg(long, default_value_t = true)]
|
||||
git: bool,
|
||||
|
||||
/// Initialize a new virtual environment with given name in initialized directory
|
||||
#[arg(long)]
|
||||
venv: Option<String>,
|
||||
|
||||
/// Define a layout for your project (https://setuptools.pypa.io/en/latest/userguide/package_discovery.html)
|
||||
#[arg(long, value_enum)]
|
||||
layout: Option<Layout>,
|
||||
|
||||
#[command(subcommand)]
|
||||
subcommands: Subcommands,
|
||||
}
|
||||
|
@ -31,12 +54,71 @@ fn main() -> Result<()> {
|
|||
let args = Args::parse();
|
||||
|
||||
match args.subcommands {
|
||||
Subcommands::New { folder } => todo!(),
|
||||
Subcommands::New { folder } => {
|
||||
fs::create_dir(&folder)?;
|
||||
let folder = folder.canonicalize()?;
|
||||
|
||||
initialize_folder(folder, args.minimum, args.layout, args.venv, args.git)?;
|
||||
}
|
||||
Subcommands::Init {} => {
|
||||
let pyproject = ask_user_inputs(args.minimum)?;
|
||||
write("pyproject.toml", toml::to_vec(&pyproject)?)?;
|
||||
let folder = env::current_dir()?;
|
||||
|
||||
initialize_folder(folder, args.minimum, args.layout, args.venv, args.git)?;
|
||||
}
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialize_folder(
|
||||
folder: PathBuf,
|
||||
minimum: bool,
|
||||
layout: Option<Layout>,
|
||||
venv: Option<String>,
|
||||
git: bool,
|
||||
) -> Result<()> {
|
||||
// todo: avoid clone and maybe find a better way
|
||||
let mut pypro = Pyproject::new(folder.clone(), minimum);
|
||||
|
||||
pypro.ask_inputs()?;
|
||||
let project_name = pypro.get_project_name();
|
||||
|
||||
pypro.create_file()?;
|
||||
|
||||
if let Some(layout) = layout {
|
||||
let layout_inner_path = match layout {
|
||||
Layout::Src => {
|
||||
let inner_path = folder.join(format!("src/{project_name}"));
|
||||
fs::create_dir_all(&inner_path)?;
|
||||
|
||||
inner_path
|
||||
}
|
||||
Layout::Flat => {
|
||||
let inner_path = folder.join(project_name);
|
||||
fs::create_dir(&inner_path)?;
|
||||
|
||||
inner_path
|
||||
}
|
||||
};
|
||||
|
||||
File::create(layout_inner_path.join("__init__.py"))?;
|
||||
}
|
||||
if let Some(venv) = venv {
|
||||
Command::new("python3")
|
||||
.args(&["-m", "venv", &venv])
|
||||
.current_dir(&folder)
|
||||
.status()?;
|
||||
}
|
||||
if git {
|
||||
Command::new("git")
|
||||
.args(&["init"])
|
||||
.current_dir(&folder)
|
||||
.status()?;
|
||||
fs::write(
|
||||
folder.join(".gitignore"),
|
||||
include_bytes!("gitignore_python.txt"),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -0,0 +1,203 @@
|
|||
use std::{fs, path::PathBuf};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use dialoguer::{theme::ColorfulTheme, Input};
|
||||
use regex::Regex;
|
||||
use serde::Serialize;
|
||||
|
||||
const PYPROJECT: &str = "pyproject.toml";
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all(serialize = "kebab-case"))]
|
||||
pub struct Pyproject {
|
||||
#[serde(skip_serializing)]
|
||||
folder: PathBuf,
|
||||
#[serde(skip_serializing)]
|
||||
minimum: bool,
|
||||
pub build_system: BuildSystem,
|
||||
pub project: Project,
|
||||
}
|
||||
|
||||
impl Pyproject {
|
||||
pub fn new(folder: PathBuf, minimum: bool) -> Self {
|
||||
Pyproject {
|
||||
folder,
|
||||
minimum,
|
||||
build_system: BuildSystem::default(),
|
||||
project: Project::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ask user inputs to create a basic (or not) pyproject.toml file.
|
||||
/// `minimum` will only ask for the `build-system` fields and the `project.name`
|
||||
/// and `project.version` fields
|
||||
pub fn ask_inputs(&mut self) -> Result<()> {
|
||||
let theme = ColorfulTheme::default();
|
||||
|
||||
self.build_system.requires = Input::<String>::with_theme(&theme)
|
||||
.with_prompt("build dependencies (comma separated)")
|
||||
.default("setuptools,wheel".to_string())
|
||||
.interact_text()?
|
||||
.split(',')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.to_string())
|
||||
.collect();
|
||||
self.build_system.build_backend = Input::with_theme(&theme)
|
||||
.with_prompt("build back-end")
|
||||
.default("setuptools.build_meta".to_string())
|
||||
.interact_text()?;
|
||||
|
||||
// ? might want to switch to OsString instead, if the Serialize macro supports it
|
||||
let folder = match self
|
||||
.folder
|
||||
.file_name()
|
||||
.ok_or(anyhow!("project can't terminate by \"..\""))?
|
||||
.to_str()
|
||||
{
|
||||
Some(v) => Some(v.to_string()),
|
||||
None => None,
|
||||
};
|
||||
let mut input: Input<String> = Input::with_theme(&theme);
|
||||
if let Some(folder) = folder {
|
||||
self.project.name = input
|
||||
.with_prompt("project name")
|
||||
.default(folder)
|
||||
.interact_text()?;
|
||||
} else {
|
||||
self.project.name = input.with_prompt("project name").interact_text()?;
|
||||
}
|
||||
|
||||
self.project.version = Input::with_theme(&theme)
|
||||
.with_prompt("version")
|
||||
.default("0.1.0".to_string())
|
||||
.interact_text()?;
|
||||
|
||||
if !self.minimum {
|
||||
self.project.description = Input::with_theme(&theme)
|
||||
.with_prompt("description")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
self.project.readme = Input::with_theme(&theme)
|
||||
.with_prompt("readme")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
self.project.requires_python = Input::with_theme(&theme)
|
||||
.with_prompt("minimum python version")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
self.project.license = Input::with_theme(&theme)
|
||||
.with_prompt("license")
|
||||
.allow_empty(true)
|
||||
.interact_text()?;
|
||||
self.project.authors = Input::<String>::with_theme(&theme)
|
||||
.with_prompt(
|
||||
r#"authors (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
||||
)
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| self.parse_contributor(v))
|
||||
.collect();
|
||||
self.project.maintainers = Input::<String>::with_theme(&theme)
|
||||
.with_prompt(
|
||||
r#"maintainers (e.g: "Antoine Langlois";"name="Antoine L",email="email@domain.net"")"#,
|
||||
)
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| self.parse_contributor(v))
|
||||
.collect();
|
||||
self.project.keywords = Input::<String>::with_theme(&theme)
|
||||
.with_prompt("keywords (e.g: KEYW1;KEYW2)")
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.to_string())
|
||||
.collect();
|
||||
self.project.classifiers = Input::<String>::with_theme(&theme)
|
||||
.with_prompt("classifiers (e.g: CLASS1;CLASS2)")
|
||||
.allow_empty(true)
|
||||
.interact_text()?
|
||||
.split(';')
|
||||
.filter(|v| !v.is_empty())
|
||||
.map(|v| v.to_string())
|
||||
.collect();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_contributor(&self, contributor: &str) -> Contributor {
|
||||
let name_regex =
|
||||
Regex::new(r#"name="([\w\s\-\.]*)""#).expect("invalid name regex expression");
|
||||
let email_regex =
|
||||
Regex::new(r#"email="([\w\s\-\.@]*)""#).expect("invalid email regex expression");
|
||||
|
||||
let name = name_regex.captures(contributor);
|
||||
let email = email_regex.captures(contributor);
|
||||
|
||||
if name.is_some() && email.is_some() {
|
||||
Contributor::Complex {
|
||||
name: name.unwrap().get(1).unwrap().as_str().to_string(),
|
||||
email: email.unwrap().get(1).unwrap().as_str().to_string(),
|
||||
}
|
||||
} else if let Some(name_match) = name {
|
||||
Contributor::Flat(name_match.get(1).unwrap().as_str().to_string())
|
||||
} else if let Some(email_match) = email {
|
||||
Contributor::Flat(email_match.get(1).unwrap().as_str().to_string())
|
||||
} else {
|
||||
Contributor::Flat(contributor.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_project_name(&self) -> String {
|
||||
// ? clone or maybe something else ?
|
||||
self.project.name.clone()
|
||||
}
|
||||
/// Consume self and write everything to a `self.folder/pyproject.toml`
|
||||
pub fn create_file(self) -> Result<()> {
|
||||
fs::write(self.folder.join(PYPROJECT), toml::to_vec(&self)?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all(serialize = "kebab-case"))]
|
||||
pub struct BuildSystem {
|
||||
pub requires: Vec<String>,
|
||||
pub build_backend: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
pub enum Contributor {
|
||||
Flat(String),
|
||||
Complex { name: String, email: String },
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
#[serde(rename_all(serialize = "kebab-case"))]
|
||||
pub struct Project {
|
||||
pub name: String,
|
||||
pub version: String,
|
||||
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub description: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub readme: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub requires_python: String,
|
||||
#[serde(skip_serializing_if = "String::is_empty")]
|
||||
pub license: String,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub authors: Vec<Contributor>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub maintainers: Vec<Contributor>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub keywords: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub classifiers: Vec<String>,
|
||||
}
|
Loading…
Reference in New Issue