text:
---
created: ["{{date}} {{time}}"]
tags: ["#Log/WeeklyLog"]
---
___

### 📅 Days
-

### 📑 Weekly Reviews
-

### 🔃 Reflection
-
### 💯 Rating 0 - 10
-
### 📜 Events
**Biggest Career Milestone**
-
### 📃 Projects
**What did I accomplish?**
-
**Am I satisfied with my progress?**
-
**What setbacks did I face?**
-
**What are some possible improvements and plans for the future??**

### 💾 Information to retain from the daily logs


text:
This template needs DataView plugin

---
created: ["{{date}} {{time}}"]
tags: ["#Log/WeeklyLog"]
---
___
```toc
 style: number
```
# 🌌 Overview ->
```dataview
TABLE WITHOUT ID
	link(file.name) as "Day",
	Breakfast AS "🍜",
	Feeling AS "✨",
	working-on AS "✏",
	money-spent AS "💸",
	Workout AS "💪",
	Motivation AS "💹"
WHERE file.folder = this.file.folder AND
 contains(tag, "#Log/dailylog")
SORT file.name ASC
```

### 📑 Weekly Reviews
-

### 🔃 Reflection
-
### 📜 Events
-
### 📃 Projects
#### **What did I accomplish?**
-
#### **Am I satisfied with my progress?**
-
#### **What setbacks did I face?**
-
#### **What are some possible improvements and plans for the future??**
-
### 💾 Information to retain from the daily logs
-

mime_type: text/plain


text:
This template needs Dynamic Table of Contents plugin and Advanced Tables
---
created: ["{{date}} {{time}}"]
tags: ["#Log/WeaklLog"]
---
___
```toc
 style: number
```
# 🌌 Overview -> Week Report
🍜 - Breakfast |✨ - Feeling |✏ - Working On
💲 - Money Spent |💪 -Workout |📈 Motivation

| Day | 🍜  | ✨  | ✏  | 💲  | 💪  | 📈  |
| --- | --- | --- | --- | --- | --- | --- |
|     |     |     |     |     |     |     |
### 📑 Got Done This Week
-

### 🔃 Need to Work On
-
### 📜 Events
-
### 📃 Projects
-
### 💾 Information to retain
-


text:
---
created: ["16-02-2022 17:38"]
aliases: ["{{title}}"]
---

# 📔 Type -> Name
___
|                               |     |     |
| ----------------------------- | --- | --- |
| Image::.                      |     |     |
| Original Name::.              |     |     |
| Started on::.                 |     |     |
| Completed on::.               |     |     |
| Perched on::.                 |     |     |
| Price::.                      |     |     |
| Producer::.                   |     |     |
| Studio::.                     |     |     |
| Writer::.                     |     |     |
| Genres::.                     |     |     |
| Demographic::.                |     |     |
| Rated::.                      |     |     |
| Score::.                      |     |     |
| Chapter/Episodes::.           |     |     |
| Chapter/Episodes completed::. |     |     |
| Tags::.                       |     |     |
___
## ❗ Impressions
-
### ❓ How I Discovered It ?
-


text:
---
created: ["{{date}} {{time}}"]
aliases: ["Blog Post Code"]
---

# ❓ Information
Type::
Writer::
Tags::
Subject::
Started Reading on::
Completed on::
Perched on::
Price::

---
# ❗ Relevant data
## 🎯 What Is The Objective
## 📦 Information Resources

# 🔰 Blog post ->
A blog post is like a conversation, you need to explain the information however you must only speak about subjects you understand and like.
## 1⃣ Intro
* A promise statement
* A preview of what's to come
## 2⃣ Overview
* A simple definition
* Examples
## 📃 Steps
* Detail of each the steps
# ✅ Checklist Inspiration
- [ ] Brainstorm the topics that I want to write about in bullet points
- [ ] Reorder those bullet points to create a line of thought Draft
- [ ] Expand those bullet points into sentences/text
- [ ] Draft 5 titles and pick one
- [ ] Revise the complete text for typos and any rephrasing that need to be made
- [ ] Publish or schedule the post
- [ ] Promote


text:
---
created: ["{{date}} {{time}}"]
aliases: ["{{title}}"]
---
# 📃 Course ->
---
# ❓ Information
Type::.
Speaker::.
Course-Origin::.
Subject::.
Tags::.
Started Reading on::.
Completed on::.
Perched on::.
Price::.
Certification::.

# 🌍 What It's About
-
---

## 📜 Notes and Ideas

## 🎯 Assignments
-
## ⌛ Lectures
-
## 📦 Resources
-
## 📅 Important Dates
-


text:
---
created: ["{{date}} {{time}}"]
aliases: ["{{title}}"]
---

# ❗❓ Information
Related to Course::
Date::
Professor/Speaker::
Tags::

---
# ❗ Topic

## 📦 Resources
-
## 🔑 Key Points
-
## ❓ Questions
-
## 🎯 Actions
- [ ]
- [ ]
- [ ]
- [ ]
- [ ]
## 📃 Summary of Notes
-


text:
---
created: ["{{date}} {{time}}"]
aliases: ["{{title}}"]
---

# ❗❓ Information
Related to::.
Tags::.

# 🌌 Course ->
---

# ❗ Description

## ❗ Answer


text:
---
created: ["{{date}} {{time}}"]
aliases: ["Recipe: {{title}}"]
---

# 🍽 Recipe ->

# ❓ Information
Type::.
Origin::.
Tags::.
## 🍜 Ingredients
1. [ ]

## 📑 Directions
1. [ ]


text:
---
created: ["{{date}} {{time}}"]
aliases: ["Book: {{title}}"]
---

# 📔 Book ->
___
# ❓ Information
Type::
Writer::
Tags::.
Subject::
Started Reading on::
Completed on::
Perched on::
Price::
___
# 🌍 What It's About
-
# 🔍 How I Discovered IT
-
# 🧠 Thoughts
-
## What I Liked About IT
-
## What I Didn't Like About it
-
# ✍ The Book in 3 Sentences
# ✍ My Top 3 Quotes
# 🎨 Impressions
# ☘ How the Book Changed Me
# 📒 Summary + Notes
# 🥰 Who Would Like it ?
-
# 📚Related Books
-


text:
---
created: ["{{date}} {{time}}"]
tags: ["#Evaluation/"]
---
# ❓ Your Questions is
- Brief description of the query.

## ✅ Reasoning for
1.
## ⛔ Reasoning against
1.
## 🔀 Alternatives considered
1.
## 📦 Resources
- Link to any relevant information

## 🧮 Pros and Cons calculation

|Pro | value 0-10 | Con | value 0-10|
|--|-----|------|------|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Pro1| 10| Con 1 |10|
|Total Pro| 10| Total Con |10|

❗❗❗ If the total divided by 100 don't have at least 20% of difference add more Pros and Cons.


text:
---
aliases: ["first_name"]
created: ["{{date}} {{time}}"]
---
# {{title}}
----
## Contact Info
Full Name::
Email::
Company::
Phone::
Address::
Linked Files::

----

## Log
-

### {{date}} {{time}} - Initial Creation

notes_go_here


text:
This template needs DataView plugin
---
created: ["{{date}} {{time}}"]
tags: ["#Log/DaiLylog"]
---
# Metadata:
- Tag:: #Log/dailylog
- Created:: {{date: DD-MM-YYYY}}
- Week::

---

```toc
 style: number
```

## 🔷Tracker
- Breakfast::
- Feeling::
- Working On::
- Money Spent:: €
- Workout::
- Motivation::

##  💬 Lingering feelings, Observations and Thoughts
1. Lingering Feelings
	1.
2. Observations
	1.
3. Thoughts
	1.
## 🔃 Reflection
1.
---

## 📅 Today's Notes
- 🕛9:00
	- Wok up and


text:
---
created: ["{{date}} {{time}}"]
tags: ["#Log/DailyLog"]
---

# 📅 Daily Log {{date: DD-MM-YYYY}}

---
## 🔷 Daily Tasks
- [ ] Breakfast
- [ ] Study something
- [ ] Work on personal projects
- [ ] Workout
	- [ ] Push up's
	- [ ] Sit downs
	- [ ] Deeps
# 🕴 Business
## 🎯 Objectives
-
## 🚀 Working On
-
## 📕 Reminders
-
## 📚 Reading
-
##  💬 Lingering feelings, Observations and Thoughts
1. Lingering Feelings
	1.
2. Observations
	1.
3. Thoughts
	1.
## 🔃 Reflection
1.
---

## 📅 Today's Notes
- 🕛9:00
	- Wok up and


text:
---
created: ["{{date: DD-MM-YYYY}} {{time}}"]
aliases: ["Project Code"]
---

# 🚀 Project ->
___
## 🧾 Project Description
-
---
## 📢 Project information
Created::. {{date: DD-MM-YYYY}} {{time}}
Deadline::
Hibernating::.
Completion date expected::.
Completed::.
Type::.
Tags::.
Platform::.

## 🧲Published
- Reddit::
- Discord::
- Forum::
- Website::
- Twitter::
- Instagram::
- Facebook::
- GitHub::
___
## 🎯 Objective

1. 🟢 Ideal project result
	1.
2. 🟠 Acceptable result
	1.
## ❓ Expectations
1. 🟢 Helpful to the project
	1.
2. 🟠 Roadblocks
	1.
3. 👶 Naivety
	1.
4. 👨💻 Insights
	1.
## ✅ Tasks
-
## 📦 Resources
-
## 📂 Project Logs
-


text:
---
created: ["{{date: DD-MM-YYYY}} {{time}}"]
aliases: ["Project Code"]
---

# 🚀 Project ->
___
## 🧾 Project Description
-
---
## 📢 Project information
Created::. {{date: DD-MM-YYYY}} {{time}}
Deadline::
Hibernating::.
Completion date expected::.
Completed::.
Type::.
Tags::.
Platform::.

___
## 🎯 Objective

1. 🟢 Ideal project result
	1.
2. 🟠 Acceptable result
	1.
## ❓ Expectations
1. 🟢 Helpful to the project
	1.
2. 🟠 Roadblocks
	1.
3. 👶 Naivety
	1.
4. 👨💻 Insights
	1.
## ✅ Tasks
-
## 📦 Resources
-
## 📂 Project Logs
-


text:
### 🚀 Dream -

- 🧠 Why?

- 🎯 SMART Goal

- ⚛ Habit?

- 🤯 How surprised would I be if I failed? (Scale of 1-10)

- ☠ Top 3 Reasons for Failing

- 🦸 WHO can help?

- 📚 HOW can I stack the deck to succeed?

- 🔥 ACTION

# All below is just an example

### 🚀 Dream - Take better notes

- 🧠 Why?
	- Have all the information i need when i need it

- 🎯 SMART Goal
	- Use better and more easier to understand notes

- ⚛ Habit?
	- Right a note every day enplaning to myself how was the day

- 🤯 How surprised would I be if I failed? (Scale of 1-10)
	- 10

