Giter VIP home page Giter VIP logo

console-kit's Introduction

Vapor

Documentation Team Chat MIT License Continuous Integration Code Coverage Swift 5.7+ Mastodon


Vapor is an HTTP web framework for Swift. It provides a beautifully expressive and easy-to-use foundation for your next website, API, or cloud project.

Take a look at some of the awesome stuff created with Vapor.

💧 Community

Join the welcoming community of fellow Vapor developers on Discord.

🚀 Contributing

To contribute a feature or idea to Vapor, create an issue explaining your idea or bring it up on Discord.

If you find a bug, please create an issue.

If you find a security vulnerability, please contact [email protected] as soon as possible.

💛 Sponsors

Support Vapor's development by becoming a sponsor.

Broken Hands Emerge Tools Jari Donut Dane MacStadium

💚 Backers

Support Vapor's development by becoming a backer.

Moritz LangMaarten EngelsThomas KrajacicJesse TiptonSteve HumeMikkel UlstrupGeoffrey FosterPaul SchmiedmayerScott RobbinsSven A. SchmidtSpencer CurtisZach RausnitzTim „Timinator“ KretzschmarKlaasAndrew Edwards+Li, Inc.Stijn WillemsKyle NewsomeVia Aurelia SolutionsJakub KiermaszBrian DrellingMattes MohrJamieGalen RhodesLitmapsDavid RomanBrian StrobachKishikawa KatsumiAlex SherbakovSidetrackGreg KarpatiFrantišek MikšJeremy GreenwoodRay FixMićo MiloložaAlanJonas SannewaldTapEnvy.us, LLCJawadPARAIPAN SORINKalyn DavisYR ChenAarón Martínez Cuevas

console-kit's People

Contributors

0xtim avatar bennydebock avatar brettrtoomey avatar calebkleveter avatar cgrindel avatar danielinoa avatar grundoon avatar guang1234567 avatar gwynne avatar ileitch avatar jaapwijnen avatar kemchenj avatar kirbyfan64 avatar kyleleneau avatar liscio avatar loganwright avatar mahdibm avatar marius-se avatar mliberman avatar mrlotu avatar mrmage avatar nathanflurry avatar prince2k3 avatar semicoleon avatar siemensikkema avatar tanner0101 avatar tkrajacic avatar vi4m avatar yageek avatar yasumoto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

console-kit's Issues

[Feature] Add -n option for confirmation inputs

We can pass in a -y flag to automatically answer yes to a confirmation dialogue. I think it would be helpful to add a -n flag to automatically answer no. This could be useful if we need to regenerate an Xcode project the toolbox and don't want to re-open it:

vapor xcode -n

AsyncCommand integration with Vapor.Application

Is your feature request related to a problem? Please describe.
I'm writing a Command for migrating between two database. The majority (if not all) of database interaction with Fluent is async.
So my command needs to conform to ConsoleKit.AsyncCommand. However, application.commands (of type ConsoleKit.Commands) only accepts synchronous commands.

Describe the solution you'd like
An extension on Commands to accept any AsyncCommand, which wraps it into a makeshift "sync" command.

import ConsoleKit

extension Commands {

    public mutating func use(_ command: any AsyncCommand, as name: String, isDefault: Bool = false) {
        self.use(command.wrapped(), as: name, isDefault: isDefault)
    }
}

extension AsyncCommand {

    private func wrapped() -> AnyCommand {
        AsyncCommandWrapper(asyncCommand: self)
    }
}

/// Inspired by others.
private struct AsyncCommandWrapper<Wrapped: AsyncCommand>: Command {

    let asyncCommand: Wrapped

    var help: String { asyncCommand.help }

    func run(using context: CommandContext, signature: Wrapped.Signature) throws {
        let promise = context.application.eventLoopGroup.next()
            .makePromise(of: Void.self)

        promise.completeWithTask {
            try await asyncCommand.run(using: context, signature: signature)
        }

        try promise.futureResult.wait()
    }

