diff --git a/src/Cli/OptionsParser.elm b/src/Cli/OptionsParser.elm index 3e7725d..81eeb56 100644 --- a/src/Cli/OptionsParser.elm +++ b/src/Cli/OptionsParser.elm @@ -147,6 +147,7 @@ import Cli.OptionsParser.MatchResult import Cli.UsageSpec as UsageSpec exposing (UsageSpec) import Internal.OptionsParser as OPInternal import Json.Decode +import List.Extra import Occurences exposing (Occurences(..)) import Tokenizer exposing (ParsedOption) import TsJson.Decode as TsDecode @@ -324,11 +325,7 @@ expectedPositionalArgCountOrFail (OPInternal.OptionsParser ({ decoder, usageSpec \({ operands } as stuff) -> if not (UsageSpec.hasRestArgs usageSpecs) - && (operands |> List.length) - > (usageSpecs - |> List.filter UsageSpec.isOperand - |> List.length - ) + && (List.length operands > List.Extra.count UsageSpec.isOperand usageSpecs) then Cli.Decode.MatchError Cli.Decode.ExtraOperand |> Err diff --git a/src/Cli/Program.elm b/src/Cli/Program.elm index f36a737..16f92fc 100644 --- a/src/Cli/Program.elm +++ b/src/Cli/Program.elm @@ -732,7 +732,7 @@ parserToJsonSchemaFromTsTypes includeBoilerplate programName parser = restArgSpec : Maybe ( UsageSpec, TsJson.Type.Type ) restArgSpec = specsWithTypes - |> List.filterMap + |> List.Extra.findMap (\( spec, ( _, tsType ) ) -> case spec of UsageSpec.RestArgs _ _ -> @@ -741,7 +741,6 @@ parserToJsonSchemaFromTsTypes includeBoilerplate programName parser = _ -> Nothing ) - |> List.head -- Build $cli schema (only subcommand + positional) cliSubProperties : List ( String, Encode.Value ) @@ -855,8 +854,7 @@ positionalSchemaProperty positionalArgs maybeRestArgs = requiredCount : Int requiredCount = positionalArgs - |> List.filter (\( _, _, occ ) -> occ == Required) - |> List.length + |> List.Extra.count (\( _, _, occ ) -> occ == Required) restItemSchema : Maybe Encode.Value restItemSchema = @@ -1143,10 +1141,10 @@ formatNoMatchReasons colorMode programName parserInfo availableSubCommands optio -- These should take priority over "subcommand not found" errors -- Note: ExtraOperand is NOT included here because it's often from -- system parsers (help/version) and isn't specific enough - missingArgErrors : List NoMatchReason - missingArgErrors = + missingArgError : Maybe NoMatchReason + missingArgError = otherReasons - |> List.filter + |> List.Extra.find (\reason -> case reason of MissingRequiredPositionalArg _ -> @@ -1159,12 +1157,12 @@ formatNoMatchReasons colorMode programName parserInfo availableSubCommands optio False ) in - case missingArgErrors of - reason :: _ -> + case missingArgError of + Just reason -> -- A parser matched the structure but is missing a required argument formatSingleReason colorMode reason programName optionsParsers - [] -> + Nothing -> let wrongSubCommandReasons : List String wrongSubCommandReasons = @@ -1184,12 +1182,12 @@ formatNoMatchReasons colorMode programName parserInfo availableSubCommands optio -- But first check: is the "wrong" command actually a valid subcommand? -- If so, the error is something else (like ExtraOperand) let - unknownCommands : List String - unknownCommands = + maybeUnknownCommand : Maybe String + maybeUnknownCommand = wrongSubCommandReasons - |> List.filter (\cmd -> not (List.member cmd availableSubCommands)) + |> List.Extra.find (\cmd -> not (List.member cmd availableSubCommands)) in - case List.head unknownCommands of + case maybeUnknownCommand of Just unknownCommand -> applyRed colorMode "Unknown command: " ++ "`" @@ -1202,10 +1200,10 @@ formatNoMatchReasons colorMode programName parserInfo availableSubCommands optio Nothing -> let -- ExtraOperand is only relevant if there are no subcommand-related issues - extraOperandErrors : List NoMatchReason - extraOperandErrors = + hasExtraOperandErrors : Bool + hasExtraOperandErrors = otherReasons - |> List.filter + |> List.any (\reason -> case reason of ExtraOperand -> @@ -1217,7 +1215,7 @@ formatNoMatchReasons colorMode programName parserInfo availableSubCommands optio in -- The command was valid but something else went wrong -- Check for ExtraOperand - if not (List.isEmpty extraOperandErrors) then + if hasExtraOperandErrors then formatSingleReason colorMode ExtraOperand programName optionsParsers else @@ -1335,10 +1333,10 @@ formatFallbackMessage colorMode programName optionsParsers = formatJsonNoMatchReasons : List NoMatchReason -> String formatJsonNoMatchReasons reasons = let - unexpectedFieldReasons : List String - unexpectedFieldReasons = + unexpectedFieldReason : Maybe String + unexpectedFieldReason = reasons - |> List.filterMap + |> List.Extra.findMap (\reason -> case reason of UnexpectedOption name -> @@ -1348,38 +1346,29 @@ formatJsonNoMatchReasons reasons = Nothing ) in - case unexpectedFieldReasons of - first :: _ -> + case unexpectedFieldReason of + Just first -> first - [] -> + Nothing -> if List.member ExtraOperand reasons then "Too many positional arguments in \"$cli.positional\"." else - let - missingFieldReasons : List String - missingFieldReasons = - reasons - |> List.filterMap - (\reason -> - case reason of - MissingRequiredKeywordArg { name } -> - Just ("Missing required field: \"" ++ name ++ "\"") - - MissingRequiredPositionalArg { name } -> - Just ("Missing required field: \"" ++ name ++ "\"") + reasons + |> List.Extra.findMap + (\reason -> + case reason of + MissingRequiredKeywordArg { name } -> + Just ("Missing required field: \"" ++ name ++ "\"") - MissingExpectedFlag { name } -> - Just ("Missing required field: \"" ++ name ++ "\"") + MissingRequiredPositionalArg { name } -> + Just ("Missing required field: \"" ++ name ++ "\"") - _ -> - Nothing - ) - in - case missingFieldReasons of - first :: _ -> - first + MissingExpectedFlag { name } -> + Just ("Missing required field: \"" ++ name ++ "\"") - [] -> - "No matching command found for JSON input." + _ -> + Nothing + ) + |> Maybe.withDefault "No matching command found for JSON input." diff --git a/src/Cli/UsageSpec.elm b/src/Cli/UsageSpec.elm index ed16426..282c410 100644 --- a/src/Cli/UsageSpec.elm +++ b/src/Cli/UsageSpec.elm @@ -115,19 +115,18 @@ changeUsageSpec possibleValues usageSpec = operandCount : List UsageSpec -> Int operandCount usageSpecs = usageSpecs - |> List.filterMap + |> List.Extra.count (\spec -> case spec of FlagOrKeywordArg _ _ _ _ -> - Nothing + False - Operand operandName _ _ _ -> - Just operandName + Operand _ _ _ _ -> + True RestArgs _ _ -> - Nothing + False ) - |> List.length optionExists : List UsageSpec -> String -> Maybe FlagOrKeywordArg diff --git a/src/Fuzzy.elm b/src/Fuzzy.elm index 071c409..2e85005 100644 --- a/src/Fuzzy.elm +++ b/src/Fuzzy.elm @@ -17,6 +17,7 @@ module Fuzzy exposing -} +import List.Extra import String @@ -151,8 +152,7 @@ distance config needle hay = String.indexes (String.fromChar c) hay hayIndex = - List.filter (\e -> not (List.member e indexList)) indexes - |> List.head + List.Extra.find (\e -> not (List.member e indexList)) indexes in case hayIndex of Just v -> @@ -170,11 +170,14 @@ distance config needle hay = mPenalty = Tuple.first sorted * config.movePenalty + accumulatedLength = + accumulated |> List.length + hPenalty = - (String.length hay - (accumulated |> List.length)) * config.addPenalty + (String.length hay - accumulatedLength) * config.addPenalty nPenalty = - (String.length needle - (accumulated |> List.length)) * config.removePenalty + (String.length needle - accumulatedLength) * config.removePenalty accumulateInsertPenalty elem result = case result of diff --git a/src/Internal/OptionsParser.elm b/src/Internal/OptionsParser.elm index 50ee207..fa978d0 100644 --- a/src/Internal/OptionsParser.elm +++ b/src/Internal/OptionsParser.elm @@ -12,6 +12,7 @@ import Cli.OptionsParser.MatchResult import Cli.UsageSpec as UsageSpec exposing (UsageSpec) import Json.Decode import Json.Encode as Encode +import List.Extra import Tokenizer exposing (ParsedOption) import TsJson.Type @@ -200,7 +201,7 @@ normalizeCliJson usageSpecs blob = restArgsName : Maybe String restArgsName = usageSpecs - |> List.filterMap + |> List.Extra.findMap (\spec -> case spec of UsageSpec.RestArgs restName _ -> @@ -209,7 +210,6 @@ normalizeCliJson usageSpecs blob = _ -> Nothing ) - |> List.head restFields : List ( String, Encode.Value ) restFields = @@ -259,28 +259,25 @@ rawJsonShapeErrors subCommand usageSpecs blob = topLevelFields = jsonObjectFields blob - cliValue : Maybe Json.Decode.Value + cliValue : Result Json.Decode.Error Json.Decode.Value cliValue = Json.Decode.decodeValue (Json.Decode.field "$cli" Json.Decode.value) blob - |> Result.toMaybe unexpectedTopLevelFields : List Cli.OptionsParser.MatchResult.NoMatchReason unexpectedTopLevelFields = topLevelFields - |> List.map Tuple.first - |> List.filter (\fieldName -> not (List.member fieldName (allowedTopLevelFieldNames usageSpecs))) - |> List.map Cli.OptionsParser.MatchResult.UnexpectedOption + |> List.filter (\( fieldName, _ ) -> not (List.member fieldName (allowedTopLevelFieldNames usageSpecs))) + |> List.map (\( fieldName, _ ) -> Cli.OptionsParser.MatchResult.UnexpectedOption fieldName) unexpectedCliFields : List Cli.OptionsParser.MatchResult.NoMatchReason unexpectedCliFields = case cliValue of - Just actualCliValue -> + Ok actualCliValue -> jsonObjectFields actualCliValue - |> List.map Tuple.first - |> List.filter (\fieldName -> not (List.member fieldName (allowedCliFieldNames subCommand usageSpecs))) - |> List.map (\fieldName -> Cli.OptionsParser.MatchResult.UnexpectedOption ("$cli." ++ fieldName)) + |> List.filter (\( fieldName, _ ) -> not (List.member fieldName (allowedCliFieldNames subCommand usageSpecs))) + |> List.map (\( fieldName, _ ) -> Cli.OptionsParser.MatchResult.UnexpectedOption ("$cli." ++ fieldName)) - Nothing -> + Err _ -> [] in unexpectedTopLevelFields ++ unexpectedCliFields @@ -353,9 +350,9 @@ extraJsonPositionalErrors usageSpecs blob baseMatchResult = [] else - case Json.Decode.decodeValue (Json.Decode.field "$cli" (Json.Decode.field "positional" (Json.Decode.list Json.Decode.value))) blob of - Ok positionalValues -> - if List.length positionalValues > List.length (List.filter UsageSpec.isOperand usageSpecs) then + case Json.Decode.decodeValue (Json.Decode.field "$cli" (Json.Decode.field "positional" (Json.Decode.field "length" Json.Decode.int))) blob of + Ok positionalValuesCount -> + if positionalValuesCount > List.Extra.count UsageSpec.isOperand usageSpecs then [ Cli.OptionsParser.MatchResult.ExtraOperand ] else diff --git a/src/TypoSuggestion.elm b/src/TypoSuggestion.elm index a1197d2..31db7dc 100644 --- a/src/TypoSuggestion.elm +++ b/src/TypoSuggestion.elm @@ -76,8 +76,7 @@ getSuggestions optionsParsers unexpectedOption = buildSubCommandSuggestions : List OptionsParser -> List TypoSuggestion buildSubCommandSuggestions optionsParsers = optionsParsers - |> List.filterMap .subCommand - |> List.map SubCommand + |> List.filterMap (\{ subCommand } -> Maybe.map SubCommand subCommand) optionSuggestions : List OptionsParser -> List TypoSuggestion @@ -85,5 +84,4 @@ optionSuggestions optionsParsers = optionsParsers |> List.concatMap .usageSpecs |> List.Extra.uniqueBy UsageSpec.name - |> List.map UsageSpec.name - |> List.map Flag + |> List.map (\usageSpec -> Flag (UsageSpec.name usageSpec))