- ☠ Top 3 Reasons for Failing
	- Lack of time
	- Unable to use PC, Phone or Tablet for some reason
	- To much work going on that only leaves time to sleep

- 🦸 WHO can help?
	- Me, my self and I

- 📚 HOW can I stack the deck to succeed?
	- Instead of trying to make all the notes at once, make it as the day goes by

- 🔥 ACTION
	- Set a timer for every task and stick to it


text:
---
created: ["{{date}} {{time}}"]
tags: ["#Log/YearlyLog"]
---

# 📆 YEAR {{date: YYYY}}
## 🔃 Reflection
-
### 💯 Rating 0 -10
Happiness::
Productivity::
Relationships::
Focus::
### 📜 Events
**Biggest Personal Milestone**::
-
**Biggest Career Milestone**::
-
### 🚀 Projects
-
### 🏢 Career
-
### 📅 Future Plan
-
### ✅ Action
> ***Be thorough in planning!***
- [ ] Update Yearly Objectives
- [ ] Update Task List
### 📚 To Read
-
### 📗 Already read
-
# ❓ Yearly Quiz
### 🔙 Past Year
1. Do you have a word for the year?
	1.
2. What was the biggest Personal Milestone?
	1.
3. What did you discover about yourself?
	1.
4. When did fear hold you back?
	1.
5. Where did you practice courage?
	1.
6. What surprised you?
	1.
7. How have you taken care of yourself physically?
	1.
8. How have you taken care of yourself mentally?
	1.
9. How have you taken care of yourself emotionally?
	1.
10. What are you ready to begin?
	1.
11. What are you ready to end?
	1.
12. How has this year impacted your priorities?
	1.
13. How has this year impacted your home life?
	1.
14. How has this year impacted your relationships?
	1.
15. How has this year impacted your work life?
	1.

### ⏭ Next Year
1. What are you looking forward to in the next year?
	1.
2. What are you feeling apprehensive about?
	1.
3. What area of your life do you most want to develop in the next year?
	1.
4. Where do you want to be in your head?
	1.
5. Where do you want to be in your heart?
	1.
6. What are your priorities for the next year?
	1.
7. I will make more time for:
	1.
8. I will learn about:
	1.
9. I will say NO to:
	1.
10. I will say YES to:
	1.


text:
---
created: ["{{date}} {{time}}"]
---

# ❗ Information
Related to::
Tags::

# 🌌 Action plan ->
---

# 🧾 Description
-

## 🌐 Action to perform
1.


text:
---
created: ["{{date}} {{time}}"]
---

# ❗ Information
Related to::
Tags::

# 💻 Application ->

## 🧾 Description
-
## 🌐 Link
-


text:
---
aliases: ["{{title}}"]
---

# {{title}} Index
---
## 1⃣ - Theme1
- File link
	- Brief description

## 2⃣ - Theme 2
- File link
	- Brief description


text:
---
created: ["{{date}} {{time}}"]
tags: ["#List/"]
---
# 💠 Task List
## 🔴 URGENT
*Do it now*
- [ ] Task 1
- [ ] Task 2
- [ ] Task 3
### 🟠 IMPORTANT
*Do it after tasks above*
- [ ] Task 1
- [ ] Task 2
- [ ] Task 3
#### 🟡 NOT URGENT / IMPORTANT
*Decide when to do it*
- [ ] Task 1
- [ ] Task 2
- [ ] Task 3
##### 🟢 Completed
- [x] Task 1
- [x] Task 2
- [x] Task 3


text:
# Changelog

## v0.4.1

- Additions:
  - Python template defaults to adding a 'format = "setuptools";' to align with nixpkgs preferences

- Fixes:
  - Clearer error when repository doesn't exist
  - Minor pypi serialization fixes

## v0.4.0

- Breaking Changes:
  - `overlay` for flake template has been moved to `overlays.default` to align with upstream changes
  - `-u` will now use an sri hash, to align with nix 2.4+ behavior

- Additions:
  - `-u` when fetching from pypi will now automatically add dependencies

- Fixes:
  - Fix failure with pypi responses not containing a platform
  - `-u` with pypi will now filter out pre-releases when determining latest release
  - Default to repo name when using `-u`

## v0.3.0

- Breaking Changes:
  - `overlays` exposed in flake are now an attr set, to better align with more recent nix versions

- Additions:
  - `aarch64-darwin` added to flake system defaults

- Improvements:
  - Serialization errors will now mention which assumption caused the failure @blaggacao
  - Updated github.com auto-detected licenses to include recently added `Apache License 2.0`
  - Fixed usage of `mkApp` inside flake tempalte

- Fixes:
  - Fix unprefixed versions being generated as `version = "version";` @blaggacao
  - Fixed directories being passed as `[PATH]` not becoming `dir/default.nix`

## v0.2.0

- Breaking Changes / Behaviors:
  - Flake template now requires -p,--pname
  - Nix expresions now have input attrs in comma-leading style (one input per line)

- Fixes:
  - --from-url no longer errors with --nixpkgs when a pname is not supplied
  - Fix differences of writing to stdout vs file
  - Update flake template (overlay usage)
  - Failures from already existing file locations occur sooner
    - Particularly irritating with `--from-url`, which would compute release and sha256 info

## v0.1.4

- Cleanup pypi noise

## v0.1.3

- Fix Cargo.lock file

## v0.1.2

- Add `-u, --from-url` support to pypi.org
- Fix crash when github's hompage url is null when used with `-u`
- Add mention of `GITHUB_TOKEN` to usage

## v0.1.1

- Add nixos module template
- Add nixos test template
- Add flake template
- Add `-u,--from-url` option
  - Github supported

## v0.1.0

- Add the following templates:
  - stdenv
  - python
  - mkshell
  - go
  - rust
  - qt
- Allow users to configure persistent maintainer name and nixpkgs location
- Add shell completions

repo_name: jonringer/nix-template

repo_language: Rust


text:
with import <nixpkgs> { };

callPackage ./nix/nix-template.nix { }


text:
{
  description = "Nix-template flake";

  inputs = {
    utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
  };

  outputs = { self, nixpkgs, utils }:
    let
      # put devShell and any other required packages into local overlay
      localOverlay = import ./nix/overlay.nix;

      pkgsForSystem = system: import nixpkgs {
        # if you have additional overlays, you may add them here
        overlays = [
          localOverlay # this should expose devShell
        ];
        inherit system;
      };
    # https://github.com/numtide/flake-utils#usage for more examples
    in utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ] (system: rec {
      legacyPackages = pkgsForSystem system;
      packages = utils.lib.flattenTree {
        inherit (legacyPackages) devShell nix-template;
      };
      defaultPackage = packages.nix-template;
      apps.nix-template = utils.lib.mkApp { drv = packages.nix-template; };  # use as `nix run <mypkg>`
      hydraJobs = { inherit (legacyPackages) nix-template; };
      checks = { inherit (legacyPackages) nix-template; };              # items to be ran as part of `nix flake check`
  }) // {
    # non-system suffixed items should go here
    overlays.default = localOverlay;
    nixosModule = { config }: { options = {}; config = {};}; # export single module
    nixosModules = {}; # attr set or list
    nixosConfigurations.hostname = { config, pkgs }: {};
  };
}


text:
[package]
name        = "nix-template"
version     = "0.4.1"
description = "Utility to generate common nix expressions"
license     = "CC0-1.0"
homepage    = "https://github.com/jonringer/nix-template"
repository  = "https://github.com/jonringer/nix-template"
authors     = ["Jonathan Ringer <jonringer117@gmail.com>"]
edition     = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = "^2.0"
env_logger = "0.8"
lazy_static = "^1.4"
regex = "^1.3"
xdg = "^2.1"
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"
serde_path_to_error = "0.1"
toml = "^0.5.0"
reqwest = { version = "^0.11.0", features = [ "json", "blocking"] }
log = "^0.4"
anyhow = "^1.0"
version-compare = "^0.0.11"

[dev-dependencies]
serial_test = "0.4"
pretty_assertions = "0.6"
serde_derive = "^1.0"


text:
with import <nixpkgs> {};

callPackage ./nix/shell.nix { }


text:
# Nix-template

*NOTE:* This is still WIP, but should be useful in most situations

Make creating nix expressions easy. Provide a nice way to create largely boilerplate nix-expressions.