    func outputAutoComplete(using context: inout CommandContext) throws {
        try asyncCommand.outputAutoComplete(using: &context)
    }

    func outputHelp(using context: inout CommandContext) throws {
        try asyncCommand.outputHelp(using: &context)
    }

    func renderCompletionFunctions(using context: CommandContext, shell: Shell) -> String {
        return asyncCommand.renderCompletionFunctions(using: context, shell: shell)
    }
}

Describe alternatives you've considered
Adding an async commands container to Vapor.Application. I think this might be less desirable since it broadens the interface.

 public var asyncCommands: AsyncCommands {
        get { self.core.storage.asyncCommands }
        set { self.core.storage.asyncCommands = newValue }
    }

Additional context
The suggested solution does conflate some of the responsibilities of Commands and AsyncCommands. I would say it is useful from a vapor standpoint, but not necessarily from a ConsoleKit standpoint.
I've no strong preference. I was confused that there is this nice async API inside vapor/console-kit which does not fit into Vapor.Application. I'm sure someone with a better understanding of these packages and their relationship can bring insight.

a bug when use class Group

When use group command, it will pass redundant argument to arguments.
For example:

final class BuildProject: Command {
	let id = "project"
	
	let signature: [Argument] = [
		Value(name: "projectname")
	]
	
	let console: ConsoleProtocol
	init(console: ConsoleProtocol) {
		self.console = console
	}
	
	func run(arguments: [String]) throws {
		guard let projectname = try value("projectname", from: arguments).string else {
			throw HVPackageError.general("no projectname")
		}
		_ = try console.backgroundExecute(program: "xcodebuild", arguments: ["-project", projectname])
	}
}
try terminal.run(executable: executable, commands: [
			Group(id: "build", commands: [
					BuildProject(console: terminal)
				], help: [
					
				])
		], arguments: Array(iterator), help: [
			
		])

use in commanline:
package build project projectName

but let projectname = try value("projectname", from: arguments).string will be project not projectName

async overload for `Command.run(using context: CommandContext, signature: Signature)`

In the context of converting a code base using Vapor commands for batch jobs to Swift 5.5's async/await, the issue arose that run does not have an async overload. This poses the following problem when converting

fun run(using context: CommandContext, signature: Signature) throws {
  try f.wait()
}

where f is func f() → EventLoopFuture<Void>.

A workaround is to use

    func run(using context: CommandContext, signature: Signature) throws {
        let promise = context.application.eventLoopGroup.next()
            .makePromise(of: Void.self)
        promise.completeWithAsync {
            try await f()
        }
        try promise.futureResult.wait()
    }

but it would be nice if this wrapper (or simply handling via @main, if possible, or some other solution) existed upstream such that one could simply write

    func run(using context: CommandContext, signature: Signature) async throws {
      try await f()
    }

Console output generated via Vapor request loggers lagging in Linux deployment

Describe the bug

When using ConsoleKit version 4.8.0 the log output that is created via the request loggers in Vapor is not printed to the console immediately as it was generated but only every 30 seconds or so in bulk.

The issue does not show in Xcode but on Linux.

In the deployment in question, I'm using Docker Swarm, and usually in that scenario logs are read via the docker service logs -f ... command. But the issue also shows when investigating a single Docker container via docker logs -f ....

To Reproduce

Steps to reproduce the behavior:

  1. Deploy a Vapor App via Docker on Linux
  2. View logs via docker logs -f ...
  3. Generate logs entries over time and investigate the output

Expected behavior

Log entries appearing as they are generated as it was with ConsoleKit prior to version 4.8.0

Environment

  • Vapor Framework version: Any
  • ConsoleKit version: 4.8.0

signature @dynamicMemberLookup

Now that SE-0252 has been accepted, we should try to adopt @dynamicMemberLookup in ConsoleKit:

let foo = try context.option(\.foo)
// could become
let foo = context.options.foo

