pkg.re/essentialkaos/ek.10@v12.41.0+incompatible/usage/man/man.go (about) 1 // Package man contains methods for man pages generation 2 package man 3 4 // ////////////////////////////////////////////////////////////////////////////////// // 5 // // 6 // Copyright (c) 2022 ESSENTIAL KAOS // 7 // Apache License, Version 2.0 <https://www.apache.org/licenses/LICENSE-2.0> // 8 // // 9 // ////////////////////////////////////////////////////////////////////////////////// // 10 11 import ( 12 "fmt" 13 "strings" 14 "time" 15 16 "pkg.re/essentialkaos/ek.v12/fmtc" 17 "pkg.re/essentialkaos/ek.v12/timeutil" 18 "pkg.re/essentialkaos/ek.v12/usage" 19 ) 20 21 // ////////////////////////////////////////////////////////////////////////////////// // 22 23 // Generate generates man page content 24 func Generate(info *usage.Info, about *usage.About) string { 25 var result string 26 27 result += genHeader(about) 28 result += genName(about) 29 result += genSynopsis(info) 30 result += genDescription(info) 31 result += genCommands(info) 32 result += genOptions(info) 33 result += genExamples(info) 34 result += genBugTrackerInfo(about) 35 result += genLicense(about) 36 result += genAuthor(about) 37 38 return result 39 } 40 41 // ////////////////////////////////////////////////////////////////////////////////// // 42 43 // genHeader generates header part 44 func genHeader(about *usage.About) string { 45 return fmt.Sprintf( 46 ".TH %s 1 \"%s\" \"%s %s\" \"%s Manual\"\n\n", 47 strings.ToUpper(about.App), 48 timeutil.Format(time.Now(), "%d %b %Y"), 49 about.App, 50 strings.Replace(about.Version, ".", "\\&.", -1), 51 about.App, 52 ) 53 } 54 55 // genName generates name part 56 func genName(about *usage.About) string { 57 return fmt.Sprintf( 58 ".SH NAME\n%s \\- %s\n", 59 about.App, about.Desc, 60 ) 61 } 62 63 // genSynopsis generatest synopsis 64 func genSynopsis(info *usage.Info) string { 65 result := ".SH SYNOPSIS\n.sp\n.nf\n" 66 result += ".B " + info.Name + " " 67 68 for index, option := range info.Options { 69 result += genOptionShort(option) 70 71 if index != 0 && index%4 == 0 && len(info.Options) != index+1 { 72 result += "\n" + strings.Repeat(" ", len(info.Name)+1) 73 } 74 } 75 76 if len(info.Commands) != 0 { 77 result += fmt.Sprintf("[\\fB%s\\fR] ", "COMMAND") 78 } 79 80 if len(info.Args) != 0 { 81 for _, arg := range info.Args { 82 result += fmt.Sprintf("\\fI%s\\fR\n", arg) 83 } 84 } else { 85 result += "\n" 86 } 87 88 return result + ".fi\n.sp\n" 89 } 90 91 // genOptions generates options part 92 func genOptions(info *usage.Info) string { 93 if len(info.Options) == 0 { 94 return "" 95 } 96 97 result := ".SH OPTIONS\n" 98 99 for _, option := range info.Options { 100 result += genOptionLong(option) 101 } 102 103 return result 104 } 105 106 // genCommands generates commands part 107 func genCommands(info *usage.Info) string { 108 if len(info.Commands) == 0 { 109 return "" 110 } 111 112 curGroup := "" 113 result := ".SH COMMANDS\n" 114 115 for _, command := range info.Commands { 116 if command.Group != "" && curGroup != command.Group { 117 result += fmt.Sprintf(".SS %s\n", command.Group) 118 curGroup = command.Group 119 } 120 121 result += ".TP\n" 122 result += fmt.Sprintf(".B %s", command.Name) 123 124 if len(command.Args) != 0 { 125 result += formatCommandArgs(command.Args) 126 } else { 127 result += "\n" 128 } 129 130 result += fmtc.Clean(command.Desc) + "\n" 131 } 132 133 return result 134 } 135 136 // genOptionShort generates short info for option 137 func genOptionShort(option *usage.Option) string { 138 if option.Arg != "" { 139 return fmt.Sprintf( 140 "[\\fB\\-\\-%s\\fR=\\fI%s\\fR] ", 141 option.Long, strings.ToUpper(option.Arg), 142 ) 143 } else { 144 return fmt.Sprintf("[\\fB\\-\\-%s\\fR] ", option.Long) 145 } 146 } 147 148 // genOptionLong generates long info for option 149 func genOptionLong(option *usage.Option) string { 150 result := ".TP\n" 151 152 result += ".BR " 153 154 if option.Short != "" { 155 result += fmt.Sprintf("\\-%s \", \" ", option.Short) 156 } 157 158 result += fmt.Sprintf("\\-\\-%s", option.Long) 159 160 if option.Arg != "" { 161 result += fmt.Sprintf("\\fR=\\fI%s\\fR\n", strings.ToUpper(option.Arg)) 162 } else { 163 result += "\n" 164 } 165 166 result += fmtc.Clean(option.Desc) + "\n" 167 168 return result 169 } 170 171 // genDescription generates description part 172 func genDescription(info *usage.Info) string { 173 if info.Spoiler == "" { 174 return "" 175 } 176 177 return fmt.Sprintf( 178 ".SH DESCRIPTION\n\n%s\n\n", 179 fmtc.Clean(info.Spoiler), 180 ) 181 } 182 183 // genExamples generates examples part 184 func genExamples(info *usage.Info) string { 185 if len(info.Examples) == 0 { 186 return "" 187 } 188 189 result := ".SH EXAMPLES\n" 190 191 for index, example := range info.Examples { 192 result += ".TP\n" 193 194 if example.Desc != "" { 195 result += ".B • " + example.Desc + "\n" 196 } else { 197 result += fmt.Sprintf(".B • Example %d\n", index+1) 198 } 199 200 if !example.Raw { 201 result += fmt.Sprintf("%s %s\n", info.Name, example.Cmd) 202 } else { 203 result += fmt.Sprintf("%s\n", example.Cmd) 204 } 205 } 206 207 return result 208 } 209 210 // genBugTrackerInfo generates bugs part 211 func genBugTrackerInfo(about *usage.About) string { 212 if about.BugTracker == "" { 213 return "" 214 } 215 216 return fmt.Sprintf( 217 ".SH BUGS\n.PD 0\n\nPlease send any comments or bug reports to <\\fB%s\\fP>.\n\n", 218 about.BugTracker, 219 ) 220 } 221 222 // genLicense generates license part 223 func genLicense(about *usage.About) string { 224 if about.License == "" { 225 return "" 226 } 227 228 license := about.License 229 230 license = strings.Replace(license, "<", `<\fB`, -1) 231 license = strings.Replace(license, ">", `\fP>`, -1) 232 233 return fmt.Sprintf(".SH LICENSE\n\n%s.\n\n", license) 234 } 235 236 // genAuthor generates author part 237 func genAuthor(about *usage.About) string { 238 if about.Owner == "" { 239 return "" 240 } 241 242 if about.Year == 0 { 243 return fmt.Sprintf( 244 ".SH AUTHOR\n\nCopyright (C) %d \\fB%s\\fP\n\n", 245 time.Now().Year(), about.Owner, 246 ) 247 } 248 249 return fmt.Sprintf( 250 ".SH AUTHOR\n\nCopyright (C) %d-%d \\fB%s\\fP\n\n", 251 about.Year, time.Now().Year(), about.Owner, 252 ) 253 } 254 255 // formatCommandArgs formats command arguments 256 func formatCommandArgs(args []string) string { 257 result := "" 258 259 for _, arg := range args { 260 if strings.HasPrefix(arg, "?") { 261 result += fmt.Sprintf(" \\fR%s\\fP", strings.Replace(arg, "?", "", -1)) 262 } else { 263 result += fmt.Sprintf(" \\fI%s\\fP", arg) 264 } 265 } 266 267 return result + "\n" 268 }