[![Packaging status](https://repology.org/badge/vertical-allrepos/nix-template.svg)](https://repology.org/project/nix-template/versions)

## Roadmap

- [ ] Finalize cli semantics
- Ease usage with nixpkgs repo
  - [X] Write to correct location using path
    - [X] Improve logic around directories vs files
    - [ ] Improve template-specific items
      - [ ] generate buildGoModule's depsSha256
      - [ ] generate buildRustPackages's cargoSha256
  - [X] Print top-level addition statement
- Support Language/frameworks/usage templates:
  - [X] Stdenv
  - [X] Python
  - [X] mkShell
  - [x] Qt
  - [x] Go
  - [x] Rust
  - [x] Flakes
  - [x] NixOS Module
  - [x] NixOS Test
  - [ ] Haskell
  - [ ] and many more...
- [ ] Add option (-d, --documentation-url) to embed noob-friendly comments and explanations about common usage patterns
- Allow contributor information to be set locally (similar to git settings)
  - [X] Set maintainer name through `$XDG_CONFIG_HOME`
  - [X] Set nixpkgs-root path through `$XDG_CONFIG_HOME`
- Better integration with fetchers
  - Automatically determine version and sha256
    - [X] Github (need a way to pass owner and repo)
    - [X] Pypi (will need a way to pass pypi pname, as it may differ from installable path)
- [X] Implement shell completion (nix-template completions <SHELL>)
- [ ] Implement `project` subcommand, for creating a flake setup for new projects #51
  - [ ] Implement `init` subcommand, for creating new projects #52

## Current Usage (--from-url, github and pypi only)

```bash
$ nix-template rust -n --from-url github.com/jonringer/nix-template
Creating directory: /home/jon/projects/nixpkgs/pkgs/applications/misc/nix-template
Generating python expression at /home/jon/projects/nixpkgs/pkgs/applications/misc/nix-template/default.nix
Please add the following line to the approriate file in top-level:

  nix-template = callPackage ../applications/misc/nix-template { };
```
The resulting file:
```
# $NIXPKGS_ROOT/pkgs/applications/misc/nix-template/default.nix
{ lib
, rustPlatform
, fetchFromGitHub
}:

rustPlatform.buildRustPackage rec {
  pname = "nix-template";
  version = "0.3.0";

  src = fetchFromGitHub {
    owner = "jonringer";
    repo = pname;
    rev = "v${version}";
    sha256 = "sha256-5redgssfwbNEgpjmakIcU8cL4Xg1kPvyK88v+xMqAtw=";
  };

  cargoSha256 = "0000000000000000000000000000000000000000000000000000";

  buildInputs = [ ];

  meta = with lib; {
    description = "Make creating nix expressions easy";
    homepage = "https://github.com/jonringer/nix-template";
    license = licenses.cc0;
    maintainers = with maintainers; [ jonringer ];
  };
}
```

## Current Usage (Generically)

```bash
# only need to config once per user
$ nix-template config name jonringer
$ nix-template config nixpkgs-root /home/jon/projects/nixpkgs

# add a package
$ nix-template python --nixpkgs --pname requests -f pypi -l asl20
Creating directory: /home/jon/projects/nixpkgs/pkgs/development/python-modules/requests/
Generating python expression at /home/jon/projects/nixpkgs/pkgs/development/python-modules/requests/default.nix
Please add the following line to the approriate file in top-level:

  requests = callPackage ../development/python-modules/requests { };
```
```nix
# pkgs/development/python-modules/requests/default.nix
{ lib
, buildPythonPackage
, fetchPypi
}:

buildPythonPackage rec {
  pname = "requests";
  version = "0.0.1";

  src = fetchPypi {
    inherit pname version;
    sha256 = "0000000000000000000000000000000000000000000000000000";
  };

  propagatedBuildInputs = [ ];

  pythonImportsCheck = [ "requests" ];

  meta = with lib; {
    description = "CHANGEME";
    homepage = "https://github.com/CHANGEME/requests/";
    license = licenses.asl20;
    maintainer = with maintainers; [ jonringer ];
  };
}
```

### Installation

from nixpkgs (unstable, not available in 20.03):
```
$ nixenv -iA nix-template
```

with nix-cli (from this repository):
```
$ nix-env -f default.nix -iA ""
```

with cargo
```
$ cargo install --path .
```

### Development

Installing depedencies on nixpkgs:
```
nix-shell
# or
nix develop
```

Other platforms, you'll need the following dependencies:
  - cargo
  - rustc
  - rust-clippy

## End Goal (Only better nixpkgs support missing)

```bash
# only need to config once per user
$ nix-template config name jonringer
$ nix-template config nixpkgs-root /home/jon/projects/nixpkgs

# add a package
$ nix-template python --nixpkgs -u https://pypi.org/project/requests/
Determining latest release for requests
Creating directory: /home/jon/projects/nixpkgs/pkgs/development/python-modules/requests/
Generating python expression at /home/jon/projects/nixpkgs/pkgs/development/python-modules/requests/default.nix
For an addition to nixpkgs as a python package, please add the following to pkgs/top-level/python-packages.nix:

  requests = callPackage ../development/python-modules/requests { };

For an addition to nixpkgs as a python application, please add the following to pkgs/top-level/all-packages.nix:

  requests = with python3Packages; toPythonApplication requests { };
```
```nix
{ lib
, buildPythonPackage
, fetchPypi
, certifi
, charset-normalizer
, idna
, urllib3
}:

buildPythonPackage rec {
  pname = "requests";
  version = "2.28.1";

  src = fetchPypi {
    inherit pname version;
    sha256 = "sha256-fFWZsQL+3apmHIJsVqtP7ii/0X9avKHrvj5/GdfJeYM=";
  };

  propagatedBuildInputs = [
    certifi
    charset-normalizer
    idna
    urllib3
  ];

  pythonImportsCheck = [ "requests" ];

  meta = with lib; {
    description = "Python HTTP for Humans";
    homepage = "https://requests.readthedocs.io";
    license = licenses.asl20;
    maintainers = with maintainers; [ jonringer ];
  };
}
```


text:
name: Rust

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

env:
  CARGO_TERM_COLOR: always

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Build
      run: cargo build --verbose
    - name: Run tests
      run: cargo test --verbose
    - name: Run lint
      run: rustup component add clippy && cargo clippy


text:
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};

use crate::file_path::nix_file_paths;
use crate::types::{ExpressionInfo, Fetcher, Template, UserConfig};
use crate::url::read_meta_from_url;

// clap will validate inputs, only use on functions with possible_values defined
pub fn arg_to_type<T>(arg: Option<&str>) -> T
where
    T: std::str::FromStr,
    <T as std::str::FromStr>::Err: std::fmt::Debug,
{
    arg.unwrap().parse::<T>().unwrap()
}

// There is the assert macro, but the panic output does not look great
pub fn assert(pred: bool, message: &str) {
    if !pred {
        eprintln!("{}", message);
        #[cfg(not(test))]
        std::process::exit(1);
        #[cfg(test)]
        panic!("{}", message)
    }
}

pub fn build_cli() -> App<'static, 'static> {
    App::new("nix-template")
        .version("0.4.1")
        .author("Jon Ringer <jonringer117@gmail.com>")
        .about("Create common nix expressions")
        .version_short("V")
        .setting(AppSettings::ColoredHelp)
        // make completions and other subcommands distinct from
        // default template usage
        .setting(AppSettings::SubcommandsNegateReqs)
        // make it so that completions subcommand doesn't
        // inherit global options
        .setting(AppSettings::ArgsNegateSubcommands)
        .after_help(
            "ENV VARS:

    GITHUB_TOKEN\tToken used during github api calls.

EXAMPLES:

# generate an expression for this package
$ nix-template rust --from-url https://github.com/jonringer/nix-template

# generate a python package expressison at pkgs/development/python-modules/requests/default.nix
$ nix-template python --nixpkgs --pname requests

# generate a shell.nix in $PWD
$ nix-template mkshell

# set maintainer name and location of nixpkgs, only needs to be set once per user
$ nix-template config name jonringer
$ nix-template config nixpkgs-root ~/nixpkgs

",
        )
        .arg(
            Arg::from_usage("<TEMPLATE> 'Language or framework template target'")
                .possible_values(&Template::variants())
                .case_insensitive(true)
                .default_value("stdenv"),
        )
        .arg(
            Arg::from_usage("[PATH] 'directory or file to be written. In the case of a directory, a default.nix will be created. When used with --nixpkgs, it will be appended to nixpkgs-root to determine path location.'")
                .default_value("default.nix")
                .default_value_if("TEMPLATE", Some("mkshell"), "shell.nix")
                .default_value_if("TEMPLATE", Some("test"), "test.nix")
                .default_value_if("TEMPLATE", Some("flake"), "flake.nix"),
        )
        .arg(Arg::from_usage(
            "-u,--from-url [url] 'Point to a github repo, and use github api to determine package values'",
            ))
        .arg(Arg::from_usage(
            "-l,--license [license] 'Set license'",
            ).default_value("CHANGE"))
        .arg(Arg::from_usage(
            "-m,--maintainer [maintainer] 'Set maintainer'",
            ))
        .arg(Arg::from_usage(
            "--no-meta 'Don't include meta section'",
            ).conflicts_with("nixpkgs"))
        .arg(Arg::from_usage(
            "-d,--documentation-links 'Add comments linking to relevant sections of the Nixpkgs contributor guide.'",
            ).takes_value(false))
        .arg(Arg::from_usage(
            "-s,--stdout 'Write expression to stdout, instead of PATH'",
            ))
        .arg(Arg::from_usage(
            "-v [version] 'Set version of package'",
            ).default_value("0.0.1"))
        .arg(Arg::from_usage(
            "-p,--pname [pname] 'Package name to be used in expression'",
            ).default_value("CHANGE"))
        .arg(Arg::from_usage(
            "-r,--nixpkgs-root [path] 'Set root of the nixpkgs directory'",
            ).env("NIXPKGS_ROOT"))
        .arg(Arg::from_usage(
            "-n,--nixpkgs 'Intended be used within nixpkgs, will append pname to file path, and print addition statement'",
        ).takes_value(false))
        .arg(
            Arg::from_usage("-f,--fetcher [fetcher] 'Fetcher to use'")
                .possible_values(&Fetcher::variants())
                .case_insensitive(true)
                .default_value("github")
                .default_value_if("TEMPLATE", Some("python"), "pypi"),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Generate shell completion scripts, writes to stdout")
                .arg(
                    Arg::from_usage("<SHELL>")
                        .case_insensitive(true)
                        .possible_values(&clap::Shell::variants()),
                ),
        )
        .subcommand(
            SubCommand::with_name("config")
                .about("Set information about nix-template usage. Writes to $XDG_CONFIG_HOME")
                .arg(
                    Arg::from_usage("-f,--file [config-file] 'Config file location. [default: $XDG_CONFIG_HOME/nix-template/config.toml]'")
                )
                .subcommand(
                    SubCommand::with_name("name")
                    .about("Set maintainer name")
                    .arg(Arg::from_usage("<name>"))
                )
                .subcommand(
                    SubCommand::with_name("nixpkgs-root")
                    .about("Set the root directory of nixpkgs")
                    .arg(Arg::from_usage("<nixpkgs-root>"))
                )
        )
}

pub fn validate_and_serialize_matches(
    matches: &ArgMatches,
    user_config: Option<&UserConfig>,
) -> ExpressionInfo {
    let template: Template = arg_to_type(matches.value_of("TEMPLATE"));
    let fetcher: Fetcher = arg_to_type(matches.value_of("fetcher"));
    let pname: String = arg_to_type(matches.value_of("pname"));
    let version: String = arg_to_type(matches.value_of("v"));
    let license: String = arg_to_type(matches.value_of("license"));
    let path_str: String = arg_to_type(matches.value_of("PATH"));
    let path = std::path::PathBuf::from(&path_str);
    let include_documentation_links: bool = matches.is_present("documentation-links");
    let include_meta: bool = !matches.is_present("no-meta");

    assert(!(matches.is_present("nixpkgs") && matches.value_of("pname") == Some("CHANGE") && matches.value_of("from-url") == None),
        "'-p,--pname' or '-u,--from-url' is required when using the -n,--nixpkgs flag");

    let maintainer: String;
    let nixpkgs_root: String;
    if let Some(ref config) = user_config {
        maintainer = matches
            .value_of("maintainer")
            .or_else(|| config.maintainer.as_deref())
            .unwrap_or("")
            .to_owned();
        nixpkgs_root = matches
            .value_of("nixpkgs-root")
            .or_else(|| config.nixpkgs_root.as_deref())
            .unwrap_or("")
            .to_owned();
    } else {
        maintainer = matches.value_of("maintainer").unwrap_or("").to_string();
        nixpkgs_root = matches.value_of("nixpkgs-root").unwrap_or("").to_string();
    };

    if template == Template::flake {
        assert(matches.occurrences_of("pname") != 0, "Must provide value for -p,--pname when using flake template.");
    }

    let mut info = ExpressionInfo {
        pname,
        version,
        license,
        maintainer,
        template,
        fetcher,
        path_to_write: std::path::PathBuf::new(),
        top_level_path: std::path::PathBuf::new(),
        include_documentation_links,
        include_meta,
        tag_prefix: "".to_owned(),
        owner: "CHANGE".to_owned(),
        src_sha: "0000000000000000000000000000000000000000000000000000".to_owned(),
        description: "CHANGE".to_owned(),
        homepage: "https://github.com/@owner@/@pname@".to_owned(),
        propagated_build_inputs: Vec::new(),
    };

    if let Some(url) = matches.value_of("from-url") {
        read_meta_from_url(url, &mut info);
    }

    let (path_to_write, top_level_path) =
        nix_file_paths(&matches, &info.template, &path, &info.pname, &nixpkgs_root);

    info.path_to_write = path_to_write.clone();
    info.top_level_path = top_level_path.clone();

    assert(matches.is_present("stdout") || (!path_to_write.exists()),
        &format!("Cannot write to file '{}', already exists", path_to_write.display()));

    info
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq;
    use serial_test::serial;

    #[test]
    fn test_python() {
        let m = build_cli().get_matches_from(vec![
            "nix-template",
            "python",
            "-r",
            "/tmp",
            "-n",
            "-p",
            "requests",
        ]);
        println!("{:?}", m);
        assert_eq!(m.value_of("pname"), Some("requests"));
        assert_eq!(m.value_of("TEMPLATE"), Some("python"));
        assert_eq!(m.value_of("fetcher"), Some("pypi"));
        assert_eq!(m.value_of("v"), Some("0.0.1"));
        assert_eq!(m.value_of("pname"), Some("requests"));
        assert_eq!(m.value_of("license"), Some("CHANGE"));
        assert_eq!(m.value_of("nixpkgs-root"), Some("/tmp"));
        assert_eq!(m.is_present("stdout"), false);
        assert_eq!(m.occurrences_of("PATH"), 0);
        assert_eq!(m.is_present("nixpkgs"), true);
        assert!(m.occurrences_of("nixpkgs") >= 1);
        assert_eq!(m.occurrences_of("from-url"), 0);
    }

    #[test]
    fn test_url() {
        let m = build_cli().get_matches_from(vec!["nix-template", "python", "-u", "https://pypi.org/project/requests/", "-n"]);
        assert_eq!(m.is_present("stdout"), false);
        assert_eq!(m.is_present("nixpkgs"), true);
        assert_eq!(m.occurrences_of("from-url"), 1);
    }

    #[test]
    fn test_mkshell() {
        let m = build_cli().get_matches_from(vec!["nix-template", "-s", "mkshell"]);
        assert_eq!(m.is_present("stdout"), true);
        assert_eq!(m.value_of("TEMPLATE"), Some("mkshell"));
        assert_eq!(m.value_of("PATH"), Some("shell.nix"));
        assert_eq!(m.value_of("pname"), Some("CHANGE"));
        assert_eq!(m.is_present("nixpkgs"), false);
        assert_eq!(m.occurrences_of("from-url"), 0);
    }

    #[test]
    fn test_test() {
        let m = build_cli().get_matches_from(vec!["nix-template", "test", "-m", "myself"]);
        assert_eq!(m.value_of("TEMPLATE"), Some("test"));
        assert_eq!(m.value_of("PATH"), Some("test.nix"));
        assert_eq!(m.value_of("maintainer"), Some("myself"));
    }

    #[test]
    fn test_fetcher() {
        let m = build_cli().get_matches_from(vec![
            "nix-template",
            "-f",
            "gitlab",
            "-l",
            "mit",
            "stdenv",
            "default.nix",
        ]);
        assert_eq!(m.value_of("license"), Some("mit"));
        assert_eq!(m.value_of("PATH"), Some("default.nix"));
        assert_eq!(m.occurrences_of("PATH"), 1);
        assert_eq!(m.value_of("fetcher"), Some("gitlab"));
    }

    #[test]
    #[serial] // touching global env, ensure serial runs
    fn test_nixpkgs() {
        use std::env::{remove_var, set_var};
        set_var("NIXPKGS_ROOT", "/testdir/");
        let m = build_cli().get_matches_from(vec!["nix-template", "-n"]);
        assert_eq!(m.value_of("nixpkgs-root"), Some("/testdir/"));
        remove_var("NIXPKGS_ROOT");
    }
}


text:
use crate::types::{ExpressionInfo, Fetcher, Template};

fn derivation_helper(template: &Template) -> (String, String) {
    let (input, derivation, documentation_key): (&str, &str, Option<&str>) = match template {
        Template::stdenv => ("stdenv", "stdenv.mkDerivation", Some("stdenvMkDerivation")),
        Template::python => ("buildPythonPackage", "buildPythonPackage", None),
        Template::mkshell => ("pkgs ? import <nixpkgs> {}", "with pkgs;\n\nmkShell", None),
        Template::qt => ("mkDerivation", "mkDerivation", None),
        Template::go => ("buildGoModule", "buildGoModule", None),
        Template::rust => ("rustPlatform", "rustPlatform.buildRustPackage", None),
        Template::flake => ("", "", None), // flakes aren't a normal expression
        Template::test => ("", "", None),  // Tests aren't a normal expression
        Template::module => ("", "", None), // Tests aren't a normal expression
    };

    match documentation_key {
        Some(key) => (String::from(input), format!("@doc:{}@{}", key, derivation)),
        None => (String::from(input), String::from(derivation)),
    }
}

fn fetch_block(fetcher: &Fetcher) -> (&'static str, &'static str) {
    match fetcher {
        Fetcher::github => (
            "fetchFromGitHub",
            "  @doc:fetcher@src = fetchFromGitHub {
    owner = \"@owner@\";
    repo = pname;
    rev = @rev@;
    sha256 = \"@src_sha@\";
  };",
        ),
        Fetcher::gitlab => (
            "fetchFromGitLab",
            "  @doc:fetcher@src = fetchFromGitLab {
    owner = \"CHANGE\";
    repo = pname;
    rev = \"CHANGE\";
    sha256 = \"0000000000000000000000000000000000000000000000000000\";
  };",
        ),
        Fetcher::url => (
            "fetchurl",
            "  @doc:fetcher@src = fetchurl {
    url = \"CHANGE\";
    sha256 = \"0000000000000000000000000000000000000000000000000000\";
  };",
        ),
        Fetcher::zip => (
            "fetchzip",
            "  @doc:fetcher@src = fetchzip {
    url = \"CHANGE\";
    sha256 = \"0000000000000000000000000000000000000000000000000000\";
  };",
        ),
        Fetcher::pypi => (
            "fetchPypi",
            "  @doc:fetcher@src = fetchPypi {
    inherit pname version;
    sha256 = \"@src_sha@\";
  };",
        ),
    }
}

