Repository: PerfectlySoft/Perfect
Branch: master
Commit: 859e696457fa
Files: 22
Total size: 134.9 KB
Directory structure:
gitextract_dd4e6amv/
├── .gitattributes
├── .gitignore
├── .jazzy.yaml
├── CODE_OF_CONDUCT.md
├── CODE_OF_CONDUCT.zh_CN.md
├── LICENSE
├── LICENSE.zh_CN
├── Package.swift
├── README.md
├── README.zh_CN.md
├── Sources/
│ └── PerfectLib/
│ ├── Bytes.swift
│ ├── Dir.swift
│ ├── File.swift
│ ├── JSONConvertible.swift
│ ├── Log.swift
│ ├── PerfectError.swift
│ ├── PerfectServer.swift
│ ├── SysProcess.swift
│ └── Utilities.swift
└── Tests/
├── LinuxMain.swift
└── PerfectLibTests/
├── PerfectLibTests.swift
└── XCTestManifests.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
Sources/LinuxBridge/* linguist-vendored
Sources/OpenSSL/* linguist-vendored
Sources/cURL/* linguist-vendored
Xcode/PerfectLib/* linguist-vendored
================================================
FILE: .gitignore
================================================
# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
*.xcscmblueprint
# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control
#
# Pods/
# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts
Carthage/Build
*.d
*.o
*.swiftdeps
*.swiftdoc
*.swiftmodule
*.so
*.DS_Store
# Perfect binaries
PerfectServer/perfectserverfcgi
PerfectServer/perfectserverhttp
# SwiftPM
.build/
Packages/
PerfectLib.xcodeproj/
docs/
================================================
FILE: .jazzy.yaml
================================================
module: PerfectLib
author: PerfectlySoft
author_url: https://perfect.org
github_url: https://github.com/PerfectlySoft/Perfect
copyright: '© 2016 PerfectlySoft Inc. and the Perfect project authors'
theme: fullwidth
xcodebuild_arguments: [-target, PerfectLib, -toolchain, org.swift.3020160620a]
clean: true
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [mailto:info@perfect.org]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CODE_OF_CONDUCT.zh_CN.md
================================================
# 贡献者行为规范
## 我们的承诺
为了实现一个开放、和谐的软件开发环境,作为该软件的贡献者和版本维护者,我们保证在社区参与项目的过
程中,对所有人一视同仁。在社区中,任何人,无论年龄、身材、长相,或者身体是否有缺陷;无论种族、
性别和学历出身;无论国籍、血统、宗教信仰和任何性取向,我们都承诺避免任何骚扰和歧视。
## 我们的标准
为创造良好的开发环境,我们鼓励:
* 采用友好的语言,秉持包容的态度
* 充分尊重不同的观点和对问题的不同看法
* 耐心接纳建设性批评
* 关注于为社区未来提高产品价值
* 对其他社区成员保持同情心
无法接受的行为包括:
* 淫秽言语或低级下流的措辞
* 恶意评论,针对人身和政治观点的言语攻击
* 公开或私下的言语骚扰
* 未经许可公布他人隐私,包括实际住址或电子地址
* 从专业角度考虑的其他不正当行为
## 我们的责任
项目管理维护人员有义务澄清本行为规范,并针对任何不当行为采取必要措施进行纠正。
项目管理维护人员有权并有义务删除、修改、抵制任何不符合本行为规范的评论、代码内容、文字提交和各类
报告P,也有权因其各种如恐吓、侵犯或者伤害的不当行为暂时或永久屏蔽违规账号。
## 适用范围
本行为规范适用于任何本项目或本项目所在社区空间内的公众行为,如公众邮件账号、公众社交媒体账号,
或者用于在线和离线活动中的委托代表。项目管理维护人员可以根据需要澄清或委派项目代表。
## 加强措施
任何不当言论、骚扰和其他不可接受的不当行为都可以通过[mailto:info@perfect.org]进行举报。所有
投诉均会被审理并根据需要进行调查。项目团队有义务为举报者身份保密。更多加强措施会在其他文件中单独
公开。
对于不遵守本行为规范的项目管理人员,本项目其他领导成员可能会临时或永久取消其管理权。
## 文件归属
本文节选自 [Contributor Covenant][homepage], version 1.4,
可从以下网址查看: [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: LICENSE.zh_CN
================================================
Apache许可证
2.0版 2004年1月
http://www.apache.org/licenses/
关于使用、复制和分发的条款
定义
"许可证"是指根据本文件第1到第9部分关于使用、复制和分发的条款。
"许可证颁发者"是指版权所有者或者由版权所有者授权许可证的实体。
"法律实体"是指实施实体和进行控制的所有其它实体受该实体控制,或者受该实体集中控制。根据此定义,"控制"是指(i)让无论是否签订协议的上述实体,进行指导或管理的直接权利或间接权利,或者(ii)拥有百分之五十(50%)或以上已发行股票的所有者,或者(iii)上述实体的实权所有者。
"用户"(或"用户的")是指行使本许可证所授予权限的个人或法律实体。
"源程序"形式是指对包括但不限于软件源代码、文件源程序和配置文件进行修改的首选形式。
"目标"形式是指对源程序形式进行机械转换或翻译的任何形式,包括但不限于对编译的目标代码,生成的文件以及转换为其它媒体类型。
"作品"是指根据本许可证所制作的源程序形式或目标形式的著作,在著作中包含的或附加的版权通知(在下面附录中提供了一个示例)。
"衍生作品"是指基于作品(或从作品衍生而来)的源程序形式或目标形式的任何作品,以及编辑修订、注释、详细描述或其它修订等构成原创著作作品的整体。根据本许可证,衍生作品不得包括与作品及其衍生作品分离之作品,或仅与作品及其衍生作品的接口相链接(或按名称结合)之作品。
"贡献"是指任何著作作品,包括作品的原始版本和对该作品或衍生作品所做的任何修订或补充,意在提交给许可证颁发者以让版权所有者或代表版权所有者的授权个人或法律实体包含在其作品中。根据此定义,"提交"一词表示发送给许可证颁发者或其代表人,任何电子的、口头的或书面的交流信息形式,包括但不限于在由许可证颁发者或者代表其管理的电子邮件清单、源代码控制系统、以及发布跟踪系统上为讨论和提高作品的交流,但不包括由版权所有者以书面形式明显标注或指定为"非贡献"的交流活动。
"贡献者"是指许可证颁发者和代表从许可证颁发者接受之贡献的并随后包含在作品之贡献中的任何个人或法律实体。
版权许可证的授予。根据本许可证的条款,每个贡献者授予用户永久性的、全球性的、非专有性的、免费的、无版权费的、不可撤销的版权许可证以源程序形式或目标形式复制、准备衍生作品、公开显示、公开执行、授予分许可证、以及分发作品和这样的衍生作品。
专利许可证的授予。根据本许可证的条款,每个贡献者授予用户永久性的、全球性的、非专有性的、免费的、无版权费的、不可撤销的(除在本部分进行说明)专利许可证对作品进行制作、让人制作、使用、提供销售、销售、进口和其它转让,且这样的许可证仅适用于在所递交作品的贡献中因可由单一的或多个这样的贡献者授予而必须侵犯的申请专利。如果用户对任何实体针对作品或作品中所涉及贡献提出因直接性或贡献性专利侵权而提起专利法律诉讼(包括交互诉讼请求或反索赔),那么根据本许可证,授予用户针对作品的任何专利许可证将在提起上述诉讼之日起终止。
重新分发。用户可在任何媒介中复制和分发作品或衍生作品之副本,无论是否修订,还是以源程序形式或目标形式,条件是用户需满足下列条款:
用户必须为作品或衍生作品的任何其他接收者提供本许可证的副本;并且
用户必须让任何修改过的文件附带明显的通知,声明用户已更改文件;并且
用户必须从作品的源程序形式中保留衍生作品源程序形式的用户所分发的所有版权、专利、商标和属性通知,但不包括不属于衍生作品任何部分的类似通知;并且
如果作品将"通知"文本文件包括为其分发作品的一部分,那么用户分发的任何衍生作品中须至少在下列地方之一包括,在这样的通知文件中所包含的属性通知的可读副本,但不包括那些不属于衍生作品任何部分的通知:在作为衍生作品一部分而分发的通知文本文件中;如果与衍生作品一起提供则在源程序形式或文件中;或者通常作为第三方通知出现的时候和地方,在衍生作品中产生的画面中。通知文件的内容仅供信息提供,并未对许可证进行修改。用户可在其分发的衍生作品中在作品的通知文本后或作为附录添加自己的属性通知,条件是附加的属性通知不得构成修改本许可证。
用户可以为自身所做出的修订添加自己的版权声明并可对自身所做出修订内容或为这样的衍生作品作为整体的使用、复制或分发提供附加或不同的条款,条件是用户对作品的使用、复制和分发必须符合本许可证中声明的条款。
贡献的提交。除非用户明确声明,在作品中由用户向许可证颁发者的提交若要包含在贡献中,必须在无任何附加条款下符合本许可证的条款。尽管上面如此规定,执行许可证颁发者有关贡献的条款时,任何情况下均不得替代或修改任何单独许可证协议的条款。
商标。本许可证并未授予用户使用许可证颁发者的商号、商标、服务标记或产品名称,除非将这些名称用于合理性和惯例性描述作品起源和复制通知文件的内容时。
保证否认条款。除非因适用法律需要或书面同意,许可证颁发者以"按原样"基础提供作品(并且每个贡献者提供其贡献),无任何明示的或暗示的保证或条件,包括但不限于关于所有权、不侵权、商品适销性、或适用性的保证或条件。用户仅对使用或重新分发作品的正确性负责,并需承担根据本许可证行使权限时的任何风险。
责任限制条款。在任何情况下并根据任何法律,无论是因侵权(包括过失)或根据合同,还是其它原因,除非根据适用法律需要(例如故意行为和重大过失行为)或经书面同意,即使贡献者事先已被告知发生损害的可能性,任何贡献者不就用户因使用本许可证或不能使用或无法使用作品(包括但不限于商誉损失、停工、计算机失效或故障,或任何商业损坏或损失)而造成的损失,包括直接的、非直接的、特殊的、意外的或间接的字符损坏而负责。
接受保证或附加责任。重新分发作品或及其衍生作品时,用户可选择提供或为符合本许可证承担之支持、担保、赔偿或其它职责义务和/或权利而收取费用。但是,在承担上述义务时,用户只可代表用户本身和用户本身责任来执行,无需代表任何其它贡献者,并且用户仅可保证、防护并保持每个贡献者不受任何因此而产生的责任或对因用户自身承担这样的保证或附加责任而对这样的贡献者所提出的索赔。
条款结束
附录:如何向用户作品中应用Apache许可证。
若要向用户作品应用Apache许可证,请附加下列样本通知,将括号"[]"中的字段以用户自身的区分信息来替换(但不包括括号)。文本必须以文件格式适当的注释句法包含在其中。另外建议将文件名或类别名以及目的说明包含在相同的"打印页"上作为版权通知,以更加容易的区分出第三方档案。
版权所有[yyyy][版权所有者的名称]
根据2.0版本Apache许可证("许可证")授权;
根据本许可证,用户可以不使用此文件。
用户可从下列网址获得许可证副本:
http://www.apache.org/licenses/LICENSE-2.0
除非因适用法律需要或书面同意,
根据许可证分发的软件是基于"按原样"基础提供,
无任何明示的或暗示的保证或条件。
详见根据许可证许可下,特定语言的管辖权限和限制。
================================================
FILE: Package.swift
================================================
// swift-tools-version:5.1
//
// Package.swift
// PerfectLib
//
// Created by Kyle Jessup on 3/22/16.
// Copyright (C) 2016 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
import PackageDescription
#if os(Linux)
let package = Package(
name: "PerfectLib",
products: [
.library(name: "PerfectLib", targets: ["PerfectLib"])
],
dependencies: [.package(url: "https://github.com/PerfectlySoft/Perfect-LinuxBridge.git", from: "3.0.0")],
targets: [
.target(name: "PerfectLib", dependencies: ["LinuxBridge"]),
.testTarget(name: "PerfectLibTests", dependencies: ["PerfectLib"])
]
)
#else
let package = Package(
name: "PerfectLib",
platforms: [
.macOS(.v10_15)
],
products: [
.library(name: "PerfectLib", targets: ["PerfectLib"])
],
dependencies: [],
targets: [
.target(name: "PerfectLib", dependencies: []),
.testTarget(name: "PerfectLibTests", dependencies: ["PerfectLib"])
]
)
#endif
================================================
FILE: README.md
================================================
# Perfect: Server-Side Swift [简体中文](README.zh_CN.md)
<p align="center">
<a href="http://perfect.org/get-involved.html" target="_blank">
<img src="http://perfect.org/assets/github/perfect_github_2_0_0.jpg" alt="Get Involed with Perfect!" width="854" />
</a>
</p>
<p align="center">
<a href="https://developer.apple.com/swift/" target="_blank">
<img src="https://img.shields.io/badge/Swift-5.2-orange.svg?style=flat" alt="Swift 5.2">
</a>
<a href="https://developer.apple.com/swift/" target="_blank">
<img src="https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat" alt="Platforms macOS | Linux">
</a>
<a href="http://perfect.org/licensing.html" target="_blank">
<img src="https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat" alt="License Apache">
</a>
</p>
## Perfect: Server-Side Swift
Perfect is a complete and powerful toolbox, framework, and application server for Linux, iOS, and macOS (OS X). It provides everything a Swift engineer needs for developing lightweight, maintainable, and scalable apps and other REST services entirely in the Swift programming language for both client-facing and server-side applications.
Perfect includes a suite of tools that will enhance your productivity as you use only one programming language to build your apps: Swift. The global development community’s most dynamic and popular server-side toolbox and framework available today, Perfect is the backbone for many live web applications and apps available on iTunes.
This guide is designed for developers at all levels of experience to get Perfect up and running quickly.
## Working with Perfect
### Compatibility with Swift
The master branch of this project currently compiles with **Xcode 11** or the **Swift 5** toolchain on Ubuntu.
### Getting Started
[Access a tutorial](https://github.com/PerfectlySoft/PerfectDocs/blob/master/guide/gettingStarted.md) to help you get started using Perfect quickly. It includes straightforward examples of how Perfect can be used.
### Documentation
[Get started working with Perfect](https://github.com/PerfectlySoft/PerfectDocs), deploy your apps, and find more detailed help by consulting our reference library.
We welcome contributions to Perfect’s documentation. If you spot a typo, bug, or other errata, or have additions or suggestions to recommend, please create a pull request or issue in Github.
### Community
We all need a little help now and then. If you do too, don’t be shy, ask us or the friendly and supportive Perfect community:
[Slack](http://perfect.ly/) | [Twitter](https://twitter.com/perfectlysoft)
### Deployment
Your Perfect project can be deployed to any Swift compatible Linux server. We provide a macOS desktop application, [Perfect Assistant](https://www.perfect.org/en/perfect-assistant.html), to help with AWS and Google Cloud deployments. Additional deployment options are in the works.
### Samples, Examples, and Tutorials
Our library continues to grow as members of [the Swift-Perfect development community have shared many samples and examples](https://github.com/PerfectExamples) of their projects in Perfect. Examples include:
- [WebSockets Server](https://github.com/PerfectExamples/PerfectExample-WebSocketsServer)
- [URL Routing](https://github.com/PerfectExamples/PerfectExample-URLRouting)
- [Upload Enumerator](https://github.com/PerfectExamples/PerfectExample-UploadEnumerator)
There are [many more examples](https://github.com/PerfectExamples) you can explore. Please share yours!
## Core Perfect Modules
Perfect project is divided into several repositories to make it easy for you to find, download, and install the components you need:
- [Perfect](https://github.com/PerfectlySoft/Perfect) – This repository contains the core PerfectLib and will continue to be the main landing point for the project
- [Perfect Docs](https://github.com/PerfectlySoft/PerfectDocs) – Contains all API reference-related material
### Examples
- [Perfect Template](https://github.com/PerfectlySoft/PerfectTemplate) - A simple starter project which compiles with the Swift Package Manager into a standalone executable HTTP server. This repository is ideal for starting a Perfect-based project
- [Perfect Examples](https://github.com/PerfectExamples) - All the Perfect example projects
### DataSources
- [Perfect Redis](https://github.com/PerfectlySoft/Perfect-Redis) - The Redis database connector
- [Perfect SQLite](https://github.com/PerfectlySoft/Perfect-SQLite) - SQLite3 database connector
- [Perfect PostgreSQL](https://github.com/PerfectlySoft/Perfect-PostgreSQL) - PostgreSQL database connector
- [Perfect MySQL](https://github.com/PerfectlySoft/Perfect-MySQL) - MySQL database connector
- [Perfect MongoDB](https://github.com/PerfectlySoft/Perfect-MongoDB) - MongoDB database connector
- [Perfect FileMaker](https://github.com/PerfectlySoft/Perfect-FileMaker) - FileMaker Server database connector
### Utilities
- [Perfect FastCGI Apache 2.4](https://github.com/PerfectlySoft/Perfect-FastCGI-Apache2.4) - Apache 2.4 FastCGI module; required for the Perfect FastCGI server variant
- [Perfect XML](https://github.com/PerfectlySoft/Perfect-XML) - DOM Core level 2 read-only APIs and XPath support
- [Perfect HTTP Server](https://github.com/PerfectlySoft/Perfect-HTTPServer) - HTTP 1.1 server for Perfect
- [Perfect Mustache](https://github.com/PerfectlySoft/Perfect-Mustache) - Mustache template support for Perfect
- [Perfect CURL](https://github.com/PerfectlySoft/Perfect-CURL) - cURL support for Perfect
- [Perfect WebSockets](https://github.com/PerfectlySoft/Perfect-WebSockets) - WebSockets support for Perfect
- [Perfect Zip](https://github.com/PerfectlySoft/Perfect-Zip) - provides simple zip and unzip functionality
- [Perfect Notifications](https://github.com/PerfectlySoft/Perfect-Notifications) - provides support for Apple Push Notification Service (APNS).
## More about Perfect
Perfect operates using either a standalone [HTTP server](https://github.com/PerfectlySoft/Perfect-HTTP), [HTTPS server](https://github.com/PerfectlySoft/Perfect-HTTPServer), or through [FastCGI server](https://github.com/PerfectlySoft/Perfect-FastCGI). It provides a system for loading your Swift-based modules at startup, for interfacing those modules with its request/response objects, or to the built-in [Mustache template processing system](https://github.com/PerfectlySoft/Perfect-Mustache).
Perfect is built on a completely asynchronous, high-performance networking engine to provide a scalable option for internet services. It supports Secure Sockets Layer (SSL) encryption, and it features a suite of tools commonly required by internet servers such as [WebSockets](https://github.com/PerfectlySoft/Perfect-WebSockets) and [iOS push notifications](https://github.com/PerfectlySoft/Perfect-Notifications), but you are not limited to those options.
Feel free to use your favourite JSON or templating systems, etc.
### Join and Contribute to the Community
The Swift-Perfect developer community is vital to improving Perfect and supporting one another.
You can help other developers by sharing your expertise and tips, as well as learn from others, by joining the [Perfect Slack channel](http://perfect.ly). Contributions of all kinds are welcome: reporting issues, updating documentation, fixing bugs, building examples, sharing projects, and any other tips that may help the Swift-Perfect community.
If you would like to share your example project, tutorial, or video, please share the URL of your work on GitHub and [Twitter](https://twitter.com/perfectlysoft), and the Perfect team will highlight it to the community.
### Code of Conduct
The Perfect team welcomes people of all ethnicities, nationalities, ages, gender, disability, levels of experience, and religious beliefs to use and contribute to the Perfect project. We pledge to foster and enforce a harassment-free environment of openness, respect, and cooperation for everyone in all project and public spaces online or offline.
Please report any behaviour that violates our [Code of Conduct](https://github.com/PerfectlySoft/Perfect/blob/master/CODE_OF_CONDUCT.md) to [info@perfect.org](mailto:info@perfect.org). The Perfect team is committed to enforcing this Code of Conduct to ensure everyone who wishes to use, contribute to, and comment on the Perfect project may do so freely and openly and without fear of reprisal.
We will investigate all complaints of unacceptable or abusive behaviour or comments expediently, and we will maintain the confidentiality of the person who reports any perceived infraction or wrongdoing to us. We will not tolerate any form of direct or indirect harassment or discrimination within the Swift-Perfect community, and will take appropriate, fair, and corrective action to any instance of inappropriate behaviour.
The Perfect team maintains the right to remove, edit, or reject any comments, code, edits, or issues that do not align with our Code of Conduct.
================================================
FILE: README.zh_CN.md
================================================
# Perfect:Swift 语言服务器端软件框架 [English](README.md)
<p align="center">
<a href="http://perfect.org/get-involved.html" target="_blank">
<img src="http://perfect.org/assets/github/perfect_github_2_0_0.jpg" alt="Get Involed with Perfect!" width="854" />
</a>
</p>
<p align="center">
<a href="https://developer.apple.com/swift/" target="_blank">
<img src="https://img.shields.io/badge/Swift-5.2-orange.svg?style=flat" alt="Swift 5.2">
</a>
<a href="https://developer.apple.com/swift/" target="_blank">
<img src="https://img.shields.io/badge/Platforms-OS%20X%20%7C%20Linux%20-lightgray.svg?style=flat" alt="Platforms OS X | Linux">
</a>
<a href="http://perfect.org/licensing.html" target="_blank">
<img src="https://img.shields.io/badge/License-Apache-lightgrey.svg?style=flat" alt="License Apache">
</a>
</p>
## Perfect:Swift 语言服务器端软件框架
Perfect是一组完整、强大的工具箱、软件框架体系和Web应用服务器,可以在Linux、iOS和macOS (OS X)上使用。该软件体系为Swift工程师量身定制了一整套用于开发轻量、易维护、规模可扩展的Web应用及其它REST服务的解决方案,这样Swift工程师就可以实现同时在服务器和客户端上采用同一种语言开发软件项目。
Perfect内建整套工具集,因为无论是客户端还是服务器都能够在此基础之上用同一种计算机语言Swift进行程序开发,因此能够为软件工程师大幅提高工作效率。在全球目前众多的服务器端框架体系和工具箱产品之中,Perfect目前已经成为许多iTunes在线应用程序的可靠后台应用。
无论您是资深程序员还是入门级的软件工程师,本文都能够帮助您快速启动Perfect实现服务器项目开发运行。
## 使用Perfect
### 快速上手
[在线教程(简体中文)](https://github.com/PerfectlySoft/PerfectDocs/blob/master/guide.zh_CN/gettingStarted.md) 能够帮助您快速开始使用Perfect。该指南包括了如何使用Perfect的几个典型例子。
### 文档
[Perfect帮助文档(简体中文)](https://github.com/PerfectlySoft/PerfectDocs) 如何部署应用程序、如何查找详细文档和帮助。
我们欢迎所有贡献以及对Perfect文档提高的宝贵意见。我们欢迎您为Perfect付出宝贵的支持。如果您发现了任何文字或者内容有错误,或者有任何建议,请[提交一个代码上传请求,或在JIRA上报告问题](http://jira.perfect.org:8080/servicedesk/customer/portal/1/user/login?destination=portal%2F1).
### 社区
我们总会需要您的帮助。如果您真的有想法,不妨加入我们的Perfect支持社区:
[Slack](http://perfect.ly/) | [Twitter](https://twitter.com/perfectlysoft)
### 部署
目前,部署Perfect的方式可以选择[Docker](https://hub.docker.com/r/perfectlysoft/ubuntu/)和[Heroku](https://github.com/PerfectlySoft/Perfect-Heroku-Buildpack)。我们强烈推荐使用这种方式进行部署,因为这些部署方式是通过最新Swift 3.0 和 Perfect 2.0编译完成的。
### 教程和案例
我们的资源库一直在随着社区成员的加入而不断增长,[Swift-Perfect开发社区有许多源程序共分享](https://github.com/PerfectExamples),都是建立在Perfect程序框架之上。典型例子包括:
- [WebSockets 服务器](https://github.com/PerfectlySoft/PerfectExample-WebSocketsServer)
- [URL 路由](https://github.com/PerfectlySoft/PerfectExample-URLRouting)
- [文件上传](https://github.com/PerfectlySoft/PerfectExample-UploadEnumerator)
[更多例子敬请关注!](https://github.com/PerfectlySoft/PerfectExamples)
[Perfect 1.0教程](http://perfect.org/tutorials.html) (支持 Swift 2.2) 由Swift-Perfect社区成员贡献。或者[从Perfect 2.0开始](http://perfect.org/downloads.html#download-perfect) (支持 Swift 3.0).
## 核心 Perfect 模块
Perfect 项目由若干代码资源库构成,便于您按需查找、下载和安装必要的组件:
- [Perfect](https://github.com/PerfectlySoft/Perfect):核心的程序库和基础软件框架
- [Perfect Docs](https://github.com/PerfectlySoft/PerfectDocs):所有必要的程序文档和帮助内容
### 参考和样例
- [Perfect 模板](https://github.com/PerfectlySoft/PerfectTemplate):一个使用SPM软件包管理器快速上手的入门项目,能够编译为一个独立运行的HTTP服务器。该代码资源非常适合基于Perfect的项目就此开始开发过程。
- [Perfect 样例](https://github.com/PerfectExamples):所有Perfect 项目的典型样例
### 数据源
- [Perfect Redis](https://github.com/PerfectlySoft/Perfect-Redis):Redis 数据库连接工具
- [Perfect SQLite](https://github.com/PerfectlySoft/Perfect-SQLite):SQLite3 数据库连接工具
- [Perfect PostgreSQL](https://github.com/PerfectlySoft/Perfect-PostgreSQL):PostgreSQL 数据库连接工具
- [Perfect MySQL](https://github.com/PerfectlySoft/Perfect-MySQL):MySQL 数据库连接工具
- [Perfect MongoDB](https://github.com/PerfectlySoft/Perfect-MongoDB):MongoDB 数据库连接工具
- [Perfect FileMaker](https://github.com/PerfectlySoft/Perfect-FileMaker):FileMaker 数据库连接工具
### 工具集
- [Perfect FastCGI Apache 2.4](https://github.com/PerfectlySoft/Perfect-FastCGI-Apache2.4) - Apache 2.4 FastCGI 模块。如果您使用FastCGI用于基础Web服务,请使用该模块
- [Perfect XML](https://github.com/PerfectlySoft/Perfect-XML) - DOM文档对象二级核心只读函数库和XPath路径支持
- [Perfect HTTP Server](https://github.com/PerfectlySoft/Perfect-HTTPServer) - HTTP 1.1标准的 Perfect服务器
- [Perfect Mustache](https://github.com/PerfectlySoft/Perfect-Mustache) - Mustache静态模板支持
- [Perfect CURL](https://github.com/PerfectlySoft/Perfect-CURL) - cURL网页传输支持
- [Perfect WebSockets](https://github.com/PerfectlySoft/Perfect-WebSockets) - 网络套接字WebSockets支持
- [Perfect Zip](https://github.com/PerfectlySoft/Perfect-Zip) - 提供简单的zip压缩和解压缩功能
- [Perfect Notifications](https://github.com/PerfectlySoft/Perfect-Notifications) - 提供苹果消息推送服务支持(APNS)
## 更多内容
Perfect 可以作为一个独立的[HTTP服务器](https://github.com/PerfectlySoft/Perfect-HTTP)或[HTTPS加密服务器](https://github.com/PerfectlySoft/Perfect-HTTPServer)进行运行,或者通过[FastCGI快速网关服务器](https://github.com/PerfectlySoft/Perfect-FastCGI)进行运行。简单来说就是提供一个能够在系统启动是加载的Web服务,从而能够将您自行开发的Swift源码模块根据URL路由要求实现请求/响应,或者根据内建的[Mustache模板](https://github.com/PerfectlySoft/Perfect-Mustache)处理页面。
Perfect是一个完全异步、高性能的网络引擎,并且能够为互联网服务提供大吞吐量控制。该软件体系支持安全套接字(SSL)加密,并且封装了一系列互联网服务器通用的特性,比如[WebSockets](https://github.com/PerfectlySoft/Perfect-WebSockets) 和 [iOS消息推送](https://github.com/PerfectlySoft/Perfect-Notifications)。然而,您的开发可以不必受限于这些选项。
请根据您自己的喜好使用JSON或者其他的模板系统,等等。
### 加入我们的开发社区并贡献自己的力量
Swift-Perfect开发者社区是改进Perfect产品并实现客户支持的关键。
在社区里,您可以通过加入[Perfect Slack 频道](http://perfect.ly)和[Perfect Gitter 频道](https://gitter.im/PerfectlySoft/Perfect)互相帮助、分享技术、互相学习和研究诀窍。任何一种贡献方式我们都非常欢迎:问题汇报、文档更新、补丁修复、编写案例、分享项目或者任何编程窍门,我们相信这些都能够极大地帮助我们的Swift-Perfect社区。
如果您希望分享一下您的项目、教程或者视频,请将URL共享到我们的推特或者GitHub账号:[Perfect 推特](https://twitter.com/perfectlysoft)。之后我们的Perfect团队会继续推广。
### 行为规范
Perfect团队欢迎所有不同种族、国际、不同年龄、性别、身残志坚、不同学历出身、不同宗教信仰的人为我们的Perfect项目作出贡献。我们承诺为所有项目和公众在线/离线空间提供一个开放、祥和、互相尊重、共同工作的环境。
如果您发现有任何违反上述[行为规范](https://github.com/PerfectlySoft/Perfect/blob/master/CODE_OF_CONDUCT.zh_CN.md)的行为,请[给我们写邮件](mailto:info@perfect.org)。Perfect团队承诺致力于维护上述价值观念以确保所有参与者和用户都能实现对Perfect项目的充分开放的自由使用、自由评论和自由贡献,不需要对任何恐吓而害怕和妥协。
我们会调查任何不当行为与不当言论的投诉,同时我们会对检举人身份保密,便于对各种违法行为进行举报。我们不会容忍在Swift-Perfect社区内的任何直接或间接的骚扰或歧视,并会针对各类不当行为采取适度、公平的纠正措施。
Perfect团队有权删除、修改或拒绝任何不符合我们行为规范的各种言论、代码、版本或问题报告。
================================================
FILE: Sources/PerfectLib/Bytes.swift
================================================
//
// Bytes.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/7/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
/// A Bytes object represents an array of UInt8 and provides various utilities for importing and exporting values into and out of that array.
/// The object maintains a position marker which is used to denote the position from which new export operations proceed.
/// An export will advance the position by the appropriate amount.
public final class Bytes {
/// The position from which new export operations begin.
public var position = 0
/// The underlying UInt8 array
public var data: [UInt8]
/// Indicates the number of bytes which may be successfully exported
public var availableExportBytes: Int { return data.count - position }
/// Create an empty Bytes object
public init() {
data = [UInt8]()
}
/// Initialize with existing bytes
public init(existingBytes: [UInt8]) {
data = existingBytes
}
// -- IMPORT
/// Imports one UInt8 value appending it to the end of the array
/// - returns: The Bytes object
@discardableResult
public func import8Bits(from frm: UInt8) -> Bytes {
data.append(frm)
return self
}
/// Imports one UInt16 value appending it to the end of the array
/// - returns: The Bytes object
@discardableResult
public func import16Bits(from frm: UInt16) -> Bytes {
data.append(UInt8(frm & 0xFF))
data.append(UInt8((frm >> 8) & 0xFF))
return self
}
/// Imports one UInt32 value appending it to the end of the array
/// - returns: The Bytes object
@discardableResult
public func import32Bits(from frm: UInt32) -> Bytes {
data.append(UInt8(frm & 0xFF))
data.append(UInt8((frm >> 8) & 0xFF))
data.append(UInt8((frm >> 16) & 0xFF))
data.append(UInt8((frm >> 24) & 0xFF))
return self
}
/// Imports one UInt64 value appending it to the end of the array
/// - returns: The Bytes object
@discardableResult
public func import64Bits(from frm: UInt64) -> Bytes {
data.append(UInt8(frm & 0xFF))
data.append(UInt8((frm >> 8) & 0xFF))
data.append(UInt8((frm >> 16) & 0xFF))
data.append(UInt8((frm >> 24) & 0xFF))
data.append(UInt8((frm >> 32) & 0xFF))
data.append(UInt8((frm >> 40) & 0xFF))
data.append(UInt8((frm >> 48) & 0xFF))
data.append(UInt8((frm >> 56) & 0xFF))
return self
}
/// Imports an array of UInt8 values appending them to the end of the array
/// - returns: The Bytes object
@discardableResult
public func importBytes(from frm: [UInt8]) -> Bytes {
data.append(contentsOf: frm)
return self
}
/// Imports the array values of the given Bytes appending them to the end of the array
/// - returns: The Bytes object
@discardableResult
public func importBytes(from frm: Bytes) -> Bytes {
data.append(contentsOf: frm.data)
return self
}
/// Imports an `ArraySlice` of UInt8 values appending them to the end of the array
/// - returns: The Bytes object
@discardableResult
public func importBytes(from frm: ArraySlice<UInt8>) -> Bytes {
data.append(contentsOf: frm)
return self
}
// -- EXPORT
/// Exports one UInt8 from the current position. Advances the position marker by 1 byte.
/// - returns: The UInt8 value
public func export8Bits() -> UInt8 {
let result = data[position]
position += 1
return result
}
/// Exports one UInt16 from the current position. Advances the position marker by 2 bytes.
/// - returns: The UInt16 value
public func export16Bits() -> UInt16 {
let one = UInt16(data[position])
position += 1
let two = UInt16(data[position])
position += 1
return (two << 8) + one
}
/// Exports one UInt32 from the current position. Advances the position marker by 4 bytes.
/// - returns: The UInt32 value
public func export32Bits() -> UInt32 {
let one = UInt32(data[position])
position += 1
let two = UInt32(data[position])
position += 1
let three = UInt32(data[position])
position += 1
let four = UInt32(data[position])
position += 1
return (four << 24) + (three << 16) + (two << 8) + one
}
/// Exports one UInt64 from the current position. Advances the position marker by 8 bytes.
/// - returns: The UInt64 value
public func export64Bits() -> UInt64 {
let one = UInt64(data[position])
position += 1
let two = UInt64(data[position]) << 8
position += 1
let three = UInt64(data[position]) << 16
position += 1
let four = UInt64(data[position]) << 24
position += 1
let five = UInt64(data[position]) << 32
position += 1
let six = UInt64(data[position]) << 40
position += 1
let seven = UInt64(data[position]) << 48
position += 1
let eight = UInt64(data[position]) << 56
position += 1
return (one+two+three+four)+(five+six+seven+eight)
}
/// Exports the indicated number of bytes
public func exportBytes(count cnt: Int) -> [UInt8] {
var sub = [UInt8]()
let end = position + cnt
while position < end {
sub.append(data[position])
position += 1
}
return sub
}
}
================================================
FILE: Sources/PerfectLib/Dir.swift
================================================
//
// Dir.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/7/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import LinuxBridge
#else
import Darwin
#endif
/// This class represents a directory on the file system.
/// It can be used for creating & inspecting directories and enumerating directory contents.
public struct Dir {
/// A typealias for directory permission modes.
public typealias PermissionMode = File.PermissionMode
var internalPath = ""
/// Create a new Dir object with the given path
public init(_ path: String) {
let pth = path.ends(with: "/") ? path : path + "/"
internalPath = File.resolveTilde(inPath: pth)
}
/// Returns true if the directory exists
public var exists: Bool {
return exists(realPath)
}
/// Set this Dir as the process' working directory.
public func setAsWorkingDir() throws {
let res = chdir(internalPath)
guard res == 0 else {
try ThrowFileError()
}
}
/// Return the process' current working directory.
public static var workingDir: Dir {
var buffer = Array(repeating: 0 as UInt8, count: 2049)
buffer.withUnsafeMutableBytes {
_ = getcwd($0.bindMemory(to: Int8.self).baseAddress, 2048)
}
let path = String(cString: buffer)
return Dir(path)
}
func exists(_ path: String) -> Bool {
return access(path, F_OK) != -1
}
/// Creates the directory using the provided permissions. All directories along the path will be created if need be.
/// - parameter perms: The permissions for use for the new directory and preceeding directories which need to be created. Defaults to RWX-GUO
/// - throws: `PerfectError.FileError`
public func create(perms: PermissionMode = [.rwxUser, .rxGroup, .rxOther]) throws {
let pth = realPath
var currPath = pth.begins(with: "/") ? "/" : ""
for component in pth.filePathComponents where component != "/" {
currPath += component
defer {
currPath += "/"
}
guard !exists(currPath) else {
continue
}
let res = mkdir(currPath, perms.rawValue)
guard res != -1 else {
try ThrowFileError()
}
}
}
/// Deletes the directory. The directory must be empty in order to be successfully deleted.
/// - throws: `PerfectError.FileError`
public func delete() throws {
let res = rmdir(realPath)
guard res != -1 else {
try ThrowFileError()
}
}
/// Returns the name of the directory.
public var name: String {
return internalPath.lastFilePathComponent
}
/// Returns a Dir object representing the current Dir's parent. Returns nil if there is no parent.
public var parentDir: Dir? {
guard internalPath != "/" else {
return nil // can not go up
}
return Dir(internalPath.deletingLastFilePathComponent)
}
/// Returns the path to the current directory.
public var path: String {
return internalPath
}
/// Returns the UNIX style permissions for the directory.
public var perms: PermissionMode {
get {
return File(internalPath).perms
}
set {
File(internalPath).perms = newValue
}
}
var realPath: String {
return internalPath.resolvingSymlinksInFilePath
}
#if os(Linux)
func readDir(_ d: OpaquePointer, _ dirEnt: inout dirent, _ endPtr: UnsafeMutablePointer<UnsafeMutablePointer<dirent>?>!) -> Int32 {
guard let ent = readdir(d) else {
return -1
}
dirEnt = ent.pointee
return 0
}
#else
func readDir(_ d: UnsafeMutablePointer<DIR>, _ dirEnt: inout dirent, _ endPtr: UnsafeMutablePointer<UnsafeMutablePointer<dirent>?>!) -> Int32 {
return readdir_r(d, &dirEnt, endPtr)
}
#endif
/// Enumerates the contents of the directory passing the name of each contained element to the provided callback.
/// - parameter closure: The callback which will receive each entry's name
/// - throws: `PerfectError.FileError`
public func forEachEntry(closure: (String) throws -> ()) throws {
guard let dir = opendir(realPath) else {
try ThrowFileError()
}
defer { closedir(dir) }
var ent = dirent()
let entPtr = UnsafeMutablePointer<UnsafeMutablePointer<dirent>?>.allocate(capacity: 1)
defer { entPtr.deallocate() }
while readDir(dir, &ent, entPtr) == 0 && entPtr.pointee != nil {
let name = ent.d_name
#if os(Linux)
let nameLen = 1024
#else
let nameLen = ent.d_namlen
#endif
let type = ent.d_type
var nameBuf = [CChar]()
let mirror = Mirror(reflecting: name)
let childGen = mirror.children.makeIterator()
for _ in 0..<nameLen {
guard let (_, elem) = childGen.next() else {
break
}
guard let elemI = elem as? Int8, elemI != 0 else {
break
}
nameBuf.append(elemI)
}
nameBuf.append(0)
if let name = String(validatingUTF8: nameBuf), !(name == "." || name == "..") {
if Int32(type) == Int32(DT_DIR) {
try closure(name + "/")
} else {
try closure(name)
}
}
}
}
}
================================================
FILE: Sources/PerfectLib/File.swift
================================================
//
// File.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/7/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import LinuxBridge
// !FIX! these are obviously sketchy
// I hope SwiftGlibc to eventually include these
// Otherwise, export them from LinuxBridge
import var Glibc.S_IRUSR
import var Glibc.S_IWUSR
import var Glibc.S_IXUSR
import var Glibc.S_IFMT
import var Glibc.S_IFREG
import var Glibc.S_IFDIR
import var Glibc.S_IFLNK
let S_IRGRP = (S_IRUSR >> 3)
let S_IWGRP = (S_IWUSR >> 3)
let S_IXGRP = (S_IXUSR >> 3)
let S_IRWXU = (__S_IREAD|__S_IWRITE|__S_IEXEC)
let S_IRWXG = (S_IRWXU >> 3)
let S_IRWXO = (S_IRWXG >> 3)
let S_IROTH = (S_IRGRP >> 3)
let S_IWOTH = (S_IWGRP >> 3)
let S_IXOTH = (S_IXGRP >> 3)
let SEEK_CUR: Int32 = 1
let EXDEV = Int32(18)
let EACCES = Int32(13)
let EAGAIN = Int32(11)
let F_OK: Int32 = 0
#else
import Darwin
#endif
let fileCopyBufferSize = 16384
/// Provides access to a file on the local file system
public class File {
/// The underlying file system descriptor.
public var fd = -1
var internalPath = ""
/// Checks that the file exists on the file system
/// - returns: True if the file exists or false otherwise
public var exists: Bool {
return access(internalPath, F_OK) != -1
}
/// Returns true if the file has been opened
public var isOpen: Bool {
return fd != -1
}
/// Returns the file's path
public var path: String { return internalPath }
/// Returns the file path. If the file is a symbolic link, the link will be resolved.
public var realPath: String {
let maxPath = 2048
guard isLink else {
return internalPath
}
var ary = [UInt8](repeating: 0, count: maxPath)
let res: Int = ary.withUnsafeMutableBytes {
guard let p = $0.bindMemory(to: Int8.self).baseAddress else {
return -1
}
return readlink(internalPath, p, maxPath)
}
guard res != -1 else {
return internalPath
}
let trailPath = String(cString: ary)
let lastChar = trailPath[trailPath.startIndex]
guard lastChar != "/" && lastChar != "." else {
return trailPath
}
return internalPath.deletingLastFilePathComponent + "/" + trailPath
}
/// Returns the modification date for the file in the standard UNIX format of seconds since 1970/01/01 00:00:00 GMT
/// - returns: The date as Int
public var modificationTime: Int {
var st = stat()
let res = isOpen ? fstat(Int32(fd), &st) : stat(internalPath, &st)
guard res == 0 else {
return Int.max
}
#if os(Linux)
return Int(st.st_mtim.tv_sec)
#else
return Int(st.st_mtimespec.tv_sec)
#endif
}
static func resolveTilde(inPath: String) -> String {
#if !os(iOS) && !os(tvOS)
if !inPath.isEmpty && inPath[inPath.startIndex] == "~" {
var wexp = wordexp_t()
guard 0 == wordexp(inPath, &wexp, 0),
let we_wordv = wexp.we_wordv else {
return inPath
}
defer {
wordfree(&wexp)
}
if let resolved = we_wordv[0], let pth = String(validatingUTF8: resolved) {
return pth
}
}
#endif
return inPath
}
/// Create a file object given a path and open mode
/// - parameter path: Path to the file which will be accessed
/// - parameter fd: The file descriptor, if any, for an already opened file
public init(_ path: String, fd: Int32 = -1) {
internalPath = File.resolveTilde(inPath: path)
self.fd = Int(fd)
}
deinit {
close()
}
/// Closes the file if it had been opened
public func close() {
if fd != -1 {
#if os(Linux)
_ = SwiftGlibc.close(CInt(fd))
#else
_ = Darwin.close(CInt(fd))
#endif
fd = -1
}
}
/// Resets the internal file descriptor, leaving the file opened if it had been.
public func abandon() {
fd = -1
}
}
public extension File {
/// The open mode for the file.
enum OpenMode {
/// Opens the file for read-only access.
case read
/// Opens the file for write-only access, creating the file if it did not exist.
case write
/// Opens the file for read-write access, creating the file if it did not exist.
case readWrite
/// Opens the file for read-write access, creating the file if it did not exist and moving the file marker to the end.
case append
/// Opens the file for read-write access, creating the file if it did not exist and setting the file's size to zero.
case truncate
var toMode: Int {
switch self {
case .read: return Int(O_RDONLY)
case .write: return Int(O_WRONLY|O_CREAT)
case .readWrite: return Int(O_RDWR|O_CREAT)
case .append: return Int(O_RDWR|O_APPEND|O_CREAT)
case .truncate: return Int(O_RDWR|O_TRUNC|O_CREAT)
}
}
}
/// A file or directory access permission value.
struct PermissionMode: OptionSet {
/// File system mode type.
public typealias Mode = mode_t
/// The raw mode.
public let rawValue: Mode
/// Create a permission mode with a raw value.
public init(rawValue: Mode) {
self.rawValue = rawValue
}
#if os(Linux)
init(rawValue: Int32) {
self.init(rawValue: UInt32(rawValue))
}
#endif
/// Readable by user.
public static let readUser = PermissionMode(rawValue: S_IRUSR)
/// Writable by user.
public static let writeUser = PermissionMode(rawValue: S_IWUSR)
/// Executable by user.
public static let executeUser = PermissionMode(rawValue: S_IXUSR)
/// Readable by group.
public static let readGroup = PermissionMode(rawValue: S_IRGRP)
/// Writable by group.
public static let writeGroup = PermissionMode(rawValue: S_IWGRP)
/// Executable by group.
public static let executeGroup = PermissionMode(rawValue: S_IXGRP)
/// Readable by others.
public static let readOther = PermissionMode(rawValue: S_IROTH)
/// Writable by others.
public static let writeOther = PermissionMode(rawValue: S_IWOTH)
/// Executable by others.
public static let executeOther = PermissionMode(rawValue: S_IXOTH)
/// Read, write, execute by user.
public static let rwxUser: PermissionMode = [.readUser, .writeUser, .executeUser]
/// Read, write by user and group.
public static let rwUserGroup: PermissionMode = [.readUser, .writeUser, .readGroup, .writeGroup]
/// Read, execute by group.
public static let rxGroup: PermissionMode = [.readGroup, .executeGroup]
/// Read, execute by other.
public static let rxOther: PermissionMode = [.readOther, .executeOther]
}
/// Opens the file using the given mode.
/// - throws: `PerfectError.FileError`
func open(_ mode: OpenMode = .read, permissions: PermissionMode = .rwUserGroup) throws {
if fd != -1 {
close()
}
#if os(Linux)
let openFd = linux_open(internalPath, CInt(mode.toMode), permissions.rawValue)
#else
let openFd = Darwin.open(internalPath, CInt(mode.toMode), permissions.rawValue)
#endif
guard openFd != -1 else {
try ThrowFileError()
}
fd = Int(openFd)
}
}
public extension File {
/// The current file read/write position.
var marker: Int {
/// Returns the value of the file's current position marker
get {
if isOpen {
return Int(lseek(Int32(fd), 0, SEEK_CUR))
}
return 0
}
/// Sets the file's position marker given the value as measured from the begining of the file.
set {
lseek(Int32(fd), off_t(newValue), SEEK_SET)
}
}
}
public extension File {
/// Closes and deletes the file
func delete() {
close()
unlink(path)
}
/// Moves the file to the new location, optionally overwriting any existing file
/// - parameter path: The path to move the file to
/// - parameter overWrite: Indicates that any existing file at the destination path should first be deleted
/// - returns: Returns a new file object representing the new location
/// - throws: `PerfectError.FileError`
func moveTo(path: String, overWrite: Bool = false) throws -> File {
let destFile = File(path)
if destFile.exists {
guard overWrite else {
throw PerfectError.fileError(-1, "Can not overwrite existing file")
}
destFile.delete()
}
close()
let res = rename(self.path, path)
if res == 0 {
return destFile
}
if errno == EXDEV {
_ = try copyTo(path: path, overWrite: overWrite)
delete()
return destFile
}
try ThrowFileError()
}
/// Copies the file to the new location, optionally overwriting any existing file
/// - parameter path: The path to copy the file to
/// - parameter overWrite: Indicates that any existing file at the destination path should first be deleted
/// - returns: Returns a new file object representing the new location
/// - throws: `PerfectError.FileError`
@discardableResult
func copyTo(path pth: String, overWrite: Bool = false) throws -> File {
let destFile = File(pth)
if destFile.exists {
guard overWrite else {
throw PerfectError.fileError(-1, "Can not overwrite existing file")
}
destFile.delete()
}
let wasOpen = isOpen
let oldMarker = marker
if !wasOpen {
try open()
} else {
_ = marker = 0
}
defer {
if !wasOpen {
close()
} else {
_ = marker = oldMarker
}
}
try destFile.open(.truncate)
var bytes = try readSomeBytes(count: fileCopyBufferSize)
while bytes.count > 0 {
try destFile.write(bytes: bytes)
bytes = try readSomeBytes(count: fileCopyBufferSize)
}
destFile.close()
return destFile
}
}
public extension File {
/// Returns the size of the file in bytes
var size: Int {
var st = stat()
let statRes = isOpen ? fstat(Int32(fd), &st) : stat(internalPath, &st)
guard statRes != -1 else {
return 0
}
return Int(st.st_size)
}
/// Returns true if the file is actually a directory
var isDir: Bool {
var st = stat()
let statRes = isOpen ? fstat(Int32(fd), &st) : stat(internalPath, &st)
guard statRes != -1 else {
return false
}
let mode = st.st_mode
return (Int32(mode) & Int32(S_IFMT)) == Int32(S_IFDIR)
}
/// Returns the UNIX style permissions for the file
var perms: PermissionMode {
get {
var st = stat()
let statRes = isOpen ? fstat(Int32(fd), &st) : stat(internalPath, &st)
guard statRes != -1 else {
return PermissionMode(rawValue: PermissionMode.Mode(0))
}
let mode = st.st_mode
return PermissionMode(rawValue: mode_t(Int32(mode) ^ Int32(S_IFMT)))
}
set {
_ = chmod(internalPath, newValue.rawValue)
}
}
}
public extension File {
/// Returns true if the file is a symbolic link
var isLink: Bool {
var st = stat()
let statRes = lstat(internalPath, &st)
guard statRes != -1 else {
return false
}
let mode = st.st_mode
return (Int32(mode) & Int32(S_IFMT)) == Int32(S_IFLNK)
}
/// Create a symlink from the target to the destination.
@discardableResult
func linkTo(path: String, overWrite: Bool = false) throws -> File {
let destFile = File(path)
if destFile.exists {
guard overWrite else {
throw PerfectError.fileError(-1, "Can not overwrite existing file")
}
destFile.delete()
}
let res = symlink(self.path, path)
if res == 0 {
return File(path)
}
try ThrowFileError()
}
}
public extension File {
/// Reads up to the indicated number of bytes from the file
/// - parameter count: The maximum number of bytes to read
/// - returns: The bytes read as an array of UInt8. May have a count of zero.
/// - throws: `PerfectError.FileError`
func readSomeBytes(count: Int) throws -> [UInt8] {
if !isOpen {
try open()
}
func sizeOr(_ value: Int) -> Int {
var st = stat()
let statRes = isOpen ? fstat(Int32(fd), &st) : stat(internalPath, &st)
guard statRes != -1 else {
return 0
}
if (Int32(st.st_mode) & Int32(S_IFMT)) == Int32(S_IFREG) {
return Int(st.st_size)
}
return value
}
let bSize = min(count, sizeOr(count))
var ary = [UInt8](repeating: 0, count: bSize)
let readCount = ary.withUnsafeMutableBytes {
read(CInt(fd), $0.bindMemory(to: Int8.self).baseAddress, bSize)
}
guard readCount >= 0 else {
try ThrowFileError()
}
if readCount < bSize {
ary.removeLast(bSize - readCount)
}
return ary
}
/// Reads the entire file as a string
func readString() throws -> String {
let bytes = try readSomeBytes(count: size)
return UTF8Encoding.encode(bytes: bytes)
}
}
public extension File {
/// Writes the string to the file using UTF-8 encoding
/// - parameter s: The string to write
/// - returns: Returns the number of bytes which were written
/// - throws: `PerfectError.FileError`
@discardableResult
func write(string: String) throws -> Int {
return try write(bytes: Array(string.utf8))
}
/// Write the indicated bytes to the file
/// - parameter bytes: The array of UInt8 to write.
/// - parameter dataPosition: The offset within `bytes` at which to begin writing.
/// - parameter length: The number of bytes to write.
/// - throws: `PerfectError.FileError`
@discardableResult
func write(bytes: [UInt8], dataPosition: Int = 0, length: Int = Int.max) throws -> Int {
let len = min(bytes.count - dataPosition, length)
#if os(Linux)
let wrote = bytes.withUnsafeBytes {
SwiftGlibc.write(Int32(fd), $0.bindMemory(to: Int8.self).baseAddress?.advanced(by: dataPosition), len)
}
#else
let wrote = bytes.withUnsafeBytes {
Darwin.write(Int32(fd), $0.bindMemory(to: Int8.self).baseAddress?.advanced(by: dataPosition), len)
}
#endif
guard wrote == len else {
try ThrowFileError()
}
return wrote
}
}
public extension File {
/// Attempts to place an advisory lock starting from the current position marker up to the indicated byte count. This function will block the current thread until the lock can be performed.
/// - parameter byteCount: The number of bytes to lock
/// - throws: `PerfectError.FileError`
func lock(byteCount: Int) throws {
if !isOpen {
try open(.write)
}
let res = lockf(Int32(fd), F_LOCK, off_t(byteCount))
guard res == 0 else {
try ThrowFileError()
}
}
/// Unlocks the number of bytes starting from the current position marker up to the indicated byte count.
/// - parameter byteCount: The number of bytes to unlock
/// - throws: `PerfectError.FileError`
func unlock(byteCount: Int) throws {
if !isOpen {
try open(.write)
}
let res = lockf(Int32(fd), F_ULOCK, off_t(byteCount))
guard res == 0 else {
try ThrowFileError()
}
}
/// Attempts to place an advisory lock starting from the current position marker up to the indicated byte count. This function will throw an exception if the file is already locked, but will not block the current thread.
/// - parameter byteCount: The number of bytes to lock
/// - throws: `PerfectError.FileError`
func tryLock(byteCount: Int) throws {
if !isOpen {
try open(.write)
}
let res = lockf(Int32(fd), F_TLOCK, off_t(byteCount))
guard res == 0 else {
try ThrowFileError()
}
}
/// Tests if the indicated bytes are locked
/// - parameter byteCount: The number of bytes to test
/// - returns: True if the file is locked
/// - throws: `PerfectError.FileError`
func testLock(byteCount: Int) throws -> Bool {
if !isOpen {
try open(.write)
}
let res = Int(lockf(Int32(fd), F_TEST, off_t(byteCount)))
guard res == 0 || res == Int(EACCES) || res == Int(EAGAIN) else {
try ThrowFileError()
}
return res != 0
}
}
// Subclass to represent a file which can not be closed
private final class UnclosableFile : File {
override init(_ path: String, fd: Int32) {
super.init(path, fd: fd)
}
override func close() {
// nothing
}
}
/// A temporary, randomly named file.
public final class TemporaryFile: File {
/// Create a temporary file, usually in the system's /tmp/ directory
/// - parameter withPrefix: The prefix for the temporary file's name. Random characters will be appended to the file's eventual name.
public convenience init(withPrefix: String) {
let template = withPrefix + "XXXXXX"
let utf8 = template.utf8
let name = UnsafeMutablePointer<Int8>.allocate(capacity: utf8.count + 1)
var i = utf8.startIndex
for index in 0..<utf8.count {
name[index] = Int8(utf8[i])
i = utf8.index(after: i)
}
name[utf8.count] = 0
let fd = mkstemp(name)
let tmpFileName = String(validatingUTF8: name)!
name.deallocate()
self.init(tmpFileName, fd: fd)
}
}
/// This file can be used to write to standard in
public var fileStdin: File {
return UnclosableFile("/dev/stdin", fd: STDIN_FILENO)
}
/// This file can be used to write to standard out
public var fileStdout: File {
return UnclosableFile("/dev/stdout", fd: STDOUT_FILENO)
}
/// This file can be used to write to standard error
public var fileStderr: File {
return UnclosableFile("/dev/stderr", fd: STDERR_FILENO)
}
================================================
FILE: Sources/PerfectLib/JSONConvertible.swift
================================================
//
// JSONConvertible.swift
// PerfectLib
//
// Created by Kyle Jessup on 2016-01-21.
// Copyright © 2016 Treefrog. All rights reserved.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import SwiftGlibc
#else
import Darwin
#endif
/// This non-instantiable object serves as an access point to a registry for JSONConvertibleObjects
/// A JSONConvertibleObject is a custom class or struct which can be converted to and from JSON.
public class JSONDecoding {
private init() {}
/// The key used in JSON data to indicate the type of custom object which was converted.
public static let objectIdentifierKey = "_jsonobjid"
/// Function which returns a new instance of a custom object which will have its members set based on the JSON data.
public typealias JSONConvertibleObjectCreator = () -> JSONConvertibleObject
static private var jsonDecodableRegistry = [String:JSONConvertibleObjectCreator]()
/// Register a custom object to be JSON encoded/decoded.
static public func registerJSONDecodable(name nam: String, creator: @escaping JSONConvertibleObjectCreator) {
JSONDecoding.jsonDecodableRegistry[nam] = creator
}
/// Instantiate a custom object based on the JSON data.
/// The system will use the `JSONDecoding.objectIdentifierKey` key to locate the custom object creator.
static public func createJSONConvertibleObject(values:[String:Any]) -> JSONConvertibleObject? {
guard let objkey = values[JSONDecoding.objectIdentifierKey] as? String else {
return nil
}
return JSONDecoding.createJSONConvertibleObject(name: objkey, values: values)
}
/// Instantiate a custom object based on the JSON data.
/// The system will use the `name` parameter to locate the custom object creator.
static public func createJSONConvertibleObject(name: String, values:[String:Any]) -> JSONConvertibleObject? {
guard let creator = JSONDecoding.jsonDecodableRegistry[name] else {
return nil
}
let jsonObj = creator()
jsonObj.setJSONValues(values)
return jsonObj
}
}
/// Protocol for an object which can be converted into JSON text.
public protocol JSONConvertible {
/// Returns the JSON encoded String for any JSONConvertible type.
func jsonEncodedString() throws -> String
}
// !FIX! changed this to be a class due to Linux protocols failing 'as' tests.
// revisit this
/// Base for a custom object which can be converted to and from JSON.
open class JSONConvertibleObject: JSONConvertible {
/// Default initializer.
public init() {}
/// Get the JSON keys/value.
open func setJSONValues(_ values:[String:Any]) {}
/// Set the object properties based on the JSON keys/values.
open func getJSONValues() -> [String:Any] { return [String:Any]() }
/// Encode the object into JSON text
open func jsonEncodedString() throws -> String {
return try getJSONValues().jsonEncodedString()
}
}
/// Get the JSON keys/values from a custom object.
public extension JSONConvertibleObject {
/// Get a named value from the Dictionary converting to the given type with a default value.
func getJSONValue<T: JSONConvertible>(named namd: String, from:[String:Any], defaultValue: T) -> T {
let f = from[namd]
if let v = f as? T {
return v
}
return defaultValue
}
}
/// An error occurring during JSON conversion.
public enum JSONConversionError: Error {
/// The object did not suppport JSON conversion.
case notConvertible(Any?)
/// A provided key was not a String.
case invalidKey(Any)
/// The JSON text contained a syntax error.
case syntaxError
}
private let jsonBackSlash = UnicodeScalar(UInt32(92))!
private let jsonBackSpace = UnicodeScalar(UInt32(8))!
private let jsonFormFeed = UnicodeScalar(UInt32(12))!
private let jsonLF = UnicodeScalar(UInt32(10))!
private let jsonCR = UnicodeScalar(UInt32(13))!
private let jsonTab = UnicodeScalar(UInt32(9))!
private let jsonQuoteDouble = UnicodeScalar(UInt32(34))!
private let jsonOpenObject = UnicodeScalar(UInt32(123))!
private let jsonOpenArray = UnicodeScalar(UInt32(91))!
private let jsonCloseObject = UnicodeScalar(UInt32(125))!
private let jsonCloseArray = UnicodeScalar(UInt32(93))!
private let jsonWhiteSpace = UnicodeScalar(UInt32(32))!
private let jsonColon = UnicodeScalar(UInt32(58))!
private let jsonComma = UnicodeScalar(UInt32(44))!
private let highSurrogateLowerBound = UInt32(strtoul("d800", nil, 16))
private let highSurrogateUpperBound = UInt32(strtoul("dbff", nil, 16))
private let lowSurrogateLowerBound = UInt32(strtoul("dc00", nil, 16))
private let lowSurrogateUpperBound = UInt32(strtoul("dfff", nil, 16))
private let surrogateStep = UInt32(strtoul("400", nil, 16))
private let surrogateBase = UInt32(strtoul("10000", nil, 16))
/// This is a stand-in for a JSON null.
/// May be produced when decoding.
public struct JSONConvertibleNull: JSONConvertible {
public func jsonEncodedString() throws -> String {
return "null"
}
}
extension String: JSONConvertible {
/// Convert a String into JSON text
public func jsonEncodedString() throws -> String {
var s = "\""
for uchar in unicodeScalars {
switch(uchar) {
case jsonBackSlash:
s.append("\\\\")
case jsonQuoteDouble:
s.append("\\\"")
case jsonBackSpace:
s.append("\\b")
case jsonFormFeed:
s.append("\\f")
case jsonLF:
s.append("\\n")
case jsonCR:
s.append("\\r")
case jsonTab:
s.append("\\t")
default:
s.append(String(uchar))
}
}
s.append("\"")
return s
}
}
extension Int: JSONConvertible {
/// Convert an Int into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension UInt: JSONConvertible {
/// Convert a UInt into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Int8: JSONConvertible {
/// Convert an Int8 into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Int16: JSONConvertible {
/// Convert an Int16 into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Int32: JSONConvertible {
/// Convert an Int into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Int64: JSONConvertible {
/// Convert an Int into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension UInt8: JSONConvertible {
/// Convert an UInt8 into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension UInt16: JSONConvertible {
/// Convert an UInt16 into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension UInt32: JSONConvertible {
/// Convert an Int into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension UInt64: JSONConvertible {
/// Convert an Int into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Double: JSONConvertible {
/// Convert a Double into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Float: JSONConvertible {
/// Convert a Float into JSON text.
public func jsonEncodedString() throws -> String {
return String(self)
}
}
extension Optional: JSONConvertible {
/// Convert an Optional into JSON text.
public func jsonEncodedString() throws -> String {
if self == nil {
return "null"
} else if let v = self! as? JSONConvertible {
return try v.jsonEncodedString()
}
throw JSONConversionError.notConvertible(self)
}
}
extension Bool: JSONConvertible {
/// Convert a Bool into JSON text.
public func jsonEncodedString() throws -> String {
if true == self {
return "true"
}
return "false"
}
}
// !FIX! Downcasting to protocol does not work on Linux
// Not sure if this is intentional, or a bug.
func jsonEncodedStringWorkAround(_ o: Any) throws -> String {
switch o {
case let jsonAble as JSONConvertibleObject: // as part of Linux work around
return try jsonAble.jsonEncodedString()
case let jsonAble as JSONConvertible:
return try jsonAble.jsonEncodedString()
case let jsonAble as String:
return try jsonAble.jsonEncodedString()
case let jsonAble as Int:
return try jsonAble.jsonEncodedString()
case let jsonAble as Int8:
return try jsonAble.jsonEncodedString()
case let jsonAble as Int16:
return try jsonAble.jsonEncodedString()
case let jsonAble as Int32:
return try jsonAble.jsonEncodedString()
case let jsonAble as Int64:
return try jsonAble.jsonEncodedString()
case let jsonAble as UInt:
return try jsonAble.jsonEncodedString()
case let jsonAble as UInt8:
return try jsonAble.jsonEncodedString()
case let jsonAble as UInt16:
return try jsonAble.jsonEncodedString()
case let jsonAble as UInt32:
return try jsonAble.jsonEncodedString()
case let jsonAble as UInt64:
return try jsonAble.jsonEncodedString()
case let jsonAble as Double:
return try jsonAble.jsonEncodedString()
case let jsonAble as Float:
return try jsonAble.jsonEncodedString()
case let jsonAble as Bool:
return try jsonAble.jsonEncodedString()
case let jsonAble as [Any]:
return try jsonAble.jsonEncodedString()
case let jsonAble as [[String:Any]]:
return try jsonAble.jsonEncodedString()
case let jsonAble as [String:Any]:
return try jsonAble.jsonEncodedString()
default:
throw JSONConversionError.notConvertible(o)
}
}
extension Array: JSONConvertible {
/// Convert an Array into JSON text.
public func jsonEncodedString() throws -> String {
var s = "["
var first = true
for v in self {
if !first {
s.append(",")
} else {
first = false
}
s.append(try jsonEncodedStringWorkAround(v))
}
s.append("]")
return s
}
}
extension Dictionary: JSONConvertible {
/// Convert a Dictionary into JSON text.
public func jsonEncodedString() throws -> String {
var s = "{"
var first = true
for (k, v) in self {
guard let strKey = k as? String else {
throw JSONConversionError.invalidKey(k)
}
if !first {
s.append(",")
} else {
first = false
}
s.append(try strKey.jsonEncodedString())
s.append(":")
s.append(try jsonEncodedStringWorkAround(v))
}
s.append("}")
return s
}
}
extension String {
/// Decode the object represented by the JSON text.
public func jsonDecode() throws -> JSONConvertible {
let state = JSONDecodeState()
state.g = unicodeScalars.makeIterator()
let o = try state.readObject()
if let _ = o as? JSONDecodeState.EOF {
throw JSONConversionError.syntaxError
}
return o
}
}
private class JSONDecodeState {
struct EOF: JSONConvertible {
func jsonEncodedString() throws -> String { return "" }
}
var g = String().unicodeScalars.makeIterator()
var pushBack: UnicodeScalar?
func movePastWhite() {
while let c = next() {
if !c.isWhiteSpace() {
pushBack = c
break
}
}
}
func readObject() throws -> JSONConvertible {
movePastWhite()
guard let c = next() else {
return EOF()
}
switch(c) {
case jsonOpenArray:
var a = [Any]()
movePastWhite()
guard let c = next() else {
throw JSONConversionError.syntaxError
}
if c != jsonCloseArray {
pushBack = c
while true {
a.append(try readObject())
movePastWhite()
guard let c = next() else {
throw JSONConversionError.syntaxError
}
if c == jsonCloseArray {
break
}
if c != jsonComma {
throw JSONConversionError.syntaxError
}
}
}
return a
case jsonOpenObject:
var d = [String:Any]()
movePastWhite()
guard let c = next() else {
throw JSONConversionError.syntaxError
}
if c != jsonCloseObject {
pushBack = c
while true {
guard let key = try readObject() as? String else {
throw JSONConversionError.syntaxError
}
movePastWhite()
guard let c = next() else {
throw JSONConversionError.syntaxError
}
guard c == jsonColon else {
throw JSONConversionError.syntaxError
}
movePastWhite()
d[key] = try readObject()
do {
movePastWhite()
guard let c = next() else {
throw JSONConversionError.syntaxError
}
if c == jsonCloseObject {
break
}
if c != jsonComma {
throw JSONConversionError.syntaxError
}
}
}
}
if let objid = d[JSONDecoding.objectIdentifierKey] as? String {
if let o = JSONDecoding.createJSONConvertibleObject(name: objid, values: d) {
return o
}
}
return d
case jsonQuoteDouble:
return try readString()
default:
if c.isWhiteSpace() {
// nothing
} else if c.isDigit() || c == "-" || c == "+" {
return try readNumber(firstChar: c)
} else if c == "t" || c == "T" {
return try readTrue()
} else if c == "f" || c == "F" {
return try readFalse()
} else if c == "n" || c == "N" {
try readNull()
return JSONConvertibleNull()
}
}
throw JSONConversionError.syntaxError
}
func next() -> UnicodeScalar? {
if pushBack != nil {
let c = pushBack!
pushBack = nil
return c
}
return g.next()
}
// the opening quote has been read
func readString() throws -> String {
var next = self.next()
var esc = false
var s = ""
while let c = next {
if esc {
switch(c) {
case jsonBackSlash:
s.append(String(jsonBackSlash))
case jsonQuoteDouble:
s.append(String(jsonQuoteDouble))
case "b":
s.append(String(jsonBackSpace))
case "f":
s.append(String(jsonFormFeed))
case "n":
s.append(String(jsonLF))
case "r":
s.append(String(jsonCR))
case "t":
s.append(String(jsonTab))
case "u":
var hexStr = ""
for _ in 1...4 {
next = self.next()
guard let hexC = next else {
throw JSONConversionError.syntaxError
}
guard hexC.isHexDigit() else {
throw JSONConversionError.syntaxError
}
hexStr.append(String(hexC))
}
var uint32Value = UInt32(strtoul(hexStr, nil, 16))
// if unicode is a high/low surrogate, it can't be converted directly by UnicodeScalar
// if it's a low surrogate (not expected), throw error
if case lowSurrogateLowerBound...lowSurrogateUpperBound = uint32Value {
throw JSONConversionError.syntaxError
}
// if it's a high surrogate, find the low surrogate which the next unicode is supposed to be, then calculate the pair
if case highSurrogateLowerBound...highSurrogateUpperBound = uint32Value {
let highSurrogateValue = uint32Value
guard self.next() == jsonBackSlash else {
throw JSONConversionError.syntaxError
}
guard self.next() == "u" else {
throw JSONConversionError.syntaxError
}
var lowSurrogateHexStr = ""
for _ in 1...4 {
next = self.next()
guard let hexC = next else {
throw JSONConversionError.syntaxError
}
guard hexC.isHexDigit() else {
throw JSONConversionError.syntaxError
}
lowSurrogateHexStr.append(String(hexC))
}
let lowSurrogateValue = UInt32(strtoul(lowSurrogateHexStr, nil, 16))
uint32Value = ( highSurrogateValue - highSurrogateLowerBound ) * surrogateStep + ( lowSurrogateValue - lowSurrogateLowerBound ) + surrogateBase
}
if let result = UnicodeScalar(uint32Value) {
s.append(String(Character(result)))
}
default:
s.append(String(c))
}
esc = false
} else if c == jsonBackSlash {
esc = true
} else if c == jsonQuoteDouble {
return s
} else {
s.append(String(c))
}
next = self.next()
}
throw JSONConversionError.syntaxError
}
func readNumber(firstChar first: UnicodeScalar) throws -> JSONConvertible {
var s = ""
var needPeriod = true, needExp = true
s.append(String(Character(first)))
if first == "." {
needPeriod = false
}
var next = self.next()
var last = first
while let c = next {
if c.isDigit() {
s.append(String(c))
} else if c == "." && !needPeriod {
break
} else if (c == "e" || c == "E") && !needExp {
break
} else if c == "." {
needPeriod = false
s.append(String(c))
} else if c == "e" || c == "E" {
needExp = false
s.append(String(c))
next = self.next()
if next != nil && (next! == "-" || next! == "+") {
s.append(String(next!))
} else {
pushBack = next!
}
} else if last.isDigit() {
pushBack = c
if needPeriod && needExp {
return Int(s) ?? s
}
return Double(s)!
} else {
break
}
last = c
next = self.next()
}
throw JSONConversionError.syntaxError
}
func readTrue() throws -> Bool {
var next = self.next()
if next != "r" && next != "R" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "u" && next != "U" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "e" && next != "E" {
throw JSONConversionError.syntaxError
}
next = self.next()
guard next != nil && !next!.isAlphaNum() else {
throw JSONConversionError.syntaxError
}
pushBack = next!
return true
}
func readFalse() throws -> Bool {
var next = self.next()
if next != "a" && next != "A" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "l" && next != "L" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "s" && next != "S" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "e" && next != "E" {
throw JSONConversionError.syntaxError
}
next = self.next()
guard next != nil && !next!.isAlphaNum() else {
throw JSONConversionError.syntaxError
}
pushBack = next!
return false
}
func readNull() throws {
var next = self.next()
if next != "u" && next != "U" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "l" && next != "L" {
throw JSONConversionError.syntaxError
}
next = self.next()
if next != "l" && next != "L" {
throw JSONConversionError.syntaxError
}
next = self.next()
guard next != nil && !next!.isAlphaNum() else {
throw JSONConversionError.syntaxError
}
pushBack = next!
}
}
================================================
FILE: Sources/PerfectLib/Log.swift
================================================
//
// LogManager.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/21/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import SwiftGlibc
import LinuxBridge
#else
import Darwin
import os
#endif
/// Placeholder functions for logging system
public protocol Logger {
func debug(message: String, _ even: Bool)
func info(message: String, _ even: Bool)
func warning(message: String, _ even: Bool)
func error(message: String, _ even: Bool)
func critical(message: String, _ even: Bool)
func terminal(message: String, _ even: Bool)
}
public struct ConsoleLogger: Logger {
public init(){}
public func debug(message: String, _ even: Bool) {
print((even ? "[DBG] " : "[DBG] ") + message)
}
public func info(message: String, _ even: Bool) {
print((even ? "[INFO] " : "[INFO] ") + message)
}
public func warning(message: String, _ even: Bool) {
print((even ? "[WARN] " : "[WARN] ") + message)
}
public func error(message: String, _ even: Bool) {
print((even ? "[ERR] " : "[ERR] ") + message)
}
public func critical(message: String, _ even: Bool) {
print((even ? "[CRIT] " : "[CRIT] ") + message)
}
public func terminal(message: String, _ even: Bool) {
print((even ? "[TERM] " : "[TERM] ") + message)
}
}
public struct SysLogger: Logger {
let consoleEcho = ConsoleLogger()
public init(){}
func syslog(priority: Int32, _ args: CVarArg...) {
#if os(Linux)// || os(macOS)
withVaList(args) {
vsyslog(priority, "%s", $0)
}
#else
// nerdo: Using unified logging (https://developer.apple.com/documentation/os/logging)
// os_log isn't very well documented... but this seems like a step in the right direction.
let osLogType: OSLogType
switch priority {
case 0, 1, 2:
osLogType = .fault
case 3:
osLogType = .error
case 4, 5, 6:
osLogType = .info
case 7:
osLogType = .debug
default:
osLogType = .default
}
os_log("%s", log: .default, type: osLogType, args)
#endif
}
public func debug(message: String, _ even: Bool) {
consoleEcho.debug(message: message, even)
message.withCString {
f in
syslog(priority: LOG_DEBUG, f)
}
}
public func info(message: String, _ even: Bool) {
consoleEcho.info(message: message, even)
message.withCString {
f in
syslog(priority: LOG_INFO, f)
}
}
public func warning(message: String, _ even: Bool) {
consoleEcho.warning(message: message, even)
message.withCString {
f in
syslog(priority: LOG_WARNING, f)
}
}
public func error(message: String, _ even: Bool) {
consoleEcho.error(message: message, even)
message.withCString {
f in
syslog(priority: LOG_ERR, f)
}
}
public func critical(message: String, _ even: Bool) {
consoleEcho.critical(message: message, even)
message.withCString {
f in
syslog(priority: LOG_CRIT, f)
}
}
public func terminal(message: String, _ even: Bool) {
consoleEcho.terminal(message: message, even)
message.withCString {
f in
syslog(priority: LOG_EMERG, f)
}
}
}
/// Placeholder functions for logging system
public struct Log {
private init(){}
public static var logger: Logger = ConsoleLogger()
/// Whether or not to even off the log messages
/// If set to true log messages will be inline with each other
public static var even = false
public static func debug(message: @autoclosure () -> String) {
// #if DEBUG
Log.logger.debug(message: message(), even)
// #endif
}
public static func info(message: String, evenIdents: Bool = even) {
Log.logger.info(message: message, evenIdents)
}
public static func warning(message: String, evenIdents: Bool = even) {
Log.logger.warning(message: message, evenIdents)
}
public static func error(message: String, evenIdents: Bool = even) {
Log.logger.error(message: message, evenIdents)
}
public static func critical(message: String, evenIdents: Bool = even) {
Log.logger.critical(message: message, evenIdents)
}
public static func terminal(message: String, evenIdents: Bool = even) -> Never {
Log.logger.terminal(message: message, evenIdents)
fatalError(message)
}
}
================================================
FILE: Sources/PerfectLib/PerfectError.swift
================================================
//
// PerfectError.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/5/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import SwiftGlibc
var errno: Int32 {
return __errno_location().pointee
}
#else
import Darwin
#endif
/// Some but not all of the exception types which may be thrown by the system
public enum PerfectError : Error {
/// A network related error code and message.
case networkError(Int32, String)
/// A file system related error code and message.
case fileError(Int32, String)
/// A OS level error code and message.
case systemError(Int32, String)
/// An API exception error message.
case apiError(String)
}
func ThrowFileError(file: String = #file, function: String = #function, line: Int = #line) throws -> Never {
let err = errno
let msg = String(validatingUTF8: strerror(err))!
// print("FileError: \(err) \(msg)")
throw PerfectError.fileError(err, msg + " \(file) \(function) \(line)")
}
func ThrowSystemError(file: String = #file, function: String = #function, line: Int = #line) throws -> Never {
let err = errno
let msg = String(validatingUTF8: strerror(err))!
// print("SystemError: \(err) \(msg)")
throw PerfectError.systemError(err, msg + " \(file) \(function) \(line)")
}
func ThrowNetworkError(file: String = #file, function: String = #function, line: Int = #line) throws -> Never {
let err = errno
let msg = String(validatingUTF8: strerror(err))!
// print("NetworkError: \(err) \(msg)")
throw PerfectError.networkError(err, msg + " \(file) \(function) \(line)")
}
================================================
FILE: Sources/PerfectLib/PerfectServer.swift
================================================
//
// Perfect.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/5/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import SwiftGlibc
#else
import Darwin
#endif
/// Provides access to various system level features for the process.
/// A static instance of this class is created at startup and all access to this object go through the `PerfectServer.staticPerfectServer` static property.
public struct PerfectServer {
@available(*, deprecated, message: "No longer required to call this")
public static func initializeServices() {
}
/// Switch the current process to run with the permissions of the indicated user
public static func switchTo(userName unam: String) throws {
guard let pw = getpwnam(unam) else {
try ThrowSystemError()
}
let gid = pw.pointee.pw_gid
let uid = pw.pointee.pw_uid
guard 0 == setgid(gid) else {
try ThrowSystemError()
}
guard 0 == setuid(uid) else {
try ThrowSystemError()
}
}
}
================================================
FILE: Sources/PerfectLib/SysProcess.swift
================================================
//
// SysProcess.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/20/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import SwiftGlibc
let WUNTRACED = Int32(2)
let WNOHANG = Int32(1)
public let SIGTERM = Int32(15)
#else
import Darwin
#endif
/// This class permits an external process to be launched given a set of command line arguments and environment variables.
/// The standard in, out and err file streams are made available. The process can be terminated or permitted to be run to completion.
public class SysProcess {
/// The standard in file stream.
public var stdin: File?
/// The standard out file stream.
public var stdout: File?
/// The standard err file stream.
public var stderr: File?
/// The process identifier.
public var pid = pid_t(-1)
/// The process group identifier.
public var gid = pid_t(-1)
/// Initialize the object and launch the process.
/// - parameter cmd: The path to the process which will be launched.
/// - parameter args: An optional array of String arguments which will be given to the process.
/// - parameter env: An optional array of environment variable name and value pairs.
/// - parameter newGroup: Create a new process group for this and all sub-processes. Default is no.
/// - throws: `PerfectError.SystemError`
public init(_ cmd: String, args: [String]?, env: [(String,String)]?, newGroup: Bool = false) throws {
typealias maybeCChar = UnsafeMutablePointer<CChar>?
let cArgsCount = args?.count ?? 0
let cArgs = UnsafeMutablePointer<maybeCChar>.allocate(capacity: cArgsCount + 2)
defer {
cArgs.deinitialize(count: cArgsCount + 2)
cArgs.deallocate()
}
cArgs[0] = strdup(cmd)
cArgs[cArgsCount + 1] = nil
for idx in 0..<cArgsCount {
cArgs[idx+1] = strdup(args![idx])
}
let cEnvCount = env?.count ?? 0
let cEnv = UnsafeMutablePointer<maybeCChar>.allocate(capacity: cEnvCount + 1)
defer {
cEnv.deinitialize(count: cEnvCount + 1)
cEnv.deallocate()
}
cEnv[cEnvCount] = nil
for idx in 0..<cEnvCount {
cEnv[idx] = strdup(env![idx].0 + "=" + env![idx].1)
}
var fSTDIN: [Int32] = [0, 0]
var fSTDOUT: [Int32] = [0, 0]
var fSTDERR: [Int32] = [0, 0]
fSTDIN.withUnsafeMutableBytes {
_ = pipe($0.bindMemory(to: Int32.self).baseAddress)
}
fSTDOUT.withUnsafeMutableBytes {
_ = pipe($0.bindMemory(to: Int32.self).baseAddress)
}
fSTDERR.withUnsafeMutableBytes {
_ = pipe($0.bindMemory(to: Int32.self).baseAddress)
}
#if os(Linux)
var action = posix_spawn_file_actions_t()
var attr = posix_spawnattr_t()
#else
var action = posix_spawn_file_actions_t(nil as OpaquePointer?)
var attr = posix_spawnattr_t(nil as OpaquePointer?)
#endif
// nerdo: This probably causes SysProcess to fail on tvOS, but it doesn't seem to be called in any of the
// basic, core Perfect libraries, so this is probably a non-issue unless it is used directly
#if os(tvOS)
var procPid = pid_t()
var spawnRes = 0
#else
posix_spawn_file_actions_init(&action);
posix_spawn_file_actions_adddup2(&action, fSTDOUT[1], STDOUT_FILENO);
posix_spawn_file_actions_adddup2(&action, fSTDIN[0], STDIN_FILENO);
posix_spawn_file_actions_adddup2(&action, fSTDERR[1], STDERR_FILENO);
posix_spawn_file_actions_addclose(&action, fSTDOUT[0]);
posix_spawn_file_actions_addclose(&action, fSTDIN[0]);
posix_spawn_file_actions_addclose(&action, fSTDERR[0]);
posix_spawn_file_actions_addclose(&action, fSTDOUT[1]);
posix_spawn_file_actions_addclose(&action, fSTDIN[1]);
posix_spawn_file_actions_addclose(&action, fSTDERR[1]);
posix_spawnattr_init(&attr)
if newGroup {
// By default, all flags are unset after the init call
// so no need to OR in the existing flags.
posix_spawnattr_setflags(&attr, Int16(POSIX_SPAWN_SETPGROUP))
}
var procPid = pid_t()
let spawnRes = posix_spawnp(&procPid, cmd, &action, &attr, cArgs, cEnv)
posix_spawn_file_actions_destroy(&action)
posix_spawnattr_destroy(&attr)
for idx in 0..<cArgsCount {
free(cArgs[idx])
}
for idx in 0..<cEnvCount {
free(cEnv[idx])
}
#endif
#if os(Linux)
// On Ubuntu the getpgid(pid_t) call does not seem to work.
// However, the Posix standard says that the pgid is the same
// as the pid of the first process spawned, so we can just use that.
gid = procPid
_ = SwiftGlibc.close(fSTDIN[0])
_ = SwiftGlibc.close(fSTDOUT[1])
_ = SwiftGlibc.close(fSTDERR[1])
if spawnRes != 0 {
_ = SwiftGlibc.close(fSTDIN[1])
_ = SwiftGlibc.close(fSTDOUT[0])
_ = SwiftGlibc.close(fSTDERR[0])
try ThrowSystemError()
}
#else
gid = Darwin.getpgid(procPid)
_ = Darwin.close(fSTDIN[0])
_ = Darwin.close(fSTDOUT[1])
_ = Darwin.close(fSTDERR[1])
if spawnRes != 0 {
_ = Darwin.close(fSTDIN[1])
_ = Darwin.close(fSTDOUT[0])
_ = Darwin.close(fSTDERR[0])
try ThrowSystemError()
}
#endif
pid = procPid
stdin = File("stdin", fd: fSTDIN[1])
stdout = File("stdout", fd: fSTDOUT[0])
stderr = File("stderr", fd: fSTDERR[0])
}
deinit {
close()
}
/// Returns true if the process was opened and was running at some point.
/// Note that the process may not be currently running. Use `wait(false)` to check if the process is currently running.
public func isOpen() -> Bool {
return pid != -1
}
/// Terminate the process and clean up.
public func close() {
if stdin != nil {
stdin!.close()
}
if stdout != nil {
stdout!.close()
}
if stderr != nil {
stderr!.close()
}
if pid != -1 {
do {
let _ = try kill()
} catch {
}
}
stdin = nil
stdout = nil
stderr = nil
pid = -1
gid = -1
}
/// Detach from the process such that it will not be manually terminated when this object is deinitialized.
public func detach() {
pid = -1
}
/// Determine if the process has completed running and retrieve its result code.
public func wait(hang: Bool = true) throws -> Int32 {
var code = Int32(0)
while true {
let status = waitpid(pid, &code, WUNTRACED | (hang ? 0 : WNOHANG))
if status == -1 && errno == EINTR {
continue
}
guard status != -1 else {
try ThrowSystemError()
}
if status == 0 && !hang {
return 0
}
break
}
pid = -1
return (((code) & 0xff00) >> 8)
}
/// Terminate the process and return its result code.
public func kill(signal: Int32 = SIGTERM) throws -> Int32 {
#if os(Linux)
let status = SwiftGlibc.kill(pid, signal)
#else
let status = Darwin.kill(pid, signal)
#endif
guard status != -1 else {
try ThrowSystemError()
}
return try wait()
}
/// Terminate the process group and return the root process result code.
public func killGroup(signal: Int32 = SIGTERM) throws -> Int32 {
#if os(Linux)
let status = SwiftGlibc.killpg(gid, signal)
#else
let status = Darwin.killpg(gid, signal)
#endif
guard status != -1 else {
try ThrowSystemError()
}
return try wait()
}
}
================================================
FILE: Sources/PerfectLib/Utilities.swift
================================================
//
// Utilities.swift
// PerfectLib
//
// Created by Kyle Jessup on 7/17/15.
// Copyright (C) 2015 PerfectlySoft, Inc.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
#if os(Linux)
import LinuxBridge
#else
import Darwin
#endif
import Foundation
/// This class permits an UnsafeMutablePointer to be used as a GeneratorType
public struct GenerateFromPointer<T> : IteratorProtocol {
public typealias Element = T
var count = 0
var pos = 0
var from: UnsafeMutablePointer<T>
/// Initialize given an UnsafeMutablePointer and the number of elements pointed to.
public init(from: UnsafeMutablePointer<T>, count: Int) {
self.from = from
self.count = count
}
/// Return the next element or nil if the sequence has been exhausted.
mutating public func next() -> Element? {
guard count > 0 else {
return nil
}
count -= 1
let result = from[pos]
pos += 1
return result
}
}
/// A generalized wrapper around the Unicode codec operations.
public struct Encoding {
/// Return a String given a character generator.
public static func encode<D : UnicodeCodec, G : IteratorProtocol>(codec inCodec: D, generator: G) -> String where G.Element == D.CodeUnit {
var encodedString = ""
var finished: Bool = false
var mutableDecoder = inCodec
var mutableGenerator = generator
repeat {
let decodingResult = mutableDecoder.decode(&mutableGenerator)
switch decodingResult {
case .scalarValue(let char):
encodedString.append(String(char))
case .emptyInput:
finished = true
/* ignore errors and unexpected values */
case .error:
finished = true
}
} while !finished
return encodedString
}
}
/// Utility wrapper permitting a UTF-8 character generator to encode a String. Also permits a String to be converted into a UTF-8 byte array.
public struct UTF8Encoding {
/// Use a character generator to create a String.
public static func encode<G : IteratorProtocol>(generator gen: G) -> String where G.Element == UTF8.CodeUnit {
return Encoding.encode(codec: UTF8(), generator: gen)
}
/// Use a character sequence to create a String.
public static func encode<S : Sequence>(bytes byts: S) -> String where S.Iterator.Element == UTF8.CodeUnit {
return encode(generator: byts.makeIterator())
}
/// Use a character sequence to create a String.
public static func encode(bytes byts: [UTF8.CodeUnit]) -> String {
return encode(generator: byts.makeIterator())
}
/// Decode a String into an array of UInt8.
public static func decode(string str: String) -> Array<UInt8> {
return [UInt8](str.utf8)
}
}
extension UInt8 {
var shouldURLEncode: Bool {
let cc = self
return ( ( cc >= 128 )
|| ( cc < 33 )
|| ( cc >= 34 && cc < 38 )
|| ( ( cc > 59 && cc < 61) || cc == 62 || cc == 58)
|| ( ( cc >= 91 && cc < 95 ) || cc == 96 )
|| ( cc >= 123 && cc <= 126 )
|| self == 43 )
}
// same as String(self, radix: 16)
// but outputs two characters. i.e. 0 padded
var hexString: String {
var s = ""
let b = self >> 4
s.append(String(Character(UnicodeScalar(b > 9 ? b - 10 + 65 : b + 48))))
let b2 = self & 0x0F
s.append(String(Character(UnicodeScalar(b2 > 9 ? b2 - 10 + 65 : b2 + 48))))
return s
}
}
extension String {
/// Returns the String with all special HTML characters encoded.
public var stringByEncodingHTML: String {
var ret = ""
var g = unicodeScalars.makeIterator()
var lastWasCR = false
while let c = g.next() {
if c == UnicodeScalar(10) {
if lastWasCR {
lastWasCR = false
ret.append("\n")
} else {
ret.append("<br>\n")
}
continue
} else if c == UnicodeScalar(13) {
lastWasCR = true
ret.append("<br>\r")
continue
}
lastWasCR = false
if c < UnicodeScalar(0x0009) {
if let scale = UnicodeScalar(0x0030 + UInt32(c)) {
ret.append("&#x")
ret.append(String(Character(scale)))
ret.append(";")
}
} else if c == UnicodeScalar(0x0022) {
ret.append(""")
} else if c == UnicodeScalar(0x0026) {
ret.append("&")
} else if c == UnicodeScalar(0x0027) {
ret.append("'")
} else if c == UnicodeScalar(0x003C) {
ret.append("<")
} else if c == UnicodeScalar(0x003E) {
ret.append(">")
} else if c > UnicodeScalar(126) {
ret.append("&#\(UInt32(c));")
} else {
ret.append(String(Character(c)))
}
}
return ret
}
/// Returns the String with all special URL characters encoded.
public var stringByEncodingURL: String {
var ret = ""
var g = utf8.makeIterator()
while let c = g.next() {
if c.shouldURLEncode {
ret.append(String(Character(UnicodeScalar(37))))
ret.append(c.hexString)
} else {
ret.append(String(Character(UnicodeScalar(c))))
}
}
return ret
}
// Utility - not sure if it makes the most sense to have here or outside or elsewhere
static func byteFromHexDigits(one c1v: UInt8, two c2v: UInt8) -> UInt8? {
let capA: UInt8 = 65
let capF: UInt8 = 70
let lowA: UInt8 = 97
let lowF: UInt8 = 102
let zero: UInt8 = 48
let nine: UInt8 = 57
var newChar = UInt8(0)
if c1v >= capA && c1v <= capF {
newChar = c1v - capA + 10
} else if c1v >= lowA && c1v <= lowF {
newChar = c1v - lowA + 10
} else if c1v >= zero && c1v <= nine {
newChar = c1v - zero
} else {
return nil
}
newChar *= 16
if c2v >= capA && c2v <= capF {
newChar += c2v - capA + 10
} else if c2v >= lowA && c2v <= lowF {
newChar += c2v - lowA + 10
} else if c2v >= zero && c2v <= nine {
newChar += c2v - zero
} else {
return nil
}
return newChar
}
/// Decode the % encoded characters in a URL and return result
public var stringByDecodingURL: String? {
let percent: UInt8 = 37
let plus: UInt8 = 43
let space: UInt8 = 32
var bytesArray = [UInt8]()
var g = utf8.makeIterator()
while let c = g.next() {
if c == percent {
guard let c1v = g.next() else {
return nil
}
guard let c2v = g.next() else {
return nil
}
guard let newChar = String.byteFromHexDigits(one: c1v, two: c2v) else {
return nil
}
bytesArray.append(newChar)
} else if c == plus {
bytesArray.append(space)
} else {
bytesArray.append(c)
}
}
bytesArray.append(0)
return bytesArray.withUnsafeBytes {
guard let p = $0.bindMemory(to: Int8.self).baseAddress else {
return nil
}
return String(validatingUTF8: p)
}
}
/// Decode a hex string into resulting byte array
public var decodeHex: [UInt8]? {
var bytesArray = [UInt8]()
var g = utf8.makeIterator()
while let c1v = g.next() {
guard let c2v = g.next() else {
return nil
}
guard let newChar = String.byteFromHexDigits(one: c1v, two: c2v) else {
return nil
}
bytesArray.append(newChar)
}
return bytesArray
}
}
@available(*, deprecated, message: "Use Foundation.UUID")
public struct UUID {
let uuid: uuid_t
public init() {
let u = UnsafeMutablePointer<UInt8>.allocate(capacity: MemoryLayout<uuid_t>.size)
defer {
u.deallocate()
}
uuid_generate_random(u)
uuid = UUID.uuidFromPointer(u)
}
public init(_ string: String) {
let u = UnsafeMutablePointer<UInt8>.allocate(capacity: MemoryLayout<uuid_t>.size)
defer {
u.deallocate()
}
uuid_parse(string, u)
uuid = UUID.uuidFromPointer(u)
}
init(_ uuid: uuid_t) {
self.uuid = uuid
}
private static func uuidFromPointer(_ u: UnsafeMutablePointer<UInt8>) -> uuid_t {
// is there a better way?
return uuid_t(u[0], u[1], u[2], u[3], u[4], u[5], u[6], u[7], u[8], u[9], u[10], u[11], u[12], u[13], u[14], u[15])
}
public var string: String {
let u = UnsafeMutablePointer<UInt8>.allocate(capacity: MemoryLayout<uuid_t>.size)
let unu = UnsafeMutablePointer<Int8>.allocate(capacity: 37) // as per spec. 36 + null
defer {
u.deallocate()
unu.deallocate()
}
var uu = uuid
memcpy(u, &uu, MemoryLayout<uuid_t>.size)
uuid_unparse_lower(u, unu)
return String(validatingUTF8: unu)!
}
}
extension String {
/// Parse an HTTP Digest authentication header returning a Dictionary containing each part.
public func parseAuthentication() -> [String:String] {
var ret = [String:String]()
if let _ = range(of: "Digest ") {
ret["type"] = "Digest"
let wantFields = ["username", "nonce", "nc", "cnonce", "response", "uri", "realm", "qop", "algorithm"]
for field in wantFields {
if let foundField = String.extractField(from: self, named: field) {
ret[field] = foundField
}
}
}
return ret
}
private static func extractField(from frm: String, named: String) -> String? {
guard let range = frm.range(of: named + "=") else {
return nil
}
var currPos = range.upperBound
var ret = ""
let quoted = frm[currPos] == "\""
if quoted {
currPos = frm.index(after: currPos)
let tooFar = frm.endIndex
while currPos != tooFar {
if frm[currPos] == "\"" {
break
}
ret.append(frm[currPos])
currPos = frm.index(after: currPos)
}
} else {
let tooFar = frm.endIndex
while currPos != tooFar {
if frm[currPos] == "," {
break
}
ret.append(frm[currPos])
currPos = frm.index(after: currPos)
}
}
return ret
}
}
extension String {
/// Replace all occurrences of `string` with `withString`.
public func stringByReplacing(string strng: String, withString: String) -> String {
guard !strng.isEmpty else {
return self
}
guard !isEmpty else {
return self
}
var ret = ""
var idx = startIndex
let endIdx = endIndex
while idx != endIdx {
if self[idx] == strng[strng.startIndex] {
var newIdx = index(after: idx)
var findIdx = strng.index(after: strng.startIndex)
let findEndIdx = strng.endIndex
while newIdx != endIndex && findIdx != findEndIdx && self[newIdx] == strng[findIdx] {
newIdx = index(after: newIdx)
findIdx = strng.index(after: findIdx)
}
if findIdx == findEndIdx { // match
ret.append(withString)
idx = newIdx
continue
}
}
ret.append(self[idx])
idx = index(after: idx)
}
return ret
}
// For compatibility due to shifting swift
public func contains(string: String) -> Bool {
return nil != range(of: string)
}
}
extension String {
func begins(with str: String) -> Bool {
return starts(with: str)
}
func ends(with str: String) -> Bool {
guard count >= str.count else {
return false
}
return str.begins(with: String(self[index(endIndex, offsetBy: -str.count)..<endIndex]))
}
}
/// Returns the current time according to ICU
/// ICU dates are the number of milliseconds since the reference date of Thu, 01-Jan-1970 00:00:00 GMT
public func getNow() -> Double {
var posixTime = timeval()
gettimeofday(&posixTime, nil)
return Double((posixTime.tv_sec * 1000) + (Int(posixTime.tv_usec)/1000))
}
/// Converts the milliseconds based ICU date to seconds since the epoch
public func icuDateToSeconds(_ icuDate: Double) -> Int {
return Int(icuDate / 1000)
}
/// Converts the seconds since the epoch into the milliseconds based ICU date
public func secondsToICUDate(_ seconds: Int) -> Double {
return Double(seconds * 1000)
}
/// Format a date value according to the indicated format string and return a date string.
/// - parameter date: The date value
/// - parameter format: The format by which the date will be formatted. Use a valid strftime style format string.
/// - parameter timezone: The optional timezone in which the date is expected to be based. Default is the local timezone.
/// - parameter locale: The optional locale which will be used when parsing the date. Default is the current global locale.
/// - returns: The resulting date string
/// - throws: `PerfectError.systemError`
public func formatDate(_ date: Double, format: String, timezone inTimezone: String? = nil, locale inLocale: String? = nil) throws -> String {
var t = tm()
var time = time_t(date / 1000.0)
gmtime_r(&time, &t)
let maxResults = 1024
let results = UnsafeMutablePointer<Int8>.allocate(capacity: maxResults)
defer {
results.deallocate()
}
let res = strftime(results, maxResults, format, &t)
if res > 0 {
let formatted = String(validatingUTF8: results)
return formatted!
}
try ThrowSystemError()
}
extension UnicodeScalar {
/// Returns true if the UnicodeScalar is a white space character
public func isWhiteSpace() -> Bool {
return isspace(Int32(value)) != 0
}
/// Returns true if the UnicodeScalar is a digit character
public func isDigit() -> Bool {
return isdigit(Int32(value)) != 0
}
/// Returns true if the UnicodeScalar is an alpha-numeric character
public func isAlphaNum() -> Bool {
return isalnum(Int32(value)) != 0
}
/// Returns true if the UnicodeScalar is a hexadecimal character
public func isHexDigit() -> Bool {
if isDigit() {
return true
}
switch self {
case "A", "B", "C", "D", "E", "F", "a", "b", "c", "d", "e", "f":
return true
default:
return false
}
}
}
extension String {
var filePathSeparator: UnicodeScalar {
return UnicodeScalar(47)
}
var fileExtensionSeparator: UnicodeScalar {
return UnicodeScalar(46)
}
public var beginsWithFilePathSeparator: Bool {
guard count > 0 else {
return false
}
return self[startIndex] == Character(filePathSeparator)
}
public var endsWithFilePathSeparator: Bool {
guard count > 0 else {
return false
}
return self[index(before: endIndex)] == Character(filePathSeparator)
}
private func filePathComponents(addFirstLast addfl: Bool) -> [String] {
var r = [String]()
guard count > 0 else {
return r
}
let fsc = Character(filePathSeparator)
let beginSlash = self[startIndex] == fsc
if addfl && beginSlash {
r.append(String(filePathSeparator))
}
r.append(contentsOf: split(separator: fsc).map { String($0) })
if addfl && self[index(before: endIndex)] == fsc {
if !beginSlash || r.count > 1 {
r.append(String(filePathSeparator))
}
}
return r
}
public var filePathComponents: [String] {
return filePathComponents(addFirstLast: true)
}
public var lastFilePathComponent: String {
let last = filePathComponents(addFirstLast: false).last ?? ""
if last.isEmpty && first == Character(filePathSeparator) {
return String(filePathSeparator)
}
return last
}
public var deletingLastFilePathComponent: String {
var comps = filePathComponents(addFirstLast: false)
guard comps.count > 1 else {
if beginsWithFilePathSeparator {
return String(filePathSeparator)
}
return ""
}
comps.removeLast()
let joined = comps.joined(separator: String(filePathSeparator))
if beginsWithFilePathSeparator {
return String(filePathSeparator) + joined
}
return joined
}
private func lastPathSeparator(in unis: String) -> String.Index {
let startIndex = unis.startIndex
var endIndex = unis.endIndex
while endIndex != startIndex {
if unis[unis.index(before: endIndex)] != Character(filePathSeparator) {
break
}
endIndex = unis.index(before: endIndex)
}
return endIndex
}
private func lastExtensionSeparator(in unis: String, endIndex: String.Index) -> String.Index {
var endIndex = endIndex
while endIndex != startIndex {
endIndex = unis.index(before: endIndex)
if unis[endIndex] == Character(fileExtensionSeparator) {
break
}
}
return endIndex
}
public var deletingFileExtension: String {
var endIndex = lastPathSeparator(in: self)
let noTrailsIndex = endIndex
endIndex = lastExtensionSeparator(in: self, endIndex: endIndex)
guard endIndex != startIndex else {
if noTrailsIndex == startIndex {
return self
}
return String(self[startIndex..<noTrailsIndex])
}
return String(self[startIndex..<endIndex])
}
public var filePathExtension: String {
var endIndex = lastPathSeparator(in: self)
let noTrailsIndex = endIndex
endIndex = lastExtensionSeparator(in: self, endIndex: endIndex)
guard endIndex != startIndex else {
return ""
}
return String(self[index(after: endIndex)..<noTrailsIndex])
}
public var resolvingSymlinksInFilePath: String {
return File(self).realPath
}
}
================================================
FILE: Tests/LinuxMain.swift
================================================
import XCTest
@testable import PerfectLibTests
XCTMain([
testCase(PerfectLibTests.allTests),
])
================================================
FILE: Tests/PerfectLibTests/PerfectLibTests.swift
================================================
//
// PerfectLibTests.swift
// PerfectLibTests
//
// Created by Kyle Jessup on 2015-10-19.
// Copyright © 2015 PerfectlySoft. All rights reserved.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
import XCTest
@testable import PerfectLib
#if os(Linux)
import SwiftGlibc
import Foundation
#endif
class PerfectLibTests: XCTestCase {
override func setUp() {
super.setUp()
#if os(Linux)
SwiftGlibc.srand(UInt32(time(nil)))
#endif
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testJSONConvertibleObject1() {
class Test: JSONConvertibleObject {
static let registerName = "test"
var one = 0
override func setJSONValues(_ values: [String : Any]) {
self.one = getJSONValue(named: "One", from: values, defaultValue: 42)
}
override func getJSONValues() -> [String : Any] {
return [JSONDecoding.objectIdentifierKey:Test.registerName, "One":1]
}
}
JSONDecoding.registerJSONDecodable(name: Test.registerName, creator: { return Test() })
do {
let encoded = try Test().jsonEncodedString()
let decoded = try encoded.jsonDecode() as? Test
XCTAssert(decoded != nil)
XCTAssert(decoded!.one == 1)
} catch {
XCTAssert(false, "Exception \(error)")
}
}
func testJSONConvertibleObject2() {
class User: JSONConvertibleObject {
static let registerName = "user"
var firstName = ""
var lastName = ""
var age = 0
override func setJSONValues(_ values: [String : Any]) {
self.firstName = getJSONValue(named: "firstName", from: values, defaultValue: "")
self.lastName = getJSONValue(named: "lastName", from: values, defaultValue: "")
self.age = getJSONValue(named: "age", from: values, defaultValue: 0)
}
override func getJSONValues() -> [String : Any] {
return [
JSONDecoding.objectIdentifierKey:User.registerName,
"firstName":firstName,
"lastName":lastName,
"age":age
]
}
}
// register the class. do this once
JSONDecoding.registerJSONDecodable(name: User.registerName, creator: { return User() })
// encode and decode the object
let user = User()
user.firstName = "Donnie"
user.lastName = "Darko"
user.age = 17
do {
let encoded = try user.jsonEncodedString()
guard let user2 = try encoded.jsonDecode() as? User else {
return XCTAssert(false, "Invalid object \(encoded)")
}
XCTAssert(user.firstName == user2.firstName)
XCTAssert(user.lastName == user2.lastName)
XCTAssert(user.age == user2.age)
} catch {}
}
func testJSONEncodeDecode() {
let srcAry: [[String:Any]] = [["i": -41451, "i2": 41451, "d": -42E+2, "t": true, "f": false, "n": nil as String? as Any, "a":[1, 2, 3, 4]], ["another":"one"]]
var encoded = ""
var decoded: [Any]?
do {
encoded = try srcAry.jsonEncodedString()
} catch let e {
XCTAssert(false, "Exception while encoding JSON \(e)")
return
}
do {
decoded = try encoded.jsonDecode() as? [Any]
} catch let e {
XCTAssert(false, "Exception while decoding JSON \(e)")
return
}
XCTAssert(decoded != nil)
let resAry = decoded!
XCTAssert(srcAry.count == resAry.count)
for index in 0..<srcAry.count {
let d1 = srcAry[index]
let d2 = resAry[index] as? [String:Any]
for (key, value) in d1 {
let value2 = d2![key]
XCTAssert(value2 != nil)
switch value {
case let i as Int:
XCTAssert(i == value2 as! Int)
case let d as Double:
XCTAssert(d == value2 as! Double)
case let s as String:
XCTAssert(s == value2 as! String)
case let s as Bool:
XCTAssert(s == value2 as! Bool)
default:
()
// does not go on to test sub-sub-elements
}
}
}
}
func testIntegerTypesEncode(){
let i8:Int8 = -12
let i16:Int16 = -1234
let i32:Int32 = -1234567890
let i64:Int64 = -123456789012345678
let u8:UInt8 = 12
let u16:UInt16 = 1234
let u32:UInt32 = 1234567890
let u64:UInt64 = 123456789012345678
var srcDict:[String:Any] = [String:Any]()
var destDict:[String:Any]?
srcDict["i8"] = i8
srcDict["i16"] = i8
srcDict["i32"] = i8
srcDict["i64"] = i8
srcDict["u8"] = i8
srcDict["u16"] = i8
srcDict["u32"] = i8
srcDict["u64"] = i8
var encoded = ""
do {
encoded = try i8.jsonEncodedString()
XCTAssert(encoded == String(i8), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding i8 \(e)")
return
}
do {
encoded = try i16.jsonEncodedString()
XCTAssert(encoded == String(i16), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding i16 \(e)")
return
}
do {
encoded = try i32.jsonEncodedString()
XCTAssert(encoded == String(i32), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding i32 \(e)")
return
}
do {
encoded = try i64.jsonEncodedString()
XCTAssert(encoded == String(i64), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding i64 \(e)")
return
}
do {
encoded = try u8.jsonEncodedString()
XCTAssert(encoded == String(u8), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding u8 \(e)")
return
}
do {
encoded = try u16.jsonEncodedString()
XCTAssert(encoded == String(u16), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding u16 \(e)")
return
}
do {
encoded = try u32.jsonEncodedString()
XCTAssert(encoded == String(u32), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding u32 \(e)")
return
}
do {
encoded = try u64.jsonEncodedString()
XCTAssert(encoded == String(u64), "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding u64 \(e)")
return
}
do {
encoded = ""
encoded = try srcDict.jsonEncodedString()
XCTAssert(encoded != "", "Invalid result")
} catch let e {
XCTAssert(false, "Exception while encoding srcDict \(e)")
}
do {
destDict = try encoded.jsonDecode() as? [String:Any]
} catch let e {
XCTAssert(false, "Exception while decoding into destDict \(e)" )
}
for (key, value) in destDict! {
let value2 = srcDict[key]
XCTAssert(value2 != nil)
switch value2 {
case let i as Int8:
XCTAssert(value as! Int == Int(i))
case let i as Int16:
XCTAssert(value as! Int == Int(i))
case let i as Int32:
XCTAssert(value as! Int == Int(i))
case let i as Int64:
XCTAssert(value as! Int == Int(i))
case let i as UInt8:
XCTAssert(value as! Int == Int(i))
case let i as UInt16:
XCTAssert(value as! Int == Int(i))
case let i as UInt32:
XCTAssert(value as! Int == Int(i))
case let i as UInt64:
XCTAssert(value as! Int == Int(i))
default:
()
}
}
}
func testJSONDecodeUnicode() {
var decoded: [String: Any]?
let jsonStr = "{\"emoji\": \"\\ud83d\\ude33\"}" // {"emoji": "\ud83d\ude33"}
do {
decoded = try jsonStr.jsonDecode() as? [String: Any]
} catch let e {
XCTAssert(false, "Exception while decoding JSON \(e)")
return
}
XCTAssert(decoded != nil)
let value = decoded!["emoji"]
XCTAssert(value != nil)
let emojiStr = decoded!["emoji"] as! String
XCTAssert(emojiStr == "😳")
}
func testSysProcess() {
do {
let proc = try SysProcess("ls", args:["-l", "/"], env:[("PATH", "/usr/bin:/bin")])
XCTAssertTrue(proc.isOpen())
XCTAssertNotNil(proc.stdin)
let fileOut = proc.stdout!
let data = try fileOut.readSomeBytes(count: 4096)
XCTAssertTrue(data.count > 0)
let waitRes = try proc.wait()
XCTAssert(0 == waitRes, "\(waitRes) \(UTF8Encoding.encode(bytes: data))")
proc.close()
} catch {
XCTAssert(false, "Exception running SysProcess test: \(error)")
}
}
func testSysProcessGroup() {
do {
let proc = try SysProcess("sh", args: ["-c", "(sleep 10s &) ; (sleep 10s &) ; sleep 10s"], env: [("PATH", "/usr/bin:/bin")], newGroup: true)
XCTAssert(proc.isOpen())
XCTAssertNotEqual(-1, proc.pid)
XCTAssertNotEqual(-1, proc.gid)
// Ensure that the process group is different from the test process
#if os(Linux)
let testGid = SwiftGlibc.getpgrp()
#else
let testGid = Darwin.getpgrp()
#endif
XCTAssertNotEqual(testGid, proc.gid)
let savedGid = proc.gid
XCTAssertTrue(try hasChildProcesses(gid: savedGid))
_ = try proc.killGroup()
XCTAssertFalse(try hasChildProcesses(gid: savedGid))
} catch {
XCTAssert(false, "Exception running SysProcess group test: \(error)")
}
}
private func hasChildProcesses(gid: pid_t) throws -> Bool {
let proc = try SysProcess("sh", args: ["-c", "ps -e -o pgid,comm | grep \(gid)"], env: [("PATH", "/usr/bin:/bin")])
_ = try proc.wait()
if let bytes = try proc.stdout?.readSomeBytes(count: 4096) {
return bytes.count > 0
} else {
return false
}
}
func testStringByEncodingHTML() {
let src = "<b>\"quoted\" '& ☃"
let res = src.stringByEncodingHTML
XCTAssertEqual(res, "<b>"quoted" '& ☃")
}
func testStringByEncodingURL() {
let src = "This has \"weird\" characters & ßtuff"
let res = src.stringByEncodingURL
XCTAssertEqual(res, "This%20has%20%22weird%22%20characters%20&%20%C3%9Ftuff")
}
func testStringByDecodingURL() {
let src = "This has \"weird\" characters & ßtuff"
let mid = src.stringByEncodingURL
guard let res = mid.stringByDecodingURL else {
XCTAssert(false, "Got nil String")
return
}
XCTAssert(res == src, "Bad URL decoding")
}
func testStringByDecodingURL2() {
let src = "This is badly%PWencoded"
let res = src.stringByDecodingURL
XCTAssert(res == nil, "Bad URL decoding")
}
func testStringByReplacingString() {
let src = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
let test = "ABCFEDGHIJKLMNOPQRSTUVWXYZABCFEDGHIJKLMNOPQRSTUVWXYZABCFEDGHIJKLMNOPQRSTUVWXYZ"
let find = "DEF"
let rep = "FED"
let res = src.stringByReplacing(string: find, withString: rep)
XCTAssert(res == test)
}
func testStringByReplacingString2() {
let src = ""
let find = "DEF"
let rep = "FED"
let res = src.stringByReplacing(string: find, withString: rep)
XCTAssert(res == src)
}
func testStringByReplacingString3() {
let src = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
let find = ""
let rep = "FED"
let res = src.stringByReplacing(string: find, withString: rep)
XCTAssert(res == src)
}
func testStringBeginsWith() {
let a = "123456"
XCTAssert(a.begins(with: "123"))
XCTAssert(!a.begins(with: "abc"))
}
func testStringEndsWith() {
let a = "123456"
XCTAssert(a.ends(with: "456"))
XCTAssert(!a.ends(with: "abc"))
}
func testDeletingPathExtension() {
let path = "/a/b/c.txt"
let del = path.deletingFileExtension
XCTAssert("/a/b/c" == del)
}
func testGetPathExtension() {
let path = "/a/b/c.txt"
let ext = path.filePathExtension
XCTAssert("txt" == ext)
}
func testDirCreate() {
let path = "/tmp/a/b/c/d/e/f/g"
do {
try Dir(path).create()
XCTAssert(Dir(path).exists)
var unPath = path
while unPath != "/tmp" {
try Dir(unPath).delete()
unPath = unPath.deletingLastFilePathComponent
}
} catch {
XCTAssert(false, "Error while creating dirs: \(error)")
}
}
func testDirCreateRel() {
let path = "a/b/c/d/e/f/g"
do {
try Dir(path).create()
XCTAssert(Dir(path).exists)
var unPath = path
repeat {
try Dir(unPath).delete()
// this was killing linux on the final path component
//unPath = unPath.stringByDeletingLastPathComponent
var splt = unPath.split(separator: "/").map(String.init)
splt.removeLast()
unPath = splt.joined(separator: "/")
} while !unPath.isEmpty
} catch {
XCTAssert(false, "Error while creating dirs: \(error)")
}
}
func testDirForEach() {
let dirs = ["a/", "b/", "c/"]
do {
try Dir("/tmp/a").create()
for d in dirs {
try Dir("/tmp/a/\(d)").create()
}
var ta = [String]()
try Dir("/tmp/a").forEachEntry {
name in
ta.append(name)
}
ta.sort()
XCTAssert(ta == dirs, "\(ta) == \(dirs)")
for d in dirs {
try Dir("/tmp/a/\(d)").delete()
}
try Dir("/tmp/a").delete()
} catch {
XCTAssert(false, "Error while creating dirs: \(error)")
}
}
func testFilePerms() {
let fileName = "/tmp/\(UUID().uuidString)"
let file = File(fileName)
do {
try file.open(.readWrite, permissions: [.readUser, .writeUser])
defer {
file.delete()
}
let res = file.perms.contains([.readUser, .writeUser])
XCTAssert(res, "\(file.perms) != \([File.PermissionMode.readUser, File.PermissionMode.writeUser])")
} catch {
XCTAssert(false, "Error testing file perms: \(error)")
}
}
func testDirPerms() {
let fileName = "/tmp/\(UUID().uuidString)"
let file = Dir(fileName)
do {
try file.create(perms: [.readUser, .writeUser])
let res = file.perms.contains([.readUser, .writeUser])
XCTAssert(res, "\(file.perms) != \([File.PermissionMode.readUser, File.PermissionMode.writeUser])")
try file.delete()
} catch {
XCTAssert(false, "Error testing file perms: \(error)")
}
}
func testDirWorkDir() {
let workDir = File("/tmp").realPath.replacingOccurrences(of: "//", with: "/") + "/"
let dir = Dir(workDir)
do {
try dir.setAsWorkingDir()
let fetch = Dir.workingDir.path
XCTAssertEqual(workDir, fetch)
} catch {
XCTAssert(false, "Error testing file perms: \(error)")
}
}
func testBytesIO() {
let i8 = 254 as UInt8
let i16 = 54045 as UInt16
let i32 = 4160745471 as UInt32
let i64 = 17293541094125989887 as UInt64
let bytes = Bytes()
bytes.import64Bits(from: i64)
.import32Bits(from: i32)
.import16Bits(from: i16)
.import8Bits(from: i8)
let bytes2 = Bytes()
bytes2.importBytes(from: bytes)
XCTAssert(i64 == bytes2.export64Bits())
XCTAssert(i32 == bytes2.export32Bits())
XCTAssert(i16 == bytes2.export16Bits())
bytes2.position -= MemoryLayout<UInt16>.size
XCTAssert(i16 == bytes2.export16Bits())
XCTAssert(bytes2.availableExportBytes == 1)
XCTAssert(i8 == bytes2.export8Bits())
}
func testSymlink() {
let f1 = File("./foo")
let f2 = File("./foo2")
do {
f2.delete()
try f1.open(.truncate)
try f1.write(string: "test")
f1.close()
defer {
f1.delete()
f2.delete()
}
let newF2 = try f1.linkTo(path: f2.path)
XCTAssert(try newF2.readString() == "test")
XCTAssert(newF2.isLink)
} catch {
XCTAssert(false, "\(error)")
}
}
}
extension PerfectLibTests {
static var allTests : [(String, (PerfectLibTests) -> () throws -> Void)] {
return [
("testJSONConvertibleObject1", testJSONConvertibleObject1),
("testJSONConvertibleObject2", testJSONConvertibleObject2),
("testJSONEncodeDecode", testJSONEncodeDecode),
("testJSONDecodeUnicode", testJSONDecodeUnicode),
("testSysProcess", testSysProcess),
("testSysProcessGroup", testSysProcessGroup),
("testStringByEncodingHTML", testStringByEncodingHTML),
("testStringByEncodingURL", testStringByEncodingURL),
("testStringByDecodingURL", testStringByDecodingURL),
("testStringByDecodingURL2", testStringByDecodingURL2),
("testStringByReplacingString", testStringByReplacingString),
("testStringByReplacingString2", testStringByReplacingString2),
("testStringByReplacingString3", testStringByReplacingString3),
("testDeletingPathExtension", testDeletingPathExtension),
("testGetPathExtension", testGetPathExtension),
("testDirCreate", testDirCreate),
("testDirCreateRel", testDirCreateRel),
("testDirForEach", testDirForEach),
("testFilePerms", testFilePerms),
("testDirPerms", testDirPerms),
("testBytesIO", testBytesIO),
("testIntegerTypesEncode", testIntegerTypesEncode)
]
}
}
================================================
FILE: Tests/PerfectLibTests/XCTestManifests.swift
================================================
//
// XCTestManifests.swift
//
// Created by Kyle Jessup on 2015-10-19.
// Copyright © 2015 PerfectlySoft. All rights reserved.
//
//===----------------------------------------------------------------------===//
//
// This source file is part of the Perfect.org open source project
//
// Copyright (c) 2015 - 2016 PerfectlySoft Inc. and the Perfect project authors
// Licensed under Apache License v2.0
//
// See http://perfect.org/licensing.html for license information
//
//===----------------------------------------------------------------------===//
//
import XCTest
#if !os(OSX)
public func allTests() -> [XCTestCaseEntry] {
return [
testCase(PerfectLibTests.allTests)
]
}
#endif
gitextract_dd4e6amv/
├── .gitattributes
├── .gitignore
├── .jazzy.yaml
├── CODE_OF_CONDUCT.md
├── CODE_OF_CONDUCT.zh_CN.md
├── LICENSE
├── LICENSE.zh_CN
├── Package.swift
├── README.md
├── README.zh_CN.md
├── Sources/
│ └── PerfectLib/
│ ├── Bytes.swift
│ ├── Dir.swift
│ ├── File.swift
│ ├── JSONConvertible.swift
│ ├── Log.swift
│ ├── PerfectError.swift
│ ├── PerfectServer.swift
│ ├── SysProcess.swift
│ └── Utilities.swift
└── Tests/
├── LinuxMain.swift
└── PerfectLibTests/
├── PerfectLibTests.swift
└── XCTestManifests.swift
Condensed preview — 22 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (148K chars).
[
{
"path": ".gitattributes",
"chars": 146,
"preview": "Sources/LinuxBridge/* linguist-vendored\nSources/OpenSSL/* linguist-vendored\nSources/cURL/* linguist-vendored\nXcode/Perfe"
},
{
"path": ".gitignore",
"chars": 861,
"preview": "# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.perspectivev3\n!defau"
},
{
"path": ".jazzy.yaml",
"chars": 305,
"preview": "module: PerfectLib\nauthor: PerfectlySoft\nauthor_url: https://perfect.org\ngithub_url: https://github.com/PerfectlySoft/Pe"
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3233,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CODE_OF_CONDUCT.zh_CN.md",
"chars": 1015,
"preview": "# 贡献者行为规范\n\n## 我们的承诺\n\n为了实现一个开放、和谐的软件开发环境,作为该软件的贡献者和版本维护者,我们保证在社区参与项目的过\n程中,对所有人一视同仁。在社区中,任何人,无论年龄、身材、长相,或者身体是否有缺陷;无论种族、\n性别"
},
{
"path": "LICENSE",
"chars": 11357,
"preview": " Apache License\n Version 2.0, January 2004\n "
},
{
"path": "LICENSE.zh_CN",
"chars": 2938,
"preview": "Apache许可证\n2.0版 2004年1月\nhttp://www.apache.org/licenses/\n\n关于使用、复制和分发的条款\n\n定义\n\"许可证\"是指根据本文件第1到第9部分关于使用、复制和分发的条款。\n\n\"许可证颁发者\"是指版"
},
{
"path": "Package.swift",
"chars": 1311,
"preview": "// swift-tools-version:5.1\n//\n// Package.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 3/22/16.\n//\tCopyright (C"
},
{
"path": "README.md",
"chars": 9071,
"preview": "# Perfect: Server-Side Swift [简体中文](README.zh_CN.md)\n<p align=\"center\">\n <a href=\"http://perfect.org/get-involved.htm"
},
{
"path": "README.zh_CN.md",
"chars": 5939,
"preview": "# Perfect:Swift 语言服务器端软件框架 [English](README.md)\n<p align=\"center\">\n <a href=\"http://perfect.org/get-involved.html\" ta"
},
{
"path": "Sources/PerfectLib/Bytes.swift",
"chars": 5403,
"preview": "//\n// Bytes.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/7/15.\n//\tCopyright (C) 2015 PerfectlySoft, Inc.\n//\n"
},
{
"path": "Sources/PerfectLib/Dir.swift",
"chars": 5463,
"preview": "//\n// Dir.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/7/15.\n//\tCopyright (C) 2015 PerfectlySoft, Inc.\n//\n//"
},
{
"path": "Sources/PerfectLib/File.swift",
"chars": 18041,
"preview": "//\n// File.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/7/15.\n//\tCopyright (C) 2015 PerfectlySoft, Inc.\n//\n/"
},
{
"path": "Sources/PerfectLib/JSONConvertible.swift",
"chars": 22903,
"preview": "//\n// JSONConvertible.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 2016-01-21.\n// Copyright © 2016 Treefrog. "
},
{
"path": "Sources/PerfectLib/Log.swift",
"chars": 4547,
"preview": "//\n// LogManager.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/21/15.\n//\tCopyright (C) 2015 PerfectlySoft, In"
},
{
"path": "Sources/PerfectLib/PerfectError.swift",
"chars": 1983,
"preview": "//\n// PerfectError.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/5/15.\n//\tCopyright (C) 2015 PerfectlySoft, I"
},
{
"path": "Sources/PerfectLib/PerfectServer.swift",
"chars": 1483,
"preview": "//\n// Perfect.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/5/15.\n//\tCopyright (C) 2015 PerfectlySoft, Inc.\n/"
},
{
"path": "Sources/PerfectLib/SysProcess.swift",
"chars": 7394,
"preview": "//\n// SysProcess.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/20/15.\n// Copyright (C) 2015 PerfectlySoft, I"
},
{
"path": "Sources/PerfectLib/Utilities.swift",
"chars": 16497,
"preview": "//\n// Utilities.swift\n// PerfectLib\n//\n// Created by Kyle Jessup on 7/17/15.\n//\tCopyright (C) 2015 PerfectlySoft, Inc"
},
{
"path": "Tests/LinuxMain.swift",
"chars": 98,
"preview": "import XCTest\n@testable import PerfectLibTests\n\nXCTMain([\n\ttestCase(PerfectLibTests.allTests),\n])\n"
},
{
"path": "Tests/PerfectLibTests/PerfectLibTests.swift",
"chars": 17469,
"preview": "//\n// PerfectLibTests.swift\n// PerfectLibTests\n//\n// Created by Kyle Jessup on 2015-10-19.\n// Copyright © 2015 Perfe"
},
{
"path": "Tests/PerfectLibTests/XCTestManifests.swift",
"chars": 696,
"preview": "//\n// XCTestManifests.swift\n//\n// Created by Kyle Jessup on 2015-10-19.\n// Copyright © 2015 PerfectlySoft. All rights"
}
]
About this extraction
This page contains the full source code of the PerfectlySoft/Perfect GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 22 files (134.9 KB), approximately 37.3k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.