Conformance to @dynamicMemberLookup should be fairly straightforward except for one issue: throwing. Since @dynamicMemberLookup will result in a subscript / computed-property for the given key path, we will not be able to throw. To remedy this, we can insert an "input validation" step before the command actually runs. In this step, we would ensure any supplied arguments / options have been converted to their desired types and store them. Then the member lookup could be done once the command runs without throwing. (Even if we don't decide to adopt @dynamicMemberLookup, this early validation step could be a nice addition)

Create a CLI without a Vapor Application

Right now the easiest way to create a working CLI is to boot up a Vapor Application instance when the command is run. This seems a bit like overkill. We should either document a better way to do this if one already exists or create a better API for Console 4.

Crash when using Secure Input in Docker container for custom Vapor Command

Describe the bug

When using the secure input within ConsoleKit, app crashes with Trace/breakpoint trap error.

To Reproduce

Simplified version of my code that produces error within Docker but NOT on macOS.

struct CreateUserCommand: Command {
    struct Signature: CommandSignature {
        @Argument(name: "username")
        var username: String
    }

    var help: String {
        NSLocalizedString("Create a new user with the provided email address.", comment: "")
    }

    func run(using context: ConsoleKit.CommandContext, signature: Signature) throws {
        context.console.print("We're about to setup a new user using the username `\(signature.username)`.")
        let firstName: String = context.console.ask("User's First Name: ")
        let lastName: String = context.console.ask("User's Last Name: ")
        let emailAddress: String = context.console.ask("E-Mail Address: ")

        // Crashes right after this displays.
        let password: String = context.console.ask("Password: ", isSecure: true)
        let passwordConfirmation: String = context.console.ask("Confirm Password: ", isSecure: true)
    }
}

Steps to reproduce the behavior:

  1. Default Vapor app with latest versions as of 8/27/2023
  2. Run command swift run App createUser userName and follow prompts
  3. Crashes once reaches the secure input.

Expected behavior

To not crash and accept secure input.

Environment

FROM swift:5.8-jammy as build

RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
    && apt-get -q update \
    && apt-get -q dist-upgrade -y \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /build

COPY ./Package.* ./
RUN swift package resolve

COPY . .

RUN swift build -c release --static-swift-stdlib

WORKDIR /staging

RUN cp "$(swift build --package-path /build -c release --show-bin-path)/App" ./
RUN find -L "$(swift build --package-path /build -c release --show-bin-path)/" -regex '.*\.resources$' -exec cp -Ra {} ./ \;

RUN [ -d /build/Public ] && { mv /build/Public ./Public && chmod -R a-w ./Public; } || true
RUN [ -d /build/Resources ] && { mv /build/Resources ./Resources && chmod -R a-w ./Resources; } || true

FROM ubuntu:jammy

RUN export DEBIAN_FRONTEND=noninteractive DEBCONF_NONINTERACTIVE_SEEN=true \
    && apt-get -q update \
    && apt-get -q dist-upgrade -y \
    && apt-get -q install -y \
      ca-certificates \
      tzdata \
    && rm -r /var/lib/apt/lists/*

RUN useradd --user-group --create-home --system --skel /dev/null --home-dir /app vapor

WORKDIR /app
COPY --from=build --chown=vapor:vapor /staging /app
USER vapor:vapor
EXPOSE 8080

ENTRYPOINT ["./App"]
CMD ["serve", "--env", "production", "--hostname", "0.0.0.0", "--port", "8080"]

Package.swift

// swift-tools-version:5.8
import PackageDescription

let package = Package(
    name: "FASAAPI",
    platforms: [
        .macOS(.v13)
    ],
    dependencies: [
        .package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
        .package(url: "https://github.com/vapor/fluent.git", from: "4.0.0"),
        .package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0"),
        .package(url: "https://github.com/vapor/leaf.git", from: "4.0.0"),
        .package(url: "https://github.com/swift-server-community/SwiftPrometheus.git", from: "1.0.0"),
        .package(url: "https://github.com/vapor-community/sendgrid.git", from: "5.0.0"),
        .package(url: "https://github.com/DiscordBM/DiscordBM", from: "1.0.0"),
        .package(url: "https://github.com/DiscordBM/DiscordLogger", from: "1.0.0-beta.1"),
        .package(url: "https://github.com/apple/swift-log.git", from: "1.0.0"),
        .package(url: "https://github.com/swift-server/async-http-client.git", from: "1.9.0"),
        .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.1.0")
    ],
    targets: [
        .executableTarget(
            name: "App",
            dependencies: [
                .product(name: "Fluent", package: "fluent"),
                .product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
                .product(name: "Leaf", package: "leaf"),
                .product(name: "Vapor", package: "vapor"),
                .product(name: "SwiftPrometheus", package: "SwiftPrometheus"),
                .product(name: "SendGrid", package: "sendgrid"),
                .product(name: "DiscordBM", package: "DiscordBM"),
                .product(name: "DiscordLogger", package: "DiscordLogger"),
                .product(name: "Logging", package: "swift-log"),
                .product(name: "AsyncHTTPClient", package: "async-http-client")
            ],
            swiftSettings: [
                .enableUpcomingFeature("StrictConcurrency"),
                .enableUpcomingFeature("ConciseMagicFile"),
                .enableUpcomingFeature("BareSlashRegexLiterals"),
                .enableUpcomingFeature("ForwardTrailingClosures"),
                .enableUpcomingFeature("ExistentialAny"),
                .unsafeFlags(["-cross-module-optimization"], .when(configuration: .release))
            ]
        ),
        .testTarget(name: "AppTests", dependencies: [
            .target(name: "App"),
            .product(name: "XCTVapor", package: "vapor")
        ])
    ]
)

Additional context

Oddly all tests pass within CI/CD system but this command fails when ran from a Docker terminal.

Fancier text coloring

Name of Feature

Introduction

Sometimes, it's handy to change colors of text in a console. Right now, it's not a very fluent process to do that.

Motivation

The goal is to make styling text in the console a much easier process.

Proposed solution

Create an class called Styler. This class would look something like this:

class Styler: CustomStringConvertible {
    var str: String
    var color: ConsoleColor = .none
    var bgColor: ConsoleColor = .none
    var modifier: ConsoleModifier = .none // e.g. bold, dim, underline, etc.

    var description: String {
        // Stylize the string and reset after string finished
    }

    init(str: String) {
        self.str = str
    }

    // Modifiers
    var red: Styler {
        color = .red
        return self
    }
    var blue: Styler {
        color = .blue
        return self
    }
    ...
    var bold: Styler {
        modifier = ConsoleModifier(raw: modifier.rawValue | ConsoleModifier.bold)
        return self
    }
}

Then, it could be used like this:

console.info("Plain text, \( Styler("blue text").blue ), \( Styler("bold text").bold.bgGreen.white ), \( Styler("no text").hidden.bgBlue.white ).")

With a bit more tweaking, it could be used for nested styling.

Note: The idea was borrowed from here.

Impact

This will not change any existing applications.

Alternatives considered

We could stick with the existing way of styling colors, which is a bit painful.

Logging improvements.

Hey all 👋

I'm new Vapor and loving it so far! My prior development experience is most recently iOS, but before that I was an SRE using Rails. I've discovered that logging in Vapor doesn't quite match my expectations based on my past experiences.

Example 1: Server

[ INFO ] GET /favicon.ico
[ ERROR ] Not Found

At the info log level this output is OK (if a little terse), but at the warning level and above it becomes useless since the error line provides no context.

Example 2: Queues

[ INFO ] Starting jobs worker (queue: tmdb)
[ INFO ] Dequeing Job:
[ INFO ] Dispatched queue job
[ INFO ] Dequeing Job:
[ INFO ] Dequeing Job:
[ INFO ] Dispatched queue job
[ INFO ] Dequeing Job:

This output at least tells me something happened, but otherwise it's mostly useless. I'll discuss log levels more below, but I think it's important to mention that this is the default output for Vapor in the development environment, and therefore what newcomers experience.

Vapor can of course log more information using the metadata mechanism, but at at least in ConsoleLogger that's restricted to the debug level. This behavior is contrary to my past experiences. For iOS we would log quite liberally at the info level, and include those logs in crash reports sent to Crashlytics. Things we'd log at this level included network activity, state machine transitions, events, etc. We logged everything necessary to help us track the users progress through the app to help in diagnosing the crash. In Rails we also used logging liberally.

We could try to define exactly the amount of information that each log level should include, but I think many people have their own preference, so while sensible defaults are great, it'd be great if we could accommodate everyone's personal preference.

Therefore I'd like to put forward some ideas. Please note that the examples provided below aren't serious API suggestions, they're just to convey the concepts.

1. Associate each metadata item with a log level

logger.info("Hello", metadata: [
    .info("job", name),
    .debug("payload", payload)
])

Rather than all metadata only being logged at the debug level, this gives us the ability to more finely control individual log line verbosity.

2. Define default metadata items, and allow their log level to be customized

Each subsystem (e.g queues, server, etc.) defines a default set of metadata items that are included on every log line output by that subsystem, even for custom lines added by users. For example the queues subsystem might contain:

struct MetadataItem {
    static var timestamp: Self {
        .init(name: "timestamp", value: { ... })
    }
    static var jobId: Self {
        .init(name: "job-id", value: { ... })
    }
    static var jobName: Self {
        .init(name: "job-name", value: { ... })
    }
    static var location: Self {
        .init(name: "location", value: { ... })
    }
}

Then calling logger.debug("Hello") would yield 2020-05-20 12:00:00 [ INFO ] Hello [job-name: "MyJob", job-id: 1234] (File.swift:42)

To accommodate personal preference, users would also be able to customize the default items output at each log level, e.g:

queues.logger.defaultMetadata.include([.timestamp, .jobName], for: [.critical, .error, .warning])
queues.logger.defaultMetadata.include([.timestamp, .jobName, .jobId], for: .info)
queues.logger.defaultMetadata.include(.all, for: [.debug, .trace])

3. Output more information by default at the info level

Since info is the default in development mode, I think it should be more informative to aid development efforts, whilst also being a more friendly experience for newcomers.

Not compiling for other platforms

Hi,

could you please add to Terminal.swift line 82:

#else
            var pass = ""

This fixes compilation for other platforms. In my case I have an iOS project, that shares some API with the backend and that depends on async-kit -> console-kit. I know, that my dependencies are too strong and that the API models should have less dependencies, but they are managed externally so I have less influence there. If console-kit compiled for other platforms as well with the above lines, it would be nice and solves the problem. Maybe add a #warning, that the current platform is not supported in the #else case.

If you rather want a pull request, let me know.

Concurrency warnings

Describe the bug

We are having some warnings at the moment that we are building a vapor project

To Reproduce

Steps to reproduce the behavior:

Using the latest version 4.85.0, the displayed warnings are like this when we build:

.build/checkouts/console-kit/Sources/ConsoleKit/Activity/ActivityIndicator.swift:1:1: remark: add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Dispatch'
import Dispatch
.build/checkouts/vapor/Sources/Vapor/Validation/Validators/CharacterSet.swift:1:1: remark: add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Foundation'
import Foundation 
.build/checkouts/vapor/Sources/Vapor/Sessions/MemorySessions.swift:1:1: remark: add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Dispatch'
import Foundation

Expected behavior

No warnings

Environment

  • Vapor Framework version: 4.85.0
  • Vapor Toolbox version: 18.6.0
  • OS version: Ubuntu 22.04

Additional context

The context is this

Corrupt LoadingBar?

I wrote a little command line tool in Xcode. While the tool is doing his job I'd like to indicate that the tool is working with a little loading bar. For this I've chosen the LoadingBar. Unfortunately the loading bar behaves not as expected.

Given is the following code:

import ConsoleKit
import Foundation

let loadingBar = Terminal().loadingBar(title: "TEST")

loadingBar.start()
sleep(3)
loadingBar.succeed()

When I run this code I get the following output:

LoadingBarWrong

The output seems to be wrong because I expect every update on one/the same line and not each print in a separate line. But when I run the very same code with swift run on the console the output is as expected:

LoadingBarOk

Does anyone know whether this is a bug or do I have to configure the console differently?

Group fallback not behaving as I expect

I am working on a new project Autobahn and am using Console for the CLI tool 👍 I am wanting to define a default command that runs if no existing matches are found. So here is my setup:

let group = Group(id: executable, commands: [
    Drive(console: terminal),
    Init(console: terminal),
], help: [
    "CLI for 'autobahn' - The swiftiest way to automate mundane developer tasks for your iOS apps",
], fallback: Drive(console: terminal))

try terminal.run(group, arguments: args)

This is the output for autobahn --help:

Autobahn v0.1.0
Usage: autobahn command

CLI for 'autobahn' - The swiftiest way to automate mundane developer tasks for your iOS apps

Commands:
  drive *Drive a specific highway
   init Creates a default Autobahn.swift file

Use `autobahn command --help` for more information on a command.

And this is the output for autobahn drive --help

Autobahn v0.1.0
Usage: autobahn drive <highway> [--verbose]

*Drive a specific highway

Arugments:
  highway The name of the highway you would like to drive

Options:
  verbose Print more info to help debug issues

Now what I want to do is mark Drive as the default to I can just run autobahn <highway> and have it run autobahn drive <highway> behind the scenes. Basically I want to be able to pass arguments to the fallback command

property wrappers

Property wrappers could be used to make the CommandSignature API more Swifty. Here's an idea for how this could look:

struct Cowsay: Command {
    struct Signature: CommandSignature {
        @Argument(help: "What the cow should say") 
        var message: String

        @Option(short: "e", help: "Change the cow's eyes")
        var eyes: String?

        @Option(short: "t", help: "Change the cow's tongue")
        var tongue: String?

        @Flag(help: "uses == for cow's eyes")
        var borg: Bool
    }

    let help = "Prints an ASCII cow with a message"

    func run(context: CommandContext, signature: Signature) throws {
        print(signature.message) // String
        print(signature.eyes) // String?
        print(signature.tongue) // String?
        print(signature.borg) // Bool
    }
}

Here's how this command could be used:

Usage: cowsay <message> [--eyes,-e eyes] [--tongue,-t tongue] [--borg]

Prints an ASCII cow with a message

Arguments:
    message What the cow should say

Options:
      --eyes, -e Change the cow's eyes
    --tongue, -t Change the cow's tongue

Flags:
    --borg uses == for cow's eyes

UnvalidatedCommand

A new protocol UnvalidatedCommand that did not require a command to pre-define it's supported arguments and options would be useful for implementing commands like vapor run that forward to an underlying program.

Method to detect pressed key(s) and returning the keyCode(s)

Please add a method to detect single or multiple pressed keys and returning the keyCode(s). This feature is required for accepting password input or screen navigation control (console games?). If you have ever used Pascal and utilized the famous CRT unit, you'll know the ReadKey() and KeyPressed() functions that provide such ability.

Thank you.

Persisting prompts and options

The documentation reads:

    /// Upon answering, the prompt and options will be cleared from the console and only the output will remain:

I was building a console tool for users who aren't very comfortable on the command line, and I'm concerned that it may be disorienting for them to have the prompt and options disappear after they answer. I was concerned enough about it that I added in a 1 second pause after each choice.

Latest version fails to build on Xcode 13.4

Describe the bug

When trying to build a project setup as iOS 13+, that includes Vapor, the project fails to build with the following errors in the console:

/Users/runner/work/1/s/DerivedData/SourcePackages/checkouts/console-kit/Sources/ConsoleKit/Command/Async/AsyncCommand.swift:84:67: concurrency is only available in iOS 13.0.0 or newer

func run(using context: CommandContext, signature: Signature) async throws

To Reproduce

Steps to reproduce the behavior:

  1. Create an iOS app with target iOS 13+
  2. Include Vapor via Swift Package Manager
  3. Try to build the project

Expected behavior

The functions should be properly tagged with @available(iOS 13, *) so Swift will be able to compile without those errors.

Environment

  • Vapor Framework version: 'vapor' branch => 'main'
  • Vapor Toolbox version: console-kit => 4.3.0
  • OS version: macOS 12.3.1, iOS 13+, Xcode 13.4

Additional context

Command Options ask for a new option syntax, but can’t actually use it.

sample project: https://github.com/EricWVGG/vapor-command-problem

This command runs correctly, but returns a warning…

vapor run quiz --answer=one
warning: [Deprecated] --option=value syntax is deprecated. Please use --option value (with no =) instead.

If we follow those directions…

vapor run quiz --answer one
we get Fatal error: Error raised at top level: ⚠️ CommandError: Too many arguments or unsupported options were supplied: ["one"]

Value Validation

Add a validation property to CommandArgument and CommandOption which is an array of (Sting)throws -> Void that validates the values when they are parsed from input.

It would look something like this:

CommandArgument(name: "times", default: "1",  validations: [isInt])

let isInt: (String)throws -> Void = { times in
    guard Int(times) != nil else {
        throw CommandError(identifier: "validationFailed", reason: "Cannot convert value `\(times)` to Int"
    }
}

This value would be assigned an empty array by default, so it would not be a breaking change.

Property Wrappers look not to work

By trying to install the vapor-beta toolbox the brew formula throws an error. (vapor/homebrew-tap#26)
This relates all to the errors that appear while building this project.
Logs of Cannot convert value of type 'Option<_>' to specified type 'String' appear in Xcode or the command line.
I've Swift 5.1.3 installed and the latest Xcode version too.
Maybe there is an obvious thing I'm not seeing to fix this issues.

Array arguments

I've been using Console for building some internal tools, and it's been going great so far. Thanks for separating it from vapor, and for the recent ConsoleKit changes!

Anyway, I was wondering today whether you folks considered allowing for an argument to be specified multiple times? For instance, say I have an input argument that can be specified multiple times. It'd be used like so:

my-command --input /path/to/input --input /path/to/other/input --output /path/to/output

Ideally, this would allow me to get an array of paths for the key "input" from a CommandContext.

Thoughts?

Threading issues (raised by --sanitize=thread)

When I run the ConsoleKit tests with the thread sanitizer on, there are a few races that need to be cleaned up.

I noticed this while validating my own tools that rely on heavy multithreading, and the ActivityIndicator was giving me grief, and making it hard to find my own issues.

To reproduce this, just try the following at the command-line:

swift test --sanitize=thread

PR incoming…

Default values

Is your feature request related to a problem? Please describe.

Parsing property wrappers like Argument, Option, and Flag don't have the ability to set a default value in the event the user doesn't pass a value for it at the command line. Currently, Argument will fatalError (effectively making all arguments required), Option will default to nil, and Flag will default to false. Allowing for a default value would enable cleaner code with less duplication since there would be less need for checking $isPresent and handling nil unnecessarily

For example, let's say I've defined an enumeration for handling conflicts between 2 values:

enum ConflictResolutionStrategy: String, LosslessStringConvertible {
    case error
    case keepExisting = "keep-existing"
    case overwrite

    init?(_ description: String) {
        self.init(rawValue: description)
    }

    var description: String {
        rawValue
    }
}

I'd like the user to be able to choose their preferred strategy, but I'd also like to have a sensible default in case they don't. Currently, my only option for using ConsoleKit to achieve something like this is to declare my property type as an Option, like this:

@Option(name: "conflict-strategy", help: "The method by which conflicts should be resolved")
var conflictStrategy: ConflictResolutionStrategy?

However, this forces me to declare my property as optional and handle the case where the user doesn't choose a strategy by checking for nil. This means that everywhere that I want to apply special handling based on the current conflictStrategy, I not only need to unwrap the optional, but I also need to choose a strategy to use every time in the event that the user didn't. This can quickly lead to fragmentation and inconsistency across the codebase around how conflicts should be handled by default, which could easily lead to different default behaviors in different places and a confusing user experience

Describe the solution you'd like

I'd like to assign a default value at the site where the property is defined with Swift's standard inline property initialization syntax, such as what's familiar from ArgumentParser's corresponding types or SwiftUI's wrappers like @State or @Published. For example:

@Option(name: "conflict-strategy", help: "The method by which conflicts should be resolved")
var conflictStrategy: ConflictResolutionStrategy = .error

This would allow the property type to be non-optional and have a default value chosen once and respected everywhere. This is possible by defining an initializer for the relevant property wrappers that accept an argument wrappedValue: WrappedValue as their first argument

Describe alternatives you've considered

Alternatively, the relevant property wrappers could take an extra parameter to their initializers, so a default value could be passed there. For example:

@Option(name: "conflict-strategy", help: "The method by which conflicts should be resolved", default: .error)
var conflictStrategy: ConflictResolutionStrategy

However, this is less obvious and discoverable in my opinion, since there's already well-established language syntax for handling this case

Additional context

N/A

Add readme.

You should probably tell people what exactly this package does and how to use it.

Interleaving in console logging

Incorrect behavior while logging HTTP requests (method+path), using a custom middleware, into console. As a result, different requests end up interleaving each other in the console. The middleware itself is pretty much the same as the default ErrorMW, except it uses Logger to log the actual request (logger's methods aren't promised based E.g: https://github.com/vapor/vapor/blob/master/Sources/Vapor/Logging/ConsoleLogger.swift#L15)

Steps to reproduce

Expected behavior

The console should log each request in a new line. E.g:

[VERBOSE] [date] GET /file1.html (ABC.swift)
[VERBOSE] [date] GET /file2.css (ABC.swift)
[VERBOSE] [date] GET /file3.css (ABC.swift)
[VERBOSE] [date] GET /file4.js (ABC.swift)
[VERBOSE] [date] GET /file5.js (ABC.swift)

Actual behavior

Requests will be interleaved instead:

[VERBOSE] [date] GET /file1.html [VERBOSE] [date] GET /file2.css (ABC.swift)
[VERBOSE] [date] GET /file3.css (ABC.swift)
[VERBOSEVERBOSE] [ ] [date] [date] GET /file4.js GET /file5.js

Environment

  • Vapor Framework version: 3.1.4
  • Vapor Toolbox version: 3.0.0-rc.2.2.2
  • OS version: macOS HS 10.13.4

dynamic command

vapor/console 2.0 supported commands that could have an indefinite number of arguments / options. This was used to power commands like vapor run:

vapor run serve
vapor run prepare --revert

In vapor/console 3.0, this ability was lost due to type-safe commands. We should consider whether we want to re-allow this in 4.0 w/ a new mechanism, or if we should implement dynamic commands slightly differently. In the current version, it would be possible to do:

vapor run serve
vapor run "serve"
vapor run "prepare --revert"
struct Signature: CommandSignature {
    let command = Argument<String>("command")
}

Table rendering

Oftentimes, it's handy to render tables to the console to debug data. This would be a good thing to include in Console.

These would be the parameters I would envision being included:

  • The data
  • Number of header, footer, and side bars (these are included in the data itself)
  • Text alignment
  • Column width
  • Split style (e.g. character, color, if there are horizontal lines)
  • Header style

This could be useful for improving the output from Vapor itself. For instance, when printing results form queries, it would be much better to print the data in tables than plain text.

For reference, someone already has a table generator written in Swift here, but it's fairly limited.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.