fn addtional_pkg_attr_headers(template: &Template) -> &'static str {
    match template {
        Template::python => "\n  format = \"setuptools\";",
        _ => "",
    }
}

fn build_inputs(template: &Template) -> &'static str {
    match template {
        Template::python => "  @doc:buildDependencies@propagatedBuildInputs = [@propagated_build_inputs@ ];

  pythonImportsCheck = [ \"@pname-import-check@\" ];",
        Template::rust => "  @doc:buildDependencies@cargoSha256 = \"0000000000000000000000000000000000000000000000000000\";

  buildInputs = [ ];",
        Template::go => "  @doc:buildDependencies@vendorSha256 = \"0000000000000000000000000000000000000000000000000000\";

  subPackages = [ \".\" ];",
        _ => "  buildInputs = [ ];",
    }
}

fn meta() -> &'static str {
    "
  @doc:meta@meta = with lib; {
    description = \"@description@\";
    homepage = \"@homepage@\";
    license = licenses.@license@;
    maintainers = with maintainers; [ @maintainer@ ];
  };"
}

pub fn generate_expression(info: &ExpressionInfo) -> String {
    match &info.template {
        Template::module   => r#"{ pkgs, lib, config, ... }:

with lib;

let
  cfg = config.services.@pname@;
in {
  options.services.@pname@ = {
    enable = mkEnableOption "CHANGE";

    package = mkOption {
      type = types.package;
      default = pkgs.@pname@;
      defaultText = "pkgs.@pname@";
      description = "Set version of @pname@ package to use.";
    };
  };

  config = mkIf cfg.enable {
    environment.systemPackages = [ cfg.package ]; # if user should have the command available as well
    services.dbus.packages = [ cfg.package ]; # if the package has dbus related configuration

    systemd.services.@pname@ = {
      description = "@pname@ server daemon.";

      wantedBy = [ "multi-user.target" ];
      after = [ "network.target" ]; # if networking is needed

      restartIfChanged = true; # set to false, if restarting is problematic

      serviceConfig = {
        DynamicUser = true;
        ExecStart = "${cfg.package}/bin/@pname@";
        Restart = "always";
      };
    };
  };

  meta.maintainers = with lib.maintainers; [ @maintainer@ ];
}"#.to_owned(),
        Template::flake   => r#"{
  description = "@pname@ flake";

  inputs = {
    utils.url = "github:numtide/flake-utils";
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
  };

  outputs = { self, nixpkgs, utils, ... }:
    let
      # put devShell and any other required packages into local overlay
      localOverlay = import ./nix/overlay.nix;

      # if you have additional overlays from other flakes, you may add them here
      allOverlays = [
        localOverlay # this should expose devShell
      ];

      pkgsForSystem = system: import nixpkgs {
        overlays = allOverlays;
        inherit system;
      };
    # https://github.com/numtide/flake-utils#usage for more examples
    in utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin" ] (system: rec {
      legacyPackages = pkgsForSystem system;
      packages = utils.lib.flattenTree {
        inherit (legacyPackages) devShell @pname@;
      };
      defaultPackage = packages.@pname@;
      apps.@pname@ = utils.lib.mkApp { drv = packages.@pname@; };  # use as `nix run .#@pname@`
      hydraJobs = { inherit (legacyPackages) @pname@; };
      checks = { inherit (legacyPackages) @pname@; };              # items to be ran as part of `nix flake check`
  }) // {
    # non-system suffixed items should go here
    overlays = {
      default = localOverlay;
    };
    nixosModule = { config, ... }: { options = {}; config = {};}; # export single module
    nixosModules = {}; # attr set or list
    nixosConfigurations.hostname = { config, pkgs, ... }: {};
  };
}"#.to_string(),
        Template::test => r#"import ./make-test-python.nix ({ pkgs, ... }:
{
  name = "@pname@";
  meta = with pkgs.lib.maintainers; {
    maintainers = [ @maintainer@ ];
  };
  machine = { pkgs, ... }: {
    environment.systemPackages = [ @pname@ ];
    services.@pname@.enable = true;
    virtualisation.memorySize = 512;
  };

  testScript =
    ''
      start_all()

      machine.wait_for_unit("multi-user.target")
      machine.wait_for_unit("@pname@.service")
      machine.wait_for_open_port(8080)
      machine.succeed("CMD")
    '';
})"#.to_string(),
        Template::mkshell => "with import <nixpkgs> { };

