diff --git a/Base/usr/share/man/man1/slugify.md b/Base/usr/share/man/man1/slugify.md new file mode 100644 index 0000000000..651ad921da --- /dev/null +++ b/Base/usr/share/man/man1/slugify.md @@ -0,0 +1,25 @@ +## Name + +slugify - text to slug transform utility + +## Synopsis + +```**sh +$ slugify [--format FORMAT] [--glue GLUE] [--single-page] [INPUTS...] +``` + +## Description + +Slugify is a simple text to slug transform utility and prints the result. + +## Options + +* `-f`, `--format`: Output format to choose from 'md', 'html', 'plain'. (default: md) +* `-g`, `--glue`: Specify delimiter (_single character only_) to join the parts. (default: -) +* `-s`, `--single-page`: Prepends hash/pound (#) to the slugified string when set, otherwise slash (/). Useful for markdowns like in GitHub (default: false) + +## Examples + +```sh +$ slugify 'Serenity is a cool ### PROject123.' +``` diff --git a/Userland/Utilities/CMakeLists.txt b/Userland/Utilities/CMakeLists.txt index cb7168a1a6..87e16bf4ef 100644 --- a/Userland/Utilities/CMakeLists.txt +++ b/Userland/Utilities/CMakeLists.txt @@ -137,6 +137,7 @@ target_link_libraries(run-tests PRIVATE LibCoredump LibDebug LibFileSystem LibRe target_link_libraries(rm PRIVATE LibFileSystem) target_link_libraries(sed PRIVATE LibRegex LibFileSystem) target_link_libraries(shot PRIVATE LibFileSystem LibGfx LibGUI LibIPC) +target_link_libraries(slugify PRIVATE LibUnicode) target_link_libraries(sql PRIVATE LibFileSystem LibIPC LibLine LibSQL) target_link_libraries(su PRIVATE LibCrypt) target_link_libraries(syscall PRIVATE LibSystem) diff --git a/Userland/Utilities/slugify.cpp b/Userland/Utilities/slugify.cpp new file mode 100644 index 0000000000..79f4f8cc46 --- /dev/null +++ b/Userland/Utilities/slugify.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023, Gurkirat Singh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +ErrorOr serenity_main(Main::Arguments arguments) +{ + Vector inputs; + String output_type; + char glue = '-'; + bool spa; + + Core::ArgsParser parser; + parser.set_general_help("Slugify is a simple text to slug transform utility\n$ slugify 'Serenity is a cool ### PROject123.'"); + parser.add_option(output_type, "Output format to choose from 'md', 'html', 'plain'. (default: md)", "format", 'f', "FORMAT"); + parser.add_option(Core::ArgsParser::Option { + .help_string = "Specify delimiter to join the parts. (default: -)", + .long_name = "glue", + .short_name = 'g', + .value_name = "GLUE", + .accept_value = [&glue](StringView s) -> ErrorOr { + if (s.length() == 1 && is_ascii_printable(s[0])) { + glue = s[0]; + return true; + } + return false; + } }); + parser.add_option(spa, "Prepends hash/pound (#) to the slugified string when set, otherwise slash (/). Useful for markdowns like in GitHub (default: false)", "single-page", 's'); + parser.add_positional_argument(inputs, "Input strings to be slugified.", "inputs"); + if (!parser.parse(arguments)) { + parser.print_usage(stderr, arguments.strings[0]); + return 1; + } + + if (is_ascii_space(glue)) { + return Error::from_string_view("Glue cannot be a space character."sv); + } + + auto prepend_char = spa ? '#' : '/'; + for (auto& input : inputs) { + auto slugified = TRY(slugify(Unicode::normalize(input, Unicode::NormalizationForm::NFD), glue)); + + if (output_type.is_empty() || output_type == "md") { + outln("[{}]({}{})", input, prepend_char, slugified); + } else if (output_type == "html") { + outln("{}", prepend_char, slugified, input); + } else { + outln("{}{}", prepend_char, input); + } + } + return 0; +}