mkShell rec {
  # include any libraries or programs in buildInputs
  buildInputs = [
  ];

  # shell commands to be ran upon entering shell
  shellHook = ''
  '';
}
"
        .to_string(),
        _ => {
            // Generate nix expression
            let (dh_input, dh_block) = derivation_helper(&info.template);
            let (f_input, f_block) = fetch_block(&info.fetcher);
            let addtional_pkg_attr_headers = addtional_pkg_attr_headers(&info.template);

            let mut inputs = vec!(String::from("lib"), dh_input, f_input.to_string());
            inputs.extend(info.propagated_build_inputs.iter().map(|s| s.to_owned()));

            let header = format!("{{ {input_list}\n}}:", input_list = inputs.join("\n, "));

            info.format(&format!(
                "{header}

{dh_helper} rec {{
  pname = \"{pname}\";
  version = \"{version}\";{addtional_pkg_attr_headers}

{f_block}

{build_inputs}
{meta}
}}
",
                header = header,
                dh_helper = dh_block,
                pname = &info.pname,
                version = &info.version,
                addtional_pkg_attr_headers = addtional_pkg_attr_headers,
                f_block = f_block,
                build_inputs = build_inputs(&info.template),
                meta = if info.include_meta { meta() } else { "" },
            ))
        }
    }
}


text:
use crate::types::Template;
use std::path::{Path, PathBuf};
use std::process::exit;

/// Determine where the file path should be written
/// Meant to save people time in dealing with file paths
/// returns (file_path_to_write, file_path_in_top_level)
/// file_path_to_write: filepath to write to disk
/// file_path_in_top_level: filepath to mention in top-level/*.nix
pub fn nix_file_paths(
    matches: &clap::ArgMatches,
    template: &Template,
    path: &Path,
    pname: &str,
    nixpkgs_root: &str,
) -> (PathBuf, PathBuf) {
    if matches.is_present("nixpkgs") {

        let mut radix: PathBuf;
        if matches.occurrences_of("PATH") == 0 {
            // default to nixpkgs path
            if *template == Template::python {
                eprintln!("No [PATH] provided, defaulting to \"pkgs/development/python-modules/\"");
                radix = PathBuf::from("development/python-modules/");
            } else if *template == Template::test {
                eprintln!("No [PATH] provided, defaulting to \"nixos/tests/\"");
                radix = PathBuf::from("nixos/tests/");
                radix.push(format!("{}.nix", &pname));
                return (radix, PathBuf::from(format!("./{}.nix", &pname)));
            } else if *template == Template::module {
                eprintln!("A path is required when using the module template.");
                eprintln!("For example, 'nixos/modules/services/{}.nix", &pname);
                exit(1);
            } else {
                eprintln!("No [PATH] provided, defaulting to \"pkgs/applications/misc/\"");
                radix = PathBuf::from("applications/misc");
            }
        } else {
            radix = path.strip_prefix("pkgs").unwrap_or(path).to_path_buf();
        }

        // modules just need the file location
        if *template == Template::module {
            radix = path.strip_prefix(".").unwrap_or(path).to_path_buf();
            radix = radix.strip_prefix("/").unwrap_or(&radix).to_path_buf();
            radix = radix
                .strip_prefix("nixos/modules")
                .unwrap_or(&radix)
                .to_path_buf();

            let mut module = PathBuf::from("./");
            module.push(&radix);

            let mut file_path = PathBuf::from(&nixpkgs_root);
            file_path.push("nixos");
            file_path.push("modules");
            file_path.push(&radix);

            return (file_path, module);
        }

        // assume that they want a directory for the individual package
        if !radix.ends_with(&pname) && radix.extension() != Some(std::ffi::OsStr::new("nix")) {
            radix.push(&pname);
        }

        // nix_path is the path used in pkgs/top-level/*.nix or nixos/tests/all-tests.nix
        let mut nix_path = PathBuf::from("..");
        nix_path.push(&radix);

        // file path is the path to the nix expression from NIXPKGS_ROOT
        let mut file_path = PathBuf::from(&nixpkgs_root);
        file_path.push("pkgs");
        file_path.push(&radix);

        // may have specified a specific nix file (E.g path/package.nix)
        if file_path.is_dir() || file_path.extension() != Some(std::ffi::OsStr::new("nix")) {
            file_path = file_path.join("default.nix");
        }

        return (file_path, nix_path);
    }

    let mut path_buf = path.to_path_buf();

    // if it's a directory, we need to default to using the default.nix which `import` expects
    // Path.is_dir() appears to return false if the directory doesn't exist, so stringify and assert if path ends in '/'
    if path.to_str().unwrap().ends_with("/") {
      path_buf.push("default.nix");
      eprintln!("Directory was passed as [PATH], defaulting to {:?}", path_buf.display());
    }

    (path_buf, PathBuf::from(""))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::cli::{build_cli, validate_and_serialize_matches};
    use clap::ArgMatches;
    use pretty_assertions::assert_eq;
    use serial_test::serial;

    fn assert_paths(m: ArgMatches, expected: (PathBuf, PathBuf)) {
        let info = validate_and_serialize_matches(&m, None);
        let actual = (info.path_to_write, info.top_level_path);

        assert_eq!(expected, actual);
    }

    #[test]
    #[serial]
    fn test_module() {
        let m = build_cli().get_matches_from(vec![
            "nix-template",
            "module",
            "-n",
            "-p",
            "my-package",
            "nixos/modules/services/my-package.nix",
        ]);
        let expected = (
            PathBuf::from("nixos/modules/services/my-package.nix"),
            PathBuf::from("./services/my-package.nix"),
        );
        assert_paths(m, expected);
    }

    #[test]
    #[serial]
    fn test_module_long_path() {
        let m = build_cli().get_matches_from(vec![
            "nix-template",
            "module",
            "-n",
            "-p",
            "my-package",
            "nixos/modules/services/some-pkg-set/my-package.nix",
        ]);
        let expected = (
            PathBuf::from("nixos/modules/services/some-pkg-set/my-package.nix"),
            PathBuf::from("./services/some-pkg-set/my-package.nix"),
        );
        assert_paths(m, expected);
    }

    #[test]
    #[serial]
    fn test_test() {
        let m = build_cli().get_matches_from(vec!["nix-template", "test", "-n", "-p", "newpkg"]);
        let expected = (
            PathBuf::from("nixos/tests/newpkg.nix"),
            PathBuf::from("./newpkg.nix"),
        );
        assert_paths(m, expected);
    }

    #[test]
    #[serial]
    fn test_dir_to_default_nix() {
        let m =
            build_cli().get_matches_from(vec!["nix-template", "python", "-p", "requests", "local/"]);
        let expected = (
            PathBuf::from("local/default.nix"),
            PathBuf::from(""),
        );
        assert_paths(m, expected);
    }

    #[test]
    #[serial]
    fn test_python() {
        let m =
            build_cli().get_matches_from(vec!["nix-template", "python", "-n", "-p", "requests"]);
        let expected = (
            PathBuf::from("pkgs/development/python-modules/requests/default.nix"),
            PathBuf::from("../development/python-modules/requests"),
        );
        assert_paths(m, expected);
    }

    #[test]
    #[serial]
    fn test_stdenv_no_path() {
        let m =
            build_cli().get_matches_from(vec!["nix-template", "stdenv", "-n", "-p", "mypackage"]);
        let expected = (
            PathBuf::from("pkgs/applications/misc/mypackage/default.nix"),
            PathBuf::from("../applications/misc/mypackage"),
        );
        assert_paths(m, expected);
    }

    #[test]
    #[serial]
    fn test_stdenv_path() {
        let m = build_cli().get_matches_from(vec![
            "nix-template",
            "stdenv",
            "-n",
            "-p",
            "mypackage",
            "pkgs/compilers/test/",
        ]);
        let expected = (
            PathBuf::from("pkgs/compilers/test/mypackage/default.nix"),
            PathBuf::from("../compilers/test/mypackage"),
        );
        assert_paths(m, expected);
    }
}


text:
#[macro_use]
extern crate lazy_static;

mod cli;
mod expression;
mod file_path;
mod types;
mod url;

use cli::arg_to_type;
use types::{Template, UserConfig};

fn main() {
    env_logger::init();

    let xdg_dirs = xdg::BaseDirectories::with_prefix("nix-template").unwrap();

    let mut user_config: Option<UserConfig> =
        if let Some(file) = xdg_dirs.find_config_file("config.toml") {
            toml::from_str(&std::fs::read_to_string(file).unwrap()).ok()
        } else {
            None
        };

    let m = cli::build_cli().get_matches();

    match m.subcommand() {
        ("completions", Some(m)) => {
            // clap would have failed if a valid shell str wasn't passed
            cli::build_cli().gen_completions_to(
                "nix-template",
                arg_to_type::<clap::Shell>(m.value_of("SHELL")),
                &mut std::io::stdout(),
            )
        }
        ("config", Some(m)) => {
            let config_path = xdg_dirs
                .place_config_file("config.toml")
                .unwrap_or_else(|_| panic!("unable to create configuration directory"));

            // set config
            match m.subcommand() {
                ("name", Some(m)) => {
                    //set name
                    let name: Option<String> = m.value_of("name").map(|s| s.to_string());

                    // since we can only set 1 value currently, this is a bit of over-engineered
                    // however, we want to prevent overriding future values
                    if let Some(ref mut config) = user_config {
                        config.maintainer = name;
                    } else {
                        user_config = Some(UserConfig {
                            maintainer: name,
                            nixpkgs_root: None,
                        })
                    };
                }
                ("nixpkgs-root", Some(m)) => {
                    //set nixpkgs root
                    let root: Option<String> = m.value_of("nixpkgs-root").map(|s| s.to_string());

                    // since we can only set 1 value currently, this is a bit of over-engineered
                    // however, we want to prevent overriding future values
                    if let Some(ref mut config) = user_config {
                        config.nixpkgs_root = root;
                    } else {
                        user_config = Some(UserConfig {
                            maintainer: None,
                            nixpkgs_root: root,
                        })
                    };
                }
                _ => {
                    eprintln!("Unexpected command given to config subcommand.");
                    std::process::exit(1);
                }
            }

            // write config
            std::fs::write(&config_path, toml::to_string(&user_config).unwrap()).unwrap_or_else(
                |_| panic!("Was unable to write to file: {}", &config_path.display()),
            );
        }
        _ => {
            // build expression
            let user_config: Option<UserConfig> =
                if let Some(file) = xdg_dirs.find_config_file("config.toml") {
                    toml::from_str(&std::fs::read_to_string(file).unwrap()).ok()
                } else {
                    None
                };

            let info = cli::validate_and_serialize_matches(&m, user_config.as_ref());

            let expr = expression::generate_expression(&info);
            let output = info.format(&expr);

            if m.is_present("stdout") {
                println!("{}", output);
            } else {
                let path = &info.path_to_write;

                // ensure directory to file exists
                if let Some(p) = path.parent() {
                    // TODO: better way to determine that file will be written PWD
                    if p.to_str() != Some("") && !p.exists() {
                        println!("Creating directory: {}", p.display());
                        std::fs::create_dir_all(p).unwrap_or_else(|_| {
                            panic!("Was unable to create directory {}", p.display())
                        });
                    }
                }
                // write file
                std::fs::write(path, output)
                    .unwrap_or_else(|_| panic!("Was unable to write to file: {}", &path.display()));
                println!(
                    "Generated a {} nix expression at {}",
                    &info.template,
                    &path.canonicalize().unwrap().display()
                );

                // print helpful message about line to be included in pkgs/top-level
                if m.is_present("nixpkgs") {
                    println!("Please add the following line to the appropriate file:");
                    println!();
                    match &info.template {
                        Template::module => println!("  {}", &info.top_level_path.display()),
                        Template::test => println!(
                            "  {} = handleTest {} {{ }};",
                            &info.pname,
                            &info.top_level_path.display()
                        ),
                        _ => println!(
                            "  {} = callPackage {} {{ }};",
                            &info.pname,
                            &info.top_level_path.display()
                        ),
                    }
                    println!();
                }
            }
        }
    }
}


text:
use crate::types;
use crate::types::Repo::{Github, Pypi};

use anyhow::anyhow;
use anyhow::Result;
use log::{debug, error};
use regex::Regex;
use reqwest::blocking::Client;
use std::collections::HashMap;
use std::process::exit;
use std::process::Command;
use version_compare::VersionCompare;

lazy_static! {
    static ref GITHUB_URL_REGEX: Regex = {
        // e.g. github.com/jonringer/nix-template
        Regex::new("github.com/([^/]*)/([^/]*)/?").unwrap()
    };

    static ref PYPI_URL_REGEX: Regex = {
        // e.g. pypi.org/project/requests
        Regex::new("pypi.org/project/([^/]*)/?").unwrap()
    };

    static ref VERSION_REGEX: Regex = {
        Regex::new("^([^0-9]*)(.+)").unwrap()
    };

    static ref STABLE_RELEASE_REGEX: Regex = {
        Regex::new(r"^([0-9.]*)+$").unwrap()
    };

    static ref GITHUB_TO_NIXPKGS_LICENSE: HashMap<&'static str, &'static str> = {
        let mut m = HashMap::new();
        m.insert("agpl-3.0", "agpl3");
        m.insert("apache-2.0", "asl20");
        m.insert("bsd-2-clause", "bsd2");
        m.insert("bsd-3-clause", "bsd3");
        m.insert("bsl-1.0", "bsl11");
        m.insert("cc0-1.0", "cc0");
        m.insert("epl-2.0", "epl20");
        m.insert("gpl-2.0", "gpl2");
        m.insert("gpl-3.0", "gpl3");
        m.insert("lgpl-2.1", "lgpl21");
        m.insert("mit", "mit");
        m.insert("mpl-2.0", "mpl20");
        m.insert("unlicense", "unlicense");
        m
    };

    static ref PYPI_TO_NIXPKGS_LICENSE: HashMap<&'static str, &'static str> = {
        let mut m = HashMap::new();
        // TODO: add more licenses
        m.insert("Apache 2.0", "asl20");
        m.insert("Apache Software License", "asl20");
        m.insert("BSD-3-clause", "bsd3");
        m.insert("MIT License", "mit");
        m
    };

}

const LOG_TARGET: &str = "nix-template::url";

fn to_sri(hash: &str) -> String {
   let sha256_cmd = Command::new("nix")
     .args(&["hash", "to-sri", "--type", "sha256", "--experimental-features", "nix-command"])
     .arg(hash)
     .output();
   std::str::from_utf8(&sha256_cmd.expect("failed to run 'nix hash'").stdout)
     .unwrap()
     .trim()
     .to_owned()
}

// This will just crazy the program, so no need to return a value
fn validate_and_parse_url(url: &str, original_url: &str) -> Result<types::Repo> {
    if url.starts_with("github.com") {
        if !GITHUB_URL_REGEX.is_match(url) {
            return Err(anyhow!(
                "Error: please provide a github url of shape 'github.com/<owner>/<repo>'"
            ));
        }

        let captures = GITHUB_URL_REGEX.captures(url).unwrap();

        return Ok(Github(types::GithubRepo {
            owner: captures.get(1).unwrap().as_str().to_owned(),
            repo: captures.get(2).unwrap().as_str().to_owned(),
        }));
    } else if url.starts_with("pypi.org") {
        if !PYPI_URL_REGEX.is_match(url) {
            return Err(anyhow!(
                "Error: please provide a pypi url of shape 'pypi.org/pypi/<repo>'"
            ));
        }

        let captures = PYPI_URL_REGEX.captures(url).unwrap();

        return Ok(Pypi(types::PypiRepo {
            project: captures.get(1).unwrap().as_str().to_owned(),
        }));
    } else {
        Err(anyhow!(
            "{} is not a supported url. Only github.com and pypi.org are supported currently",
            original_url
        ))
    }
}

fn get_json(request: reqwest::blocking::RequestBuilder) -> Result<String, reqwest::Error> {
    let response: reqwest::blocking::Response = request.send()?;
    response.error_for_status_ref()?;
    response.text()
}

pub fn fetch_pypi_project_info(pypi_repo: &types::PypiRepo) -> types::PypiResponse {
    let request_client = Client::new();
    let request = request_client
        .get(format!("https://pypi.io/pypi/{}/json", pypi_repo.project))
        .header("User-Agent", "reqwest")
        .header("Content", "application/json");

    let body = get_json(request).expect("Unable to get remote data.");
    let jd = &mut serde_json::Deserializer::from_str(&body);
    match serde_path_to_error::deserialize(jd) {
        Ok(s) => s,
        Err(e) => {
            error!(
                target: LOG_TARGET,
                "Unable to parse response from pypi.io to json: {:?}", e
            );
            exit(1)
        }
    }
}

pub fn fetch_github_repo_info(repo: &types::GithubRepo) -> types::GhRepoResponse {
    let request_client = Client::new();
    let mut request = request_client
        .get(format!(
            "https://api.github.com/repos/{}/{}",
            repo.owner, repo.repo
        ))
        .header("User-Agent", "reqwest")
        .header("Accept", "application/vnd.github.v3+json");

    if let Ok(github_token) = std::env::var("GITHUB_TOKEN") {
        debug!(target: LOG_TARGET, "Using github token for github api request");
        request = request.header("Authorization", format!("token {}", github_token));
    }

    let body = get_json(request).expect("Unable to get remote data.");
    let jd = &mut serde_json::Deserializer::from_str(&body);
    match serde_path_to_error::deserialize(jd) {
        Ok(s) => s,
        Err(e) => {
            error!(
                target: LOG_TARGET,
                "Unable to parse response from github to json: {:?}", e
            );
            exit(1)
        }
    }
}

pub fn fetch_github_release_info(repo: &types::GithubRepo) -> types::GhReleaseResponse {
    let request_client = Client::new();
    let mut request = request_client
        .get(format!(
            "https://api.github.com/repos/{}/{}/releases",
            repo.owner, repo.repo
        ))
        .header("User-Agent", "reqwest")
        .header("Accept", "application/vnd.github.v3+json");

    if let Ok(github_token) = std::env::var("GITHUB_TOKEN") {
        debug!(target: LOG_TARGET, "Using github token for polling events");
        request = request.header("Authorization", format!("token {}", github_token));
    }

    let body = get_json(request).expect("Unable to get remote data.");
    let jd = &mut serde_json::Deserializer::from_str(&body);
    match serde_path_to_error::deserialize(jd) {
        Ok(s) => s,
        Err(e) => {
            error!(
                target: LOG_TARGET,
                "Unable to parse response from github to json: {:?}", e
            );
            exit(1)
        }
    }
}

pub fn fill_github_info(repo: &types::GithubRepo, info: &mut types::ExpressionInfo) {
    if info.pname == "CHANGE" {
        info.pname = repo.repo.to_string();
    }

    eprintln!("Determining latest release for {}", &repo.repo);
    let mut releases = fetch_github_release_info(&repo);
    if !releases.is_empty() {
        releases.sort_by(|a, b| {
            VersionCompare::compare(&b.tag_name, &a.tag_name)
                .unwrap()
                .ord()
                .unwrap()
        });
        releases = releases.into_iter().filter(|a| !a.prerelease).collect();
        let parsed_version = VERSION_REGEX
            .captures(&releases.first().unwrap().tag_name)
            .unwrap();
        info.version = parsed_version.get(2).unwrap().as_str().to_owned();
        info.tag_prefix = parsed_version.get(1).unwrap().as_str().to_owned();

        eprintln!("Determining sha256 for {}", &repo.repo);
        let sha256_cmd = Command::new("nix-prefetch-url")
            .args(&["--unpack", "--type", "sha256"])
            .arg(format!(
                "https://github.com/{}/{}/archive/refs/tags/{}{}.tar.gz",
                &repo.owner, &repo.repo, &info.tag_prefix, &info.version
            ))
            .output();
        let output = std::str::from_utf8(&sha256_cmd.unwrap().stdout)
            .unwrap()
            .trim()
            .to_owned();
        info.src_sha = to_sri(&output);
    } else {
        eprintln!(
            "No releases found for github.com/{}/{}",
            &repo.owner, &repo.repo
        );
    }

    let repo_info = fetch_github_repo_info(&repo);
    if repo_info.license.key != "other" {
        info.license = GITHUB_TO_NIXPKGS_LICENSE
            .get(&*repo_info.license.key)
            .unwrap_or(&"CHANGE")
            .to_string();
    }
    info.description = repo_info.description.unwrap_or("CHANGE".to_owned());

    if info.pname == "CHANGE" {
        info.pname = repo.repo.clone();
    }
    if info.owner == "CHANGE" {
        info.owner = repo.owner.clone();
    }
}

pub fn fill_pypi_info(pypi_repo: &types::PypiRepo, info: &mut types::ExpressionInfo) {

    eprintln!("Determining latest release for {}", &pypi_repo.project);
    let pypi_response = fetch_pypi_project_info(pypi_repo);
    if info.pname == "CHANGE" {
        info.pname = pypi_repo.project.clone();
    }

    let mut releases: Vec<String> = pypi_response
        .releases
        .keys()
        .map(|a| a.to_owned())
        .filter(|v| STABLE_RELEASE_REGEX.is_match(v))
        .collect();

    if !releases.is_empty() {
        releases.sort_by(|a, b| VersionCompare::compare(&b, &a).unwrap().ord().unwrap());

        let latest_version = releases.first().unwrap();

        let latest_release = pypi_response
            .releases
            .get(latest_version)
            .unwrap()
            .iter()
            .filter(|a| a.packagetype == "sdist")
            .next();

        info.version = latest_version.clone();
        info.homepage = pypi_response.info.home_page.clone();
        info.description = pypi_response.info.summary.trim_end_matches(".").to_string();

        info.license = PYPI_TO_NIXPKGS_LICENSE
            .get(&*pypi_response.info.license)
            .unwrap_or(&"CHANGE")
            .to_string();

        // Grab dependencies, filter out extras, normalize names
        let mut dependencies: Vec<String> = pypi_response.info.requires_dist
            .unwrap_or_else(|| Vec::new())
            .into_iter()
            .filter(|s| !s.contains("extra =="))
            .map(|s| s.split(" ").next().unwrap().to_string().replace(".", "-").replace("_", "-"))
            .collect();
        dependencies.sort();
        info.propagated_build_inputs = dependencies;

        match latest_release {
            Some(dist) => {
                info.fetcher = types::Fetcher::pypi;
                info.src_sha = to_sri(&dist.digests.sha256);
            }
            None => {
                eprintln!(
                    "Unable to find sdist for {}. Using default template",
                    &pypi_repo.project
                );
            }
        }
    } else {
        eprintln!(
            "No releases found for pypi.org/project/{}",
            &pypi_repo.project
        );
    }
}

pub fn read_meta_from_url(url: &str, info: &mut types::ExpressionInfo) {
    let trimmed_url = url
        .trim_start_matches("http://")
        .trim_start_matches("https://");

    match validate_and_parse_url(trimmed_url, url) {
        Ok(Github(repo)) => {
            fill_github_info(&repo, info);
        }
        Ok(Pypi(pypi_repo)) => {
            fill_pypi_info(&pypi_repo, info);
        }
        Err(e) => {
            eprintln!("{}", e);
            exit(1);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use pretty_assertions::assert_eq;
    use crate::types::Repo::Github;
    use crate::types::GithubRepo;

    #[test]
    fn test_url_parse() {
        let repo = validate_and_parse_url(
            "github.com/jonringer/nix-template",
            "github.com/jonringer/nix-template",
        )
        .unwrap();

        assert_eq!(repo, Github(GithubRepo {
            owner: "jonringer".to_string(),
            repo: "nix-template".to_string()
        }));
    }

    #[test]
    fn test_version_regex() {
        let captures = VERSION_REGEX.captures("v0.1.0").unwrap();
        assert_eq!(captures.len(), 3);
        assert_eq!(captures.get(1).unwrap().as_str(), "v");
        assert_eq!(captures.get(2).unwrap().as_str(), "0.1.0");

        let captures = VERSION_REGEX.captures("azure-cli-21.1.3").unwrap();
        assert_eq!(captures.len(), 3);
        assert_eq!(captures.get(1).unwrap().as_str(), "azure-cli-");
        assert_eq!(captures.get(2).unwrap().as_str(), "21.1.3");

        let captures = VERSION_REGEX.captures("zfs-2.1.0-rc7").unwrap();
        assert_eq!(captures.len(), 3);
        assert_eq!(captures.get(1).unwrap().as_str(), "zfs-");
        assert_eq!(captures.get(2).unwrap().as_str(), "2.1.0-rc7");
    }

    #[test]
    fn test_simple_version_regex() {
        let captures = VERSION_REGEX.captures("2.1.1").unwrap();
        assert_eq!(captures.len(), 3);
        assert_eq!(captures.get(1).unwrap().as_str(), "");
        assert_eq!(captures.get(2).unwrap().as_str(), "2.1.1");
    }
}


text:
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

use clap::arg_enum;
use regex::{Captures, Regex};

pub mod gh_release_response;
pub mod gh_repo_response;
pub mod pypi;

pub use gh_release_response::*;
pub use gh_repo_response::*;
pub use pypi::*;

lazy_static! {
    static ref DOCUMENTATION_LINKS: HashMap<&'static str, &'static str> = {
        let mut m = HashMap::new();
        m.insert(
            "buildDependencies",
            "https://nixos.org/nixpkgs/manual/#ssec-stdenv-dependencies\n  ",
        );
        m.insert(
            "fetcher",
            "https://nixos.org/nixpkgs/manual/#chap-pkgs-fetchers\n  ",
        );
        m.insert("meta", "https://nixos.org/nixpkgs/manual/#chap-meta\n  ");
        m.insert(
            "stdenvMkDerivation",
            "https://nixos.org/nixpkgs/manual/#sec-using-stdenv\n  ",
        );
        m
    };
}

arg_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Debug,PartialEq)]
    pub enum Template {
        flake,
        stdenv,
        python,
        module,
        mkshell,
        go,
        rust,
        qt,
        test,
    }
}

arg_enum! {
    #[allow(non_camel_case_types)]
    #[derive(Debug)]
    pub enum Fetcher {
        github,
        gitlab,
        url,
        zip,
        pypi,
    }
}

#[derive(Debug, PartialEq)]
pub enum Repo {
    Pypi(PypiRepo),
    Github(GithubRepo),
}

#[derive(Debug, PartialEq)]
pub struct PypiRepo {
    pub project: String,
}

#[derive(Debug, PartialEq)]
pub struct GithubRepo {
    pub owner: String,
    pub repo: String,
}

#[derive(Debug)]
pub struct ExpressionInfo {
    pub pname: String,
    pub version: String,
    pub license: String,
    pub maintainer: String,
    pub fetcher: Fetcher,
    pub template: Template,
    pub path_to_write: std::path::PathBuf,
    pub top_level_path: std::path::PathBuf,
    pub include_documentation_links: bool,
    pub include_meta: bool,
    pub tag_prefix: String,
    pub owner: String,
    pub src_sha: String,
    pub description: String,
    pub homepage: String,
    pub propagated_build_inputs: Vec<String>,
}

impl ExpressionInfo {
    pub fn format(&self, s: &str) -> String {
        let rev: String = if self.tag_prefix.is_empty() {
            "version".to_owned()
        } else {
            format!(r#""{}${{version}}""#, &self.tag_prefix)
        };

        fn format_inputs(inputs: &Vec<String>) -> String {
            if inputs.is_empty() {
                "".to_owned()
            } else {
                let body = inputs.join("\n    ");
                format!("\n    {}\n ", body)
            }
        }

        let result = s
            .to_owned()
            .replace("@pname@", &self.pname)
            .replace("@pname-import-check@", &self.pname.replace("-", ".")) // used for pythonImportsCheck, "azure-mgmt" -> "azure.mgmt"
            .replace("@version@", &self.version)
            .replace("@owner@", &self.owner)
            .replace("@rev@", &rev)
            .replace("@src_sha@", &self.src_sha)
            .replace("@description@", &self.description)
            .replace("@homepage@", &self.homepage)
            .replace("@license@", &self.license)
            .replace("@maintainer@", &self.maintainer)
            .replace("@propagated_build_inputs@", &format_inputs(&self.propagated_build_inputs));

        if self.include_documentation_links {
            Self::insert_documentation_links(result)
        } else {
            Regex::new(r"@doc:.*@")
                .unwrap()
                .replace_all(&result, "")
                .to_string()
        }
    }

    fn insert_documentation_links(s: String) -> String {
        let re = Regex::new(r"@doc:(\w+)@").unwrap();

        re.replace_all(&s, |caps: &Captures| {
            let key = &caps[1];
            format!(
                "# See the guide for more information: {}",
                DOCUMENTATION_LINKS.get(key).unwrap_or(&"").to_string()
            )
        })
        .to_string()
    }
}

#[derive(Serialize, Deserialize, Debug)]
pub struct UserConfig {
    pub maintainer: Option<String>,
    pub nixpkgs_root: Option<String>,
}


text:
// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::[object Object];
//
// fn main() {
//     let json = r#"{"answer": 42}"#;
//     let model: [object Object] = serde_json::from_str(&json).unwrap();
// }

use serde;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

#[derive(Debug, Serialize, Deserialize)]
pub struct PypiResponse {
    #[serde(rename = "info")]
    pub info: Info,

    #[serde(rename = "last_serial")]
    last_serial: i64,

    #[serde(rename = "releases")]
    pub releases: HashMap<String, Vec<Url>>,

    #[serde(rename = "urls")]
    urls: Vec<Url>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Info {
    #[serde(rename = "author")]
    author: String,

    #[serde(rename = "author_email")]
    author_email: String,

    #[serde(rename = "bugtrack_url")]
    bugtrack_url: Option<serde_json::Value>,

    #[serde(rename = "classifiers")]
    classifiers: Vec<String>,

    #[serde(rename = "description")]
    description: String,

    #[serde(rename = "description_content_type")]
    description_content_type: String,

    #[serde(rename = "docs_url")]
    docs_url: Option<serde_json::Value>,

    #[serde(rename = "download_url")]
    download_url: String,

    #[serde(rename = "home_page")]
    pub home_page: String,

    #[serde(rename = "license")]
    pub license: String,

    #[serde(rename = "name")]
    pub name: String,

    #[serde(rename = "package_url")]
    package_url: String,

    #[serde(rename = "platform")]
    platform: Option<String>,

    #[serde(rename = "project_url")]
    project_url: String,

    #[serde(rename = "project_urls")]
    project_urls: Option<ProjectUrls>,

    #[serde(rename = "release_url")]
    release_url: String,

    #[serde(rename = "requires_dist")]
    pub requires_dist: Option<Vec<String>>,

    #[serde(rename = "requires_python")]
    requires_python: String,

    #[serde(rename = "summary")]
    pub summary: String,

    #[serde(rename = "version")]
    version: String,

    #[serde(rename = "yanked")]
    yanked: bool,

    #[serde(rename = "yanked_reason")]
    yanked_reason: Option<serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectUrls {
    #[serde(rename = "Bug Tracker")]
    bug_tracker: Option<String>,

    #[serde(rename = "Documentation")]
    documentation: Option<String>,

    #[serde(rename = "Homepage")]
    homepage: Option<String>,

    #[serde(rename = "Source Code")]
    source_code: Option<String>,

    #[serde(rename = "Source")]
    source: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Url {
    #[serde(rename = "comment_text")]
    comment_text: Option<String>,

    #[serde(rename = "digests")]
    pub digests: Digests,

    #[serde(rename = "downloads")]
    downloads: i64,

    #[serde(rename = "filename")]
    filename: String,

    #[serde(rename = "has_sig")]
    has_sig: bool,

    #[serde(rename = "md5_digest")]
    md5_digest: String,

    #[serde(rename = "packagetype")]
    pub packagetype: String,

    #[serde(rename = "python_version")]
    python_version: String,

    #[serde(rename = "requires_python")]
    requires_python: Option<String>,

    #[serde(rename = "size")]
    size: i64,

    #[serde(rename = "upload_time")]
    upload_time: String,

    #[serde(rename = "upload_time_iso_8601")]
    upload_time_iso_8601: String,

    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "yanked")]
    yanked: bool,

    #[serde(rename = "yanked_reason")]
    yanked_reason: Option<serde_json::Value>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Digests {
    #[serde(rename = "md5")]
    md5: String,

    #[serde(rename = "sha256")]
    pub sha256: String,
}


text:
// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::[object Object];
//
// fn main() {
//     let json = r#"{"answer": 42}"#;
//     let model: [object Object] = serde_json::from_str(&json).unwrap();
// }

use serde;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct GhRepoResponse {
    #[serde(rename = "id")]
    id: i64,

    #[serde(rename = "node_id")]
    node_id: String,

    #[serde(rename = "name")]
    name: String,

    #[serde(rename = "full_name")]
    full_name: String,

    #[serde(rename = "private")]
    private: bool,

    #[serde(rename = "owner")]
    owner: Owner,

    #[serde(rename = "html_url")]
    html_url: String,

    #[serde(rename = "description")]
    pub description: Option<String>,

    #[serde(rename = "fork")]
    fork: bool,

    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "forks_url")]
    forks_url: String,

    #[serde(rename = "keys_url")]
    keys_url: String,

    #[serde(rename = "collaborators_url")]
    collaborators_url: String,

    #[serde(rename = "teams_url")]
    teams_url: String,

    #[serde(rename = "hooks_url")]
    hooks_url: String,

    #[serde(rename = "issue_events_url")]
    issue_events_url: String,

    #[serde(rename = "events_url")]
    events_url: String,

    #[serde(rename = "assignees_url")]
    assignees_url: String,

    #[serde(rename = "branches_url")]
    branches_url: String,

    #[serde(rename = "tags_url")]
    tags_url: String,

    #[serde(rename = "blobs_url")]
    blobs_url: String,

    #[serde(rename = "git_tags_url")]
    git_tags_url: String,

    #[serde(rename = "git_refs_url")]
    git_refs_url: String,

    #[serde(rename = "trees_url")]
    trees_url: String,

    #[serde(rename = "statuses_url")]
    statuses_url: String,

    #[serde(rename = "languages_url")]
    languages_url: String,

    #[serde(rename = "stargazers_url")]
    stargazers_url: String,

    #[serde(rename = "contributors_url")]
    contributors_url: String,

    #[serde(rename = "subscribers_url")]
    subscribers_url: String,

    #[serde(rename = "subscription_url")]
    subscription_url: String,

    #[serde(rename = "commits_url")]
    commits_url: String,

    #[serde(rename = "git_commits_url")]
    git_commits_url: String,

    #[serde(rename = "comments_url")]
    comments_url: String,

    #[serde(rename = "issue_comment_url")]
    issue_comment_url: String,

    #[serde(rename = "contents_url")]
    contents_url: String,

    #[serde(rename = "compare_url")]
    compare_url: String,

    #[serde(rename = "merges_url")]
    merges_url: String,

    #[serde(rename = "archive_url")]
    archive_url: String,

    #[serde(rename = "downloads_url")]
    downloads_url: String,

    #[serde(rename = "issues_url")]
    issues_url: String,

    #[serde(rename = "pulls_url")]
    pulls_url: String,

    #[serde(rename = "milestones_url")]
    milestones_url: String,

    #[serde(rename = "notifications_url")]
    notifications_url: String,

    #[serde(rename = "labels_url")]
    labels_url: String,

    #[serde(rename = "releases_url")]
    releases_url: String,

    #[serde(rename = "deployments_url")]
    deployments_url: String,

    #[serde(rename = "created_at")]
    created_at: String,

    #[serde(rename = "updated_at")]
    updated_at: String,

    #[serde(rename = "pushed_at")]
    pushed_at: String,

    #[serde(rename = "git_url")]
    git_url: String,

    #[serde(rename = "ssh_url")]
    ssh_url: String,

    #[serde(rename = "clone_url")]
    clone_url: String,

    #[serde(rename = "svn_url")]
    svn_url: String,

    #[serde(rename = "homepage")]
    homepage: Option<String>,

    #[serde(rename = "size")]
    size: i64,

    #[serde(rename = "stargazers_count")]
    stargazers_count: i64,

    #[serde(rename = "watchers_count")]
    watchers_count: i64,

    #[serde(rename = "language")]
    language: Option<String>,

    #[serde(rename = "has_issues")]
    has_issues: bool,

    #[serde(rename = "has_projects")]
    has_projects: bool,

    #[serde(rename = "has_downloads")]
    has_downloads: bool,

    #[serde(rename = "has_wiki")]
    has_wiki: bool,

    #[serde(rename = "has_pages")]
    has_pages: bool,

    #[serde(rename = "forks_count")]
    forks_count: i64,

    #[serde(rename = "mirror_url")]
    mirror_url: Option<serde_json::Value>,

    #[serde(rename = "archived")]
    archived: bool,

    #[serde(rename = "disabled")]
    disabled: bool,

    #[serde(rename = "open_issues_count")]
    open_issues_count: i64,

    #[serde(rename = "license")]
    pub license: License,

    #[serde(rename = "forks")]
    forks: i64,

    #[serde(rename = "open_issues")]
    open_issues: i64,

    #[serde(rename = "watchers")]
    watchers: i64,

    #[serde(rename = "default_branch")]
    default_branch: String,

    #[serde(rename = "temp_clone_token")]
    temp_clone_token: Option<serde_json::Value>,

    #[serde(rename = "network_count")]
    network_count: i64,

    #[serde(rename = "subscribers_count")]
    subscribers_count: i64,

    #[serde(rename = "organization")]
    organization: Option<Owner>,
}

#[derive(Serialize, Deserialize)]
pub struct License {
    #[serde(rename = "key")]
    pub key: String,

    #[serde(rename = "name")]
    name: String,

    #[serde(rename = "spdx_id")]
    spdx_id: String,

    #[serde(rename = "url")]
    url: Option<String>,

    #[serde(rename = "node_id")]
    node_id: String,
}

#[derive(Serialize, Deserialize)]
pub struct Owner {
    #[serde(rename = "login")]
    login: String,

    #[serde(rename = "id")]
    id: i64,

    #[serde(rename = "node_id")]
    node_id: String,

    #[serde(rename = "avatar_url")]
    avatar_url: String,

    #[serde(rename = "gravatar_id")]
    gravatar_id: String,

    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "html_url")]
    html_url: String,

    #[serde(rename = "followers_url")]
    followers_url: String,

    #[serde(rename = "following_url")]
    following_url: String,

    #[serde(rename = "gists_url")]
    gists_url: String,

    #[serde(rename = "starred_url")]
    starred_url: String,

    #[serde(rename = "subscriptions_url")]
    subscriptions_url: String,

    #[serde(rename = "organizations_url")]
    organizations_url: String,

    #[serde(rename = "repos_url")]
    repos_url: String,

    #[serde(rename = "events_url")]
    events_url: String,

    #[serde(rename = "received_events_url")]
    received_events_url: String,

    #[serde(rename = "type")]
    owner_type: String,

    #[serde(rename = "site_admin")]
    site_admin: bool,
}


text:
// Example code that deserializes and serializes the model.
// extern crate serde;
// #[macro_use]
// extern crate serde_derive;
// extern crate serde_json;
//
// use generated_module::[object Object];
//
// fn main() {
//     let json = r#"{"answer": 42}"#;
//     let model: [object Object] = serde_json::from_str(&json).unwrap();
// }

use serde;
use serde::{Deserialize, Serialize};

pub type GhReleaseResponse = Vec<GhReleaseResponseElement>;

#[derive(Debug, Serialize, Deserialize)]
pub struct GhReleaseResponseElement {
    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "assets_url")]
    assets_url: String,

    #[serde(rename = "upload_url")]
    upload_url: String,

    #[serde(rename = "html_url")]
    html_url: String,

    #[serde(rename = "id")]
    id: i64,

    #[serde(rename = "author")]
    author: Author,

    #[serde(rename = "node_id")]
    node_id: String,

    #[serde(rename = "tag_name")]
    pub tag_name: String,

    #[serde(rename = "target_commitish")]
    target_commitish: String,

    #[serde(rename = "draft")]
    draft: bool,

    #[serde(rename = "prerelease")]
    pub prerelease: bool,

    #[serde(rename = "created_at")]
    created_at: String,

    #[serde(rename = "published_at")]
    published_at: String,

    #[serde(rename = "assets")]
    assets: Vec<Asset>,

    #[serde(rename = "tarball_url")]
    tarball_url: String,

    #[serde(rename = "zipball_url")]
    zipball_url: String,

    #[serde(rename = "reactions")]
    reactions: Option<Reactions>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Asset {
    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "id")]
    id: i64,

    #[serde(rename = "node_id")]
    node_id: String,

    #[serde(rename = "name")]
    name: String,

    #[serde(rename = "label")]
    label: Option<serde_json::Value>,

    #[serde(rename = "uploader")]
    uploader: Author,

    #[serde(rename = "content_type")]
    content_type: String,

    #[serde(rename = "state")]
    state: String,

    #[serde(rename = "size")]
    size: i64,

    #[serde(rename = "download_count")]
    download_count: i64,

    #[serde(rename = "created_at")]
    created_at: String,

    #[serde(rename = "updated_at")]
    updated_at: String,

    #[serde(rename = "browser_download_url")]
    browser_download_url: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Reactions {
    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "total_count")]
    total_count: i64,

    #[serde(rename = "+1")]
    the_1: i64,

    #[serde(rename = "-1")]
    reactions_1: i64,

    #[serde(rename = "laugh")]
    laugh: i64,

    #[serde(rename = "hooray")]
    hooray: i64,

    #[serde(rename = "confused")]
    confused: i64,

    #[serde(rename = "heart")]
    heart: i64,

    #[serde(rename = "rocket")]
    rocket: i64,

    #[serde(rename = "eyes")]
    eyes: i64,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Author {
    #[serde(rename = "login")]
    login: String,

    #[serde(rename = "id")]
    id: i64,

    #[serde(rename = "node_id")]
    node_id: String,

    #[serde(rename = "avatar_url")]
    avatar_url: String,

    #[serde(rename = "gravatar_id")]
    gravatar_id: String,

    #[serde(rename = "url")]
    url: String,

    #[serde(rename = "html_url")]
    html_url: String,

    #[serde(rename = "followers_url")]
    followers_url: String,

    #[serde(rename = "following_url")]
    following_url: String,

    #[serde(rename = "gists_url")]
    gists_url: String,

    #[serde(rename = "starred_url")]
    starred_url: String,

    #[serde(rename = "subscriptions_url")]
    subscriptions_url: String,

    #[serde(rename = "organizations_url")]
    organizations_url: String,

    #[serde(rename = "repos_url")]
    repos_url: String,

    #[serde(rename = "events_url")]
    events_url: String,

    #[serde(rename = "received_events_url")]
    received_events_url: String,

    #[serde(rename = "type")]
    author_type: String,

    #[serde(rename = "site_admin")]
    site_admin: bool,
}


text:
final: prev: {
  nix-template = prev.callPackage ./nix-template.nix { };

  devShell = prev.callPackage ./shell.nix { };
}


text:
{ lib, rustPlatform, nix-gitignore, clippy, makeWrapper, nix, openssl, pkg-config, stdenv, darwin }:

rustPlatform.buildRustPackage rec {
   name = "nix-template";

   src = nix-gitignore.gitignoreSource [] ../.;

   cargoLock.lockFile = ../Cargo.lock;

   nativeBuildInputs = [ pkg-config makeWrapper ];
   buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin [ darwin.apple_sdk.frameworks.Security ];

   doCheck = true;
   checkInputs = [ clippy ];
   postCheck = ''
     cargo clippy
   '';

   # needed for `nix-prefetch-url`
   postInstall = ''
     wrapProgram $out/bin/nix-template \
       --prefix PATH : ${lib.makeBinPath [ nix ]}
   '';
}


text:
{ mkShell, cargo, clippy, openssl, pkg-config, rustfmt }:

mkShell {
  buildInputs = [
    cargo
    clippy
    openssl
    pkg-config
    rustfmt
  ];
}