diff --git a/Cargo.toml b/Cargo.toml index 5ecf927..e1902b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ workspace.resolver = "2" [workspace.dependencies] clap = { version = "4.5.3", features = ["derive"] } axum = { version = "0.7.5", features = ["ws"] } +matchit = { version = "0.7.3" } tokio = { version = "1", features = ["full"] } tokio-stream = { version = "0.1.15", features = ["sync"] } futures = "0.3.30" @@ -31,3 +32,4 @@ toml = { version = "0.8.12" } mime_guess = "2.0.4" tempfile = "3.10.1" insta = { version = "1.38.0", features = ["yaml"] } +miette = { version = "7.2.0", features = ["fancy", "syntect-highlighter"] } diff --git a/crates/bsnext_client/generated/dto.ts b/crates/bsnext_client/generated/dto.ts index c39d1b4..a1d37d5 100644 --- a/crates/bsnext_client/generated/dto.ts +++ b/crates/bsnext_client/generated/dto.ts @@ -51,38 +51,19 @@ export interface ServerDTO { socket_addr: string; } -export interface GetServersMessageResponse { +export interface GetServersMessageResponseDTO { servers: ServerDTO[]; } -export interface ServersChanged { - servers_resp: GetServersMessageResponse; +export interface ServersChangedDTO { + servers_resp: GetServersMessageResponseDTO; } -export enum EventLevel { - External = "BSLIVE_EXTERNAL", -} - -export type ExternalEvents = - | { kind: "ServersChanged", payload: ServersChanged } - | { kind: "Watching", payload: Watching } - | { kind: "WatchingStopped", payload: StoppedWatching } - | { kind: "FileChanged", payload: FileChanged } - | { kind: "FilesChanged", payload: FilesChangedDTO } - | { kind: "InputFileChanged", payload: FileChanged } - | { kind: "InputAccepted", payload: InputAccepted } - | { kind: "InputError", payload: InputErrorDTO }; - -export interface ExternalEvent { - level: EventLevel; - fields: ExternalEvents; -} - -export interface InputAccepted { +export interface InputAcceptedDTO { path: string; } -export interface FileChanged { +export interface FileChangedDTO { path: string; } @@ -95,12 +76,12 @@ export interface DebounceDTO { ms: string; } -export interface Watching { +export interface WatchingDTO { paths: string[]; debounce: DebounceDTO; } -export interface StoppedWatching { +export interface StoppedWatchingDTO { paths: string[]; } @@ -139,14 +120,24 @@ export interface ClientConfigDTO { * todo(alpha): clean this up */ export type InternalEventsDTO = - | { kind: "ServersChanged", payload: GetServersMessageResponse }; + | { kind: "ServersChanged", payload: GetServersMessageResponseDTO }; -export type StartupEvent = - | { kind: "Started", payload?: undefined } - | { kind: "FailedStartup", payload: StartupErrorDTO }; +export enum EventLevel { + External = "BSLIVE_EXTERNAL", +} -export type StartupErrorDTO = - | { kind: "InputError", payload: InputErrorDTO }; +export type ExternalEventsDTO = + | { kind: "ServersChanged", payload: ServersChangedDTO } + | { kind: "Watching", payload: WatchingDTO } + | { kind: "WatchingStopped", payload: StoppedWatchingDTO } + | { kind: "FileChanged", payload: FileChangedDTO } + | { kind: "FilesChanged", payload: FilesChangedDTO } + | { kind: "InputFileChanged", payload: FileChangedDTO } + | { kind: "InputAccepted", payload: InputAcceptedDTO }; + +export type StartupEventDTO = + | { kind: "Started", payload?: undefined } + | { kind: "FailedStartup", payload: string }; export type InputErrorDTO = | { kind: "MissingInputs", payload: string } @@ -161,7 +152,8 @@ export type InputErrorDTO = | { kind: "Io", payload: string } | { kind: "UnsupportedExtension", payload: string } | { kind: "MissingExtension", payload: string } - | { kind: "EmptyInput", payload: string }; + | { kind: "EmptyInput", payload: string } + | { kind: "BsLiveRules", payload: string }; export type ClientEvent = | { kind: "Change", payload: ChangeDTO } diff --git a/crates/bsnext_client/generated/schema.js b/crates/bsnext_client/generated/schema.js index 8b61d02..582e6e9 100644 --- a/crates/bsnext_client/generated/schema.js +++ b/crates/bsnext_client/generated/schema.js @@ -2,10 +2,6 @@ import { z } from "zod"; // generated/dto.ts -var EventLevel = /* @__PURE__ */ ((EventLevel2) => { - EventLevel2["External"] = "BSLIVE_EXTERNAL"; - return EventLevel2; -})(EventLevel || {}); var LogLevelDTO = /* @__PURE__ */ ((LogLevelDTO2) => { LogLevelDTO2["Info"] = "info"; LogLevelDTO2["Debug"] = "debug"; @@ -13,6 +9,10 @@ var LogLevelDTO = /* @__PURE__ */ ((LogLevelDTO2) => { LogLevelDTO2["Error"] = "error"; return LogLevelDTO2; })(LogLevelDTO || {}); +var EventLevel = /* @__PURE__ */ ((EventLevel2) => { + EventLevel2["External"] = "BSLIVE_EXTERNAL"; + return EventLevel2; +})(EventLevel || {}); var ChangeKind = /* @__PURE__ */ ((ChangeKind2) => { ChangeKind2["Changed"] = "Changed"; ChangeKind2["Added"] = "Added"; @@ -94,87 +94,32 @@ var serverDTOSchema = z.object({ identity: serverIdentityDTOSchema, socket_addr: z.string() }); -var getServersMessageResponseSchema = z.object({ +var getServersMessageResponseDTOSchema = z.object({ servers: z.array(serverDTOSchema) }); -var serversChangedSchema = z.object({ - servers_resp: getServersMessageResponseSchema +var serversChangedDTOSchema = z.object({ + servers_resp: getServersMessageResponseDTOSchema }); -var eventLevelSchema = z.nativeEnum(EventLevel); -var stoppedWatchingSchema = z.object({ - paths: z.array(z.string()) +var inputAcceptedDTOSchema = z.object({ + path: z.string() }); -var fileChangedSchema = z.object({ +var fileChangedDTOSchema = z.object({ path: z.string() }); var filesChangedDTOSchema = z.object({ paths: z.array(z.string()) }); -var inputAcceptedSchema = z.object({ - path: z.string() -}); -var inputErrorDTOSchema = z.union([ - z.object({ - kind: z.literal("MissingInputs"), - payload: z.string() - }), - z.object({ - kind: z.literal("InvalidInput"), - payload: z.string() - }), - z.object({ - kind: z.literal("NotFound"), - payload: z.string() - }), - z.object({ - kind: z.literal("InputWriteError"), - payload: z.string() - }), - z.object({ - kind: z.literal("PathError"), - payload: z.string() - }), - z.object({ - kind: z.literal("PortError"), - payload: z.string() - }), - z.object({ - kind: z.literal("DirError"), - payload: z.string() - }), - z.object({ - kind: z.literal("YamlError"), - payload: z.string() - }), - z.object({ - kind: z.literal("MarkdownError"), - payload: z.string() - }), - z.object({ - kind: z.literal("Io"), - payload: z.string() - }), - z.object({ - kind: z.literal("UnsupportedExtension"), - payload: z.string() - }), - z.object({ - kind: z.literal("MissingExtension"), - payload: z.string() - }), - z.object({ - kind: z.literal("EmptyInput"), - payload: z.string() - }) -]); var debounceDTOSchema = z.object({ kind: z.string(), ms: z.string() }); -var watchingSchema = z.object({ +var watchingDTOSchema = z.object({ paths: z.array(z.string()), debounce: debounceDTOSchema }); +var stoppedWatchingDTOSchema = z.object({ + paths: z.array(z.string()) +}); var serverChangeSchema = z.union([ z.object({ kind: z.literal("Stopped"), @@ -210,44 +155,25 @@ var clientConfigDTOSchema = z.object({ }); var internalEventsDTOSchema = z.object({ kind: z.literal("ServersChanged"), - payload: getServersMessageResponseSchema -}); -var startupErrorDTOSchema = z.object({ - kind: z.literal("InputError"), - payload: inputErrorDTOSchema + payload: getServersMessageResponseDTOSchema }); -var changeKindSchema = z.nativeEnum(ChangeKind); -var changeDTOSchema = z.lazy( - () => z.union([ - z.object({ - kind: z.literal("Fs"), - payload: z.object({ - path: z.string(), - change_kind: changeKindSchema - }) - }), - z.object({ - kind: z.literal("FsMany"), - payload: z.array(changeDTOSchema) - }) - ]) -); -var externalEventsSchema = z.union([ +var eventLevelSchema = z.nativeEnum(EventLevel); +var externalEventsDTOSchema = z.union([ z.object({ kind: z.literal("ServersChanged"), - payload: serversChangedSchema + payload: serversChangedDTOSchema }), z.object({ kind: z.literal("Watching"), - payload: watchingSchema + payload: watchingDTOSchema }), z.object({ kind: z.literal("WatchingStopped"), - payload: stoppedWatchingSchema + payload: stoppedWatchingDTOSchema }), z.object({ kind: z.literal("FileChanged"), - payload: fileChangedSchema + payload: fileChangedDTOSchema }), z.object({ kind: z.literal("FilesChanged"), @@ -255,31 +181,97 @@ var externalEventsSchema = z.union([ }), z.object({ kind: z.literal("InputFileChanged"), - payload: fileChangedSchema + payload: fileChangedDTOSchema }), z.object({ kind: z.literal("InputAccepted"), - payload: inputAcceptedSchema - }), - z.object({ - kind: z.literal("InputError"), - payload: inputErrorDTOSchema + payload: inputAcceptedDTOSchema }) ]); -var externalEventSchema = z.object({ - level: eventLevelSchema, - fields: externalEventsSchema -}); -var startupEventSchema = z.union([ +var startupEventDTOSchema = z.union([ z.object({ kind: z.literal("Started"), payload: z.undefined().optional() }), z.object({ kind: z.literal("FailedStartup"), - payload: startupErrorDTOSchema + payload: z.string() + }) +]); +var inputErrorDTOSchema = z.union([ + z.object({ + kind: z.literal("MissingInputs"), + payload: z.string() + }), + z.object({ + kind: z.literal("InvalidInput"), + payload: z.string() + }), + z.object({ + kind: z.literal("NotFound"), + payload: z.string() + }), + z.object({ + kind: z.literal("InputWriteError"), + payload: z.string() + }), + z.object({ + kind: z.literal("PathError"), + payload: z.string() + }), + z.object({ + kind: z.literal("PortError"), + payload: z.string() + }), + z.object({ + kind: z.literal("DirError"), + payload: z.string() + }), + z.object({ + kind: z.literal("YamlError"), + payload: z.string() + }), + z.object({ + kind: z.literal("MarkdownError"), + payload: z.string() + }), + z.object({ + kind: z.literal("Io"), + payload: z.string() + }), + z.object({ + kind: z.literal("UnsupportedExtension"), + payload: z.string() + }), + z.object({ + kind: z.literal("MissingExtension"), + payload: z.string() + }), + z.object({ + kind: z.literal("EmptyInput"), + payload: z.string() + }), + z.object({ + kind: z.literal("BsLiveRules"), + payload: z.string() }) ]); +var changeKindSchema = z.nativeEnum(ChangeKind); +var changeDTOSchema = z.lazy( + () => z.union([ + z.object({ + kind: z.literal("Fs"), + payload: z.object({ + path: z.string(), + change_kind: changeKindSchema + }) + }), + z.object({ + kind: z.literal("FsMany"), + payload: z.array(changeDTOSchema) + }) + ]) +); var clientEventSchema = z.union([ z.object({ kind: z.literal("Change"), @@ -297,12 +289,11 @@ export { clientEventSchema, debounceDTOSchema, eventLevelSchema, - externalEventSchema, - externalEventsSchema, - fileChangedSchema, + externalEventsDTOSchema, + fileChangedDTOSchema, filesChangedDTOSchema, - getServersMessageResponseSchema, - inputAcceptedSchema, + getServersMessageResponseDTOSchema, + inputAcceptedDTOSchema, inputErrorDTOSchema, internalEventsDTOSchema, logLevelDTOSchema, @@ -314,9 +305,8 @@ export { serverDTOSchema, serverDescSchema, serverIdentityDTOSchema, - serversChangedSchema, - startupErrorDTOSchema, - startupEventSchema, - stoppedWatchingSchema, - watchingSchema + serversChangedDTOSchema, + startupEventDTOSchema, + stoppedWatchingDTOSchema, + watchingDTOSchema }; diff --git a/crates/bsnext_client/generated/schema.ts b/crates/bsnext_client/generated/schema.ts index 7109617..3079c3d 100644 --- a/crates/bsnext_client/generated/schema.ts +++ b/crates/bsnext_client/generated/schema.ts @@ -1,6 +1,6 @@ // Generated by ts-to-zod import { z } from "zod"; -import { EventLevel, LogLevelDTO, ChangeKind, ChangeDTO } from "./dto"; +import { LogLevelDTO, EventLevel, ChangeKind, ChangeDTO } from "./dto"; export const routeKindDTOSchema = z.union([ z.object({ @@ -80,21 +80,19 @@ export const serverDTOSchema = z.object({ socket_addr: z.string(), }); -export const getServersMessageResponseSchema = z.object({ +export const getServersMessageResponseDTOSchema = z.object({ servers: z.array(serverDTOSchema), }); -export const serversChangedSchema = z.object({ - servers_resp: getServersMessageResponseSchema, +export const serversChangedDTOSchema = z.object({ + servers_resp: getServersMessageResponseDTOSchema, }); -export const eventLevelSchema = z.nativeEnum(EventLevel); - -export const stoppedWatchingSchema = z.object({ - paths: z.array(z.string()), +export const inputAcceptedDTOSchema = z.object({ + path: z.string(), }); -export const fileChangedSchema = z.object({ +export const fileChangedDTOSchema = z.object({ path: z.string(), }); @@ -102,75 +100,20 @@ export const filesChangedDTOSchema = z.object({ paths: z.array(z.string()), }); -export const inputAcceptedSchema = z.object({ - path: z.string(), -}); - -export const inputErrorDTOSchema = z.union([ - z.object({ - kind: z.literal("MissingInputs"), - payload: z.string(), - }), - z.object({ - kind: z.literal("InvalidInput"), - payload: z.string(), - }), - z.object({ - kind: z.literal("NotFound"), - payload: z.string(), - }), - z.object({ - kind: z.literal("InputWriteError"), - payload: z.string(), - }), - z.object({ - kind: z.literal("PathError"), - payload: z.string(), - }), - z.object({ - kind: z.literal("PortError"), - payload: z.string(), - }), - z.object({ - kind: z.literal("DirError"), - payload: z.string(), - }), - z.object({ - kind: z.literal("YamlError"), - payload: z.string(), - }), - z.object({ - kind: z.literal("MarkdownError"), - payload: z.string(), - }), - z.object({ - kind: z.literal("Io"), - payload: z.string(), - }), - z.object({ - kind: z.literal("UnsupportedExtension"), - payload: z.string(), - }), - z.object({ - kind: z.literal("MissingExtension"), - payload: z.string(), - }), - z.object({ - kind: z.literal("EmptyInput"), - payload: z.string(), - }), -]); - export const debounceDTOSchema = z.object({ kind: z.string(), ms: z.string(), }); -export const watchingSchema = z.object({ +export const watchingDTOSchema = z.object({ paths: z.array(z.string()), debounce: debounceDTOSchema, }); +export const stoppedWatchingDTOSchema = z.object({ + paths: z.array(z.string()), +}); + export const serverChangeSchema = z.union([ z.object({ kind: z.literal("Stopped"), @@ -211,48 +154,27 @@ export const clientConfigDTOSchema = z.object({ export const internalEventsDTOSchema = z.object({ kind: z.literal("ServersChanged"), - payload: getServersMessageResponseSchema, + payload: getServersMessageResponseDTOSchema, }); -export const startupErrorDTOSchema = z.object({ - kind: z.literal("InputError"), - payload: inputErrorDTOSchema, -}); - -export const changeKindSchema = z.nativeEnum(ChangeKind); - -export const changeDTOSchema: z.ZodSchema = z.lazy(() => - z.union([ - z.object({ - kind: z.literal("Fs"), - payload: z.object({ - path: z.string(), - change_kind: changeKindSchema, - }), - }), - z.object({ - kind: z.literal("FsMany"), - payload: z.array(changeDTOSchema), - }), - ]), -); +export const eventLevelSchema = z.nativeEnum(EventLevel); -export const externalEventsSchema = z.union([ +export const externalEventsDTOSchema = z.union([ z.object({ kind: z.literal("ServersChanged"), - payload: serversChangedSchema, + payload: serversChangedDTOSchema, }), z.object({ kind: z.literal("Watching"), - payload: watchingSchema, + payload: watchingDTOSchema, }), z.object({ kind: z.literal("WatchingStopped"), - payload: stoppedWatchingSchema, + payload: stoppedWatchingDTOSchema, }), z.object({ kind: z.literal("FileChanged"), - payload: fileChangedSchema, + payload: fileChangedDTOSchema, }), z.object({ kind: z.literal("FilesChanged"), @@ -260,34 +182,102 @@ export const externalEventsSchema = z.union([ }), z.object({ kind: z.literal("InputFileChanged"), - payload: fileChangedSchema, + payload: fileChangedDTOSchema, }), z.object({ kind: z.literal("InputAccepted"), - payload: inputAcceptedSchema, - }), - z.object({ - kind: z.literal("InputError"), - payload: inputErrorDTOSchema, + payload: inputAcceptedDTOSchema, }), ]); -export const externalEventSchema = z.object({ - level: eventLevelSchema, - fields: externalEventsSchema, -}); - -export const startupEventSchema = z.union([ +export const startupEventDTOSchema = z.union([ z.object({ kind: z.literal("Started"), payload: z.undefined().optional(), }), z.object({ kind: z.literal("FailedStartup"), - payload: startupErrorDTOSchema, + payload: z.string(), }), ]); +export const inputErrorDTOSchema = z.union([ + z.object({ + kind: z.literal("MissingInputs"), + payload: z.string(), + }), + z.object({ + kind: z.literal("InvalidInput"), + payload: z.string(), + }), + z.object({ + kind: z.literal("NotFound"), + payload: z.string(), + }), + z.object({ + kind: z.literal("InputWriteError"), + payload: z.string(), + }), + z.object({ + kind: z.literal("PathError"), + payload: z.string(), + }), + z.object({ + kind: z.literal("PortError"), + payload: z.string(), + }), + z.object({ + kind: z.literal("DirError"), + payload: z.string(), + }), + z.object({ + kind: z.literal("YamlError"), + payload: z.string(), + }), + z.object({ + kind: z.literal("MarkdownError"), + payload: z.string(), + }), + z.object({ + kind: z.literal("Io"), + payload: z.string(), + }), + z.object({ + kind: z.literal("UnsupportedExtension"), + payload: z.string(), + }), + z.object({ + kind: z.literal("MissingExtension"), + payload: z.string(), + }), + z.object({ + kind: z.literal("EmptyInput"), + payload: z.string(), + }), + z.object({ + kind: z.literal("BsLiveRules"), + payload: z.string(), + }), +]); + +export const changeKindSchema = z.nativeEnum(ChangeKind); + +export const changeDTOSchema: z.ZodSchema = z.lazy(() => + z.union([ + z.object({ + kind: z.literal("Fs"), + payload: z.object({ + path: z.string(), + change_kind: changeKindSchema, + }), + }), + z.object({ + kind: z.literal("FsMany"), + payload: z.array(changeDTOSchema), + }), + ]), +); + export const clientEventSchema = z.union([ z.object({ kind: z.literal("Change"), diff --git a/crates/bsnext_client/inject/dist/index.js b/crates/bsnext_client/inject/dist/index.js index a7b5705..3316a2c 100644 --- a/crates/bsnext_client/inject/dist/index.js +++ b/crates/bsnext_client/inject/dist/index.js @@ -6277,10 +6277,6 @@ var z = /* @__PURE__ */ Object.freeze({ }); // ../generated/schema.js -var EventLevel = /* @__PURE__ */ ((EventLevel2) => { - EventLevel2["External"] = "BSLIVE_EXTERNAL"; - return EventLevel2; -})(EventLevel || {}); var LogLevelDTO = /* @__PURE__ */ ((LogLevelDTO22) => { LogLevelDTO22["Info"] = "info"; LogLevelDTO22["Debug"] = "debug"; @@ -6288,6 +6284,10 @@ var LogLevelDTO = /* @__PURE__ */ ((LogLevelDTO22) => { LogLevelDTO22["Error"] = "error"; return LogLevelDTO22; })(LogLevelDTO || {}); +var EventLevel = /* @__PURE__ */ ((EventLevel2) => { + EventLevel2["External"] = "BSLIVE_EXTERNAL"; + return EventLevel2; +})(EventLevel || {}); var ChangeKind = /* @__PURE__ */ ((ChangeKind2) => { ChangeKind2["Changed"] = "Changed"; ChangeKind2["Added"] = "Added"; @@ -6367,87 +6367,32 @@ var serverDTOSchema = z.object({ identity: serverIdentityDTOSchema, socket_addr: z.string() }); -var getServersMessageResponseSchema = z.object({ +var getServersMessageResponseDTOSchema = z.object({ servers: z.array(serverDTOSchema) }); -var serversChangedSchema = z.object({ - servers_resp: getServersMessageResponseSchema +var serversChangedDTOSchema = z.object({ + servers_resp: getServersMessageResponseDTOSchema }); -var eventLevelSchema = z.nativeEnum(EventLevel); -var stoppedWatchingSchema = z.object({ - paths: z.array(z.string()) +var inputAcceptedDTOSchema = z.object({ + path: z.string() }); -var fileChangedSchema = z.object({ +var fileChangedDTOSchema = z.object({ path: z.string() }); var filesChangedDTOSchema = z.object({ paths: z.array(z.string()) }); -var inputAcceptedSchema = z.object({ - path: z.string() -}); -var inputErrorDTOSchema = z.union([ - z.object({ - kind: z.literal("MissingInputs"), - payload: z.string() - }), - z.object({ - kind: z.literal("InvalidInput"), - payload: z.string() - }), - z.object({ - kind: z.literal("NotFound"), - payload: z.string() - }), - z.object({ - kind: z.literal("InputWriteError"), - payload: z.string() - }), - z.object({ - kind: z.literal("PathError"), - payload: z.string() - }), - z.object({ - kind: z.literal("PortError"), - payload: z.string() - }), - z.object({ - kind: z.literal("DirError"), - payload: z.string() - }), - z.object({ - kind: z.literal("YamlError"), - payload: z.string() - }), - z.object({ - kind: z.literal("MarkdownError"), - payload: z.string() - }), - z.object({ - kind: z.literal("Io"), - payload: z.string() - }), - z.object({ - kind: z.literal("UnsupportedExtension"), - payload: z.string() - }), - z.object({ - kind: z.literal("MissingExtension"), - payload: z.string() - }), - z.object({ - kind: z.literal("EmptyInput"), - payload: z.string() - }) -]); var debounceDTOSchema = z.object({ kind: z.string(), ms: z.string() }); -var watchingSchema = z.object({ +var watchingDTOSchema = z.object({ paths: z.array(z.string()), debounce: debounceDTOSchema }); +var stoppedWatchingDTOSchema = z.object({ + paths: z.array(z.string()) +}); var serverChangeSchema = z.union([ z.object({ kind: z.literal("Stopped"), @@ -6483,44 +6428,25 @@ var clientConfigDTOSchema = z.object({ }); var internalEventsDTOSchema = z.object({ kind: z.literal("ServersChanged"), - payload: getServersMessageResponseSchema -}); -var startupErrorDTOSchema = z.object({ - kind: z.literal("InputError"), - payload: inputErrorDTOSchema + payload: getServersMessageResponseDTOSchema }); -var changeKindSchema = z.nativeEnum(ChangeKind); -var changeDTOSchema = z.lazy( - () => z.union([ - z.object({ - kind: z.literal("Fs"), - payload: z.object({ - path: z.string(), - change_kind: changeKindSchema - }) - }), - z.object({ - kind: z.literal("FsMany"), - payload: z.array(changeDTOSchema) - }) - ]) -); -var externalEventsSchema = z.union([ +var eventLevelSchema = z.nativeEnum(EventLevel); +var externalEventsDTOSchema = z.union([ z.object({ kind: z.literal("ServersChanged"), - payload: serversChangedSchema + payload: serversChangedDTOSchema }), z.object({ kind: z.literal("Watching"), - payload: watchingSchema + payload: watchingDTOSchema }), z.object({ kind: z.literal("WatchingStopped"), - payload: stoppedWatchingSchema + payload: stoppedWatchingDTOSchema }), z.object({ kind: z.literal("FileChanged"), - payload: fileChangedSchema + payload: fileChangedDTOSchema }), z.object({ kind: z.literal("FilesChanged"), @@ -6528,31 +6454,97 @@ var externalEventsSchema = z.union([ }), z.object({ kind: z.literal("InputFileChanged"), - payload: fileChangedSchema + payload: fileChangedDTOSchema }), z.object({ kind: z.literal("InputAccepted"), - payload: inputAcceptedSchema - }), - z.object({ - kind: z.literal("InputError"), - payload: inputErrorDTOSchema + payload: inputAcceptedDTOSchema }) ]); -var externalEventSchema = z.object({ - level: eventLevelSchema, - fields: externalEventsSchema -}); -var startupEventSchema = z.union([ +var startupEventDTOSchema = z.union([ z.object({ kind: z.literal("Started"), payload: z.undefined().optional() }), z.object({ kind: z.literal("FailedStartup"), - payload: startupErrorDTOSchema + payload: z.string() + }) +]); +var inputErrorDTOSchema = z.union([ + z.object({ + kind: z.literal("MissingInputs"), + payload: z.string() + }), + z.object({ + kind: z.literal("InvalidInput"), + payload: z.string() + }), + z.object({ + kind: z.literal("NotFound"), + payload: z.string() + }), + z.object({ + kind: z.literal("InputWriteError"), + payload: z.string() + }), + z.object({ + kind: z.literal("PathError"), + payload: z.string() + }), + z.object({ + kind: z.literal("PortError"), + payload: z.string() + }), + z.object({ + kind: z.literal("DirError"), + payload: z.string() + }), + z.object({ + kind: z.literal("YamlError"), + payload: z.string() + }), + z.object({ + kind: z.literal("MarkdownError"), + payload: z.string() + }), + z.object({ + kind: z.literal("Io"), + payload: z.string() + }), + z.object({ + kind: z.literal("UnsupportedExtension"), + payload: z.string() + }), + z.object({ + kind: z.literal("MissingExtension"), + payload: z.string() + }), + z.object({ + kind: z.literal("EmptyInput"), + payload: z.string() + }), + z.object({ + kind: z.literal("BsLiveRules"), + payload: z.string() }) ]); +var changeKindSchema = z.nativeEnum(ChangeKind); +var changeDTOSchema = z.lazy( + () => z.union([ + z.object({ + kind: z.literal("Fs"), + payload: z.object({ + path: z.string(), + change_kind: changeKindSchema + }) + }), + z.object({ + kind: z.literal("FsMany"), + payload: z.array(changeDTOSchema) + }) + ]) +); var clientEventSchema = z.union([ z.object({ kind: z.literal("Change"), diff --git a/crates/bsnext_client/ui/dist/index.js b/crates/bsnext_client/ui/dist/index.js index 253389e..19fbb56 100644 --- a/crates/bsnext_client/ui/dist/index.js +++ b/crates/bsnext_client/ui/dist/index.js @@ -883,7 +883,8 @@ customElements.define("bs-icon", BsIcon); var all = fetch("/__bs_api/servers").then((x2) => x2.json()); var me = fetch("/__bs_api/me").then((x2) => x2.json()); Promise.all([all, me]).then(([servers, me2]) => { - let next = ke``; + let next = ke` + `; let app = document.querySelector("#app"); if (!app) throw new Error("cannot..."); diff --git a/crates/bsnext_client/ui/src/components/bs-debug.ts b/crates/bsnext_client/ui/src/components/bs-debug.ts index 15ad572..0c130a9 100644 --- a/crates/bsnext_client/ui/src/components/bs-debug.ts +++ b/crates/bsnext_client/ui/src/components/bs-debug.ts @@ -1,10 +1,10 @@ import {css, html, LitElement} from "lit"; import {property} from "lit/decorators.js"; -import {GetServersMessageResponse, ServerDesc, ServerDTO} from "../../../generated/dto"; +import {GetServersMessageResponseDTO, ServerDesc, ServerDTO} from "../../../generated/dto"; class BsDebug extends LitElement { @property({type: Object}) - servers: GetServersMessageResponse = {servers: []}; + servers: GetServersMessageResponseDTO = {servers: []}; @property({type: Object}) me: ServerDesc = {routes: [], id: ''} diff --git a/crates/bsnext_client/ui/src/index.ts b/crates/bsnext_client/ui/src/index.ts index 6493dc1..6c365a7 100644 --- a/crates/bsnext_client/ui/src/index.ts +++ b/crates/bsnext_client/ui/src/index.ts @@ -5,15 +5,16 @@ import "./components/bs-server-detail"; import "./components/bs-server-identity"; import "./components/bs-header"; import "./components/bs-icon"; -import {GetServersMessageResponse, ServerDesc} from "../../generated/dto"; +import {GetServersMessageResponseDTO, ServerDesc} from "../../generated/dto"; import {html, render} from "lit"; const all = fetch('/__bs_api/servers').then(x => x.json()) const me = fetch('/__bs_api/me').then(x => x.json()) Promise.all([all, me]) - .then(([servers, me]: [GetServersMessageResponse, ServerDesc]) => { - let next = html`` + .then(([servers, me]: [GetServersMessageResponseDTO, ServerDesc]) => { + let next = html` + ` let app = document.querySelector('#app') as HTMLElement; if (!app) throw new Error('cannot...'); // console.log(x); diff --git a/crates/bsnext_core/Cargo.toml b/crates/bsnext_core/Cargo.toml index f437057..4eeeb17 100644 --- a/crates/bsnext_core/Cargo.toml +++ b/crates/bsnext_core/Cargo.toml @@ -20,7 +20,7 @@ axum-extra = { version = "0.9.3", features = ["typed-header"] } hyper = { version = "1.0.0", features = ["full"] } hyper-tls = { version = "0.6.0" } hyper-util = { version = "0.1.1", features = ["client-legacy"] } -matchit = { version = "0.7.3" } +matchit = { workspace = true } htmlescape = { version = "0.3.1" } mime_guess = { workspace = true } diff --git a/crates/bsnext_core/examples/abc.rs b/crates/bsnext_core/examples/abc.rs index 83d1440..44051eb 100644 --- a/crates/bsnext_core/examples/abc.rs +++ b/crates/bsnext_core/examples/abc.rs @@ -2,7 +2,7 @@ use actix::Actor; use bsnext_core::server::actor::ServerActor; use bsnext_core::server::handler_listen::Listen; use bsnext_core::servers_supervisor::get_servers_handler::{GetServersMessage, IncomingEvents}; -use bsnext_dto::GetServersMessageResponse; +use bsnext_dto::GetServersMessageResponseDTO; use bsnext_input::route::{JsonWrapper, Route, RouteKind}; use bsnext_input::server_config::{ServerConfig, ServerIdentity}; use serde_json::Value; @@ -12,13 +12,13 @@ use tokio::sync::oneshot; async fn main() { let (_tx, rx) = oneshot::channel::<()>(); let route1 = Route { - path: "/".to_string(), + path: "/".parse().unwrap(), kind: RouteKind::new_html("hello world!"), ..Default::default() }; let value: Value = serde_json::from_str("[]").expect("json"); let route2 = Route { - path: "/j".to_string(), + path: "/j".parse().unwrap(), kind: RouteKind::new_json(JsonWrapper(value)), ..Default::default() }; @@ -73,7 +73,7 @@ impl actix::Actor for ServerParent { type Context = actix::Context; } impl actix::Handler for ServerParent { - type Result = GetServersMessageResponse; + type Result = GetServersMessageResponseDTO; fn handle(&mut self, _msg: GetServersMessage, _ctx: &mut Self::Context) -> Self::Result { todo!("woop!") diff --git a/crates/bsnext_core/src/handler_stack.rs b/crates/bsnext_core/src/handler_stack.rs index 5c24dd2..5e12993 100644 --- a/crates/bsnext_core/src/handler_stack.rs +++ b/crates/bsnext_core/src/handler_stack.rs @@ -14,10 +14,20 @@ use tower_http::services::{ServeDir, ServeFile}; pub enum HandlerStack { None, // todo: make this a separate thing - Raw { raw: RawRoute, opts: Opts }, + Raw { + raw: RawRoute, + opts: Opts, + }, Dirs(Vec), - Proxy { proxy: ProxyRoute, opts: Opts }, - DirsProxy(Vec, ProxyRoute), + Proxy { + proxy: ProxyRoute, + opts: Opts, + }, + DirsProxy { + dirs: Vec, + proxy: ProxyRoute, + opts: Opts, + }, } #[derive(Debug, PartialEq)] @@ -83,7 +93,7 @@ impl RouteMap { mapping: routes .iter() .fold(HashMap::>::new(), |mut acc, route| { - acc.entry(route.path.clone()) + acc.entry(route.path.as_str().to_string()) .and_modify(|acc| acc.push(route.clone())) .or_insert(vec![route.clone()]); acc @@ -143,23 +153,32 @@ pub fn append_stack(state: HandlerStack, route: Route) -> HandlerStack { dirs.push(DirRouteOpts::new(next_dir, route.opts, route.fallback)); HandlerStack::Dirs(dirs) } - RouteKind::Proxy(proxy) => HandlerStack::DirsProxy(dirs, proxy), + RouteKind::Proxy(proxy) => HandlerStack::DirsProxy { + dirs, + proxy, + opts: route.opts, + }, _ => HandlerStack::Dirs(dirs), }, HandlerStack::Proxy { proxy, opts } => match route.kind { - RouteKind::Dir(dir) => HandlerStack::DirsProxy( - vec![DirRouteOpts::new(dir, route.opts, route.fallback)], + RouteKind::Dir(dir) => HandlerStack::DirsProxy { + dirs: vec![DirRouteOpts::new(dir, route.opts, route.fallback)], proxy, - ), + opts, + }, _ => HandlerStack::Proxy { proxy, opts }, }, - HandlerStack::DirsProxy(mut dirs, proxy) => match route.kind { + HandlerStack::DirsProxy { + mut dirs, + proxy, + opts, + } => match route.kind { RouteKind::Dir(dir) => { dirs.push(DirRouteOpts::new(dir, route.opts, route.fallback)); - HandlerStack::DirsProxy(dirs, proxy) + HandlerStack::DirsProxy { dirs, proxy, opts } } // todo(alpha): how to handle multiple proxies? should it just override for now? - _ => HandlerStack::DirsProxy(dirs, proxy), + _ => HandlerStack::DirsProxy { dirs, proxy, opts }, }, } } @@ -196,11 +215,11 @@ pub fn stack_to_router(path: &str, stack: HandlerStack) -> Router { HandlerStack::Raw { raw, opts } => { let svc = any_service(serve_raw_one.with_state(raw)); let out = optional_layers(svc, &opts); - Router::new().route_service(path, out) } HandlerStack::Dirs(dirs) => { - Router::new().nest_service(path, serve_dir_layer(&dirs, Router::new())) + let service = serve_dir_layer(&dirs, Router::new()); + Router::new().nest_service(path, service) } HandlerStack::Proxy { proxy, opts } => { let proxy_config = ProxyConfig { @@ -213,15 +232,9 @@ pub fn stack_to_router(path: &str, stack: HandlerStack) -> Router { Router::new().nest_service(path, optional_layers(as_service, &opts)) } - HandlerStack::DirsProxy(dir_list, proxy) => { - let r2 = stack_to_router( - path, - HandlerStack::Proxy { - proxy, - opts: Default::default(), - }, - ); - let r1 = serve_dir_layer(&dir_list, Router::new().fallback_service(r2)); + HandlerStack::DirsProxy { dirs, proxy, opts } => { + let proxy_router = stack_to_router(path, HandlerStack::Proxy { proxy, opts }); + let r1 = serve_dir_layer(&dirs, Router::new().fallback_service(proxy_router)); Router::new().nest_service(path, r1) } } @@ -254,12 +267,15 @@ fn serve_dir_layer(dir_list_with_opts: &[DirRouteOpts], initial: Router) -> Rout #[cfg(test)] mod test { use super::*; - use crate::server::router::common::to_resp_parts_and_body; use axum::body::Body; + use bsnext_input::Input; use http::Request; use insta::assert_debug_snapshot; + + + use tower::ServiceExt; #[test] diff --git a/crates/bsnext_core/src/handlers/proxy.rs b/crates/bsnext_core/src/handlers/proxy.rs index 74418ae..4eaf9fd 100644 --- a/crates/bsnext_core/src/handlers/proxy.rs +++ b/crates/bsnext_core/src/handlers/proxy.rs @@ -13,6 +13,7 @@ use hyper_util::client::legacy::connect::HttpConnector; use hyper_util::client::legacy::Client; use tower::ServiceExt; use tower_http::decompression::DecompressionLayer; +use tracing::trace_span; #[derive(Debug, Clone)] pub struct ProxyConfig { @@ -44,8 +45,11 @@ where pub async fn proxy_handler( Extension(config): Extension, + uri: Uri, req: Request, ) -> Result { + let s = trace_span!(parent: None, "proxy_handler", ?uri); + let _g = s.enter(); let target = config.target.clone(); tracing::trace!(?config); @@ -92,17 +96,17 @@ pub async fn proxy_handler( "will accept request + decompress" ); if req_accepted { - let sv2 = any(serve_raw_one.layer(DecompressionLayer::new())); + let sv2 = any(serve_one_proxy_req.layer(DecompressionLayer::new())); return Ok(sv2.oneshot(req).await.into_response()); } } - let sv2 = any(serve_raw_one); + let sv2 = any(serve_one_proxy_req); Ok(sv2.oneshot(req).await.into_response()) } -async fn serve_raw_one(req: Request) -> Response { - tracing::trace!("serve_raw_one {}", req.uri().to_string()); +async fn serve_one_proxy_req(req: Request) -> Response { + tracing::trace!("serve_one_proxy_req {}", req.uri().to_string()); let client = { req.extensions() .get::, Body>>() diff --git a/crates/bsnext_core/src/not_found/not_found_service.rs b/crates/bsnext_core/src/not_found/not_found_service.rs index fd37e4a..c7dfdbe 100644 --- a/crates/bsnext_core/src/not_found/not_found_service.rs +++ b/crates/bsnext_core/src/not_found/not_found_service.rs @@ -13,7 +13,7 @@ use bsnext_client::html_with_base; use tracing::{span, Level}; pub async fn not_found_loader(req: Request, next: Next) -> Result { - let span = span!(parent: None, Level::INFO, "not_found", path = req.uri().path()); + let span = span!(parent: None, Level::INFO, "not_found_mw", path = req.uri().path()); let _guard = span.enter(); tracing::info!("not_found->"); @@ -36,6 +36,24 @@ pub async fn not_found_loader(req: Request, next: Next) -> Result Result { + let span = span!(parent: None, Level::INFO, "not_found_srv", path = req.uri().path()); + let _guard = span.enter(); + tracing::info!("responding"); + + let markup = html_with_base("/__bs_assets/ui/"); + + Ok(( + StatusCode::NOT_FOUND, + [( + CONTENT_TYPE, + HeaderValue::from_static(mime::TEXT_HTML_UTF_8.as_ref()), + )], + markup, + ) + .into_response()) +} + pub async fn maybe_proxy(req: Request, next: Next) -> impl IntoResponse { let span = span!(parent: None, Level::INFO, "maybe_proxy", path = req.uri().path()); let _guard = span.enter(); diff --git a/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs b/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs index 90b6fea..1c7aecc 100644 --- a/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +++ b/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs @@ -1,17 +1,17 @@ use crate::servers_supervisor::actor::ServersSupervisor; use crate::servers_supervisor::file_changed_handler::FilesChanged; use actix::AsyncContext; -use bsnext_dto::{GetServersMessageResponse, ServerDTO}; +use bsnext_dto::{GetServersMessageResponseDTO, ServerDTO}; #[derive(actix::Message)] -#[rtype(result = "GetServersMessageResponse")] +#[rtype(result = "GetServersMessageResponseDTO")] pub struct GetServersMessage; impl actix::Handler for ServersSupervisor { - type Result = GetServersMessageResponse; + type Result = GetServersMessageResponseDTO; fn handle(&mut self, _msg: GetServersMessage, _ctx: &mut Self::Context) -> Self::Result { - GetServersMessageResponse { + GetServersMessageResponseDTO { servers: self .handlers .iter() diff --git a/crates/bsnext_core/src/snapshots/bsnext_core__handler_stack__test__handler_stack_02.snap b/crates/bsnext_core/src/snapshots/bsnext_core__handler_stack__test__handler_stack_02.snap index 02c7077..2e39696 100644 --- a/crates/bsnext_core/src/snapshots/bsnext_core__handler_stack__test__handler_stack_02.snap +++ b/crates/bsnext_core/src/snapshots/bsnext_core__handler_stack__test__handler_stack_02.snap @@ -2,8 +2,8 @@ source: crates/bsnext_core/src/handler_stack.rs expression: actual --- -DirsProxy( - [ +DirsProxy { + dirs: [ DirRouteOpts { dir_route: DirRoute { dir: "another", @@ -43,7 +43,19 @@ DirsProxy( fallback_route: None, }, ], - ProxyRoute { + proxy: ProxyRoute { proxy: "example.com", }, -) + opts: Opts { + cors: None, + delay: None, + watch: Bool( + true, + ), + inject: Bool( + true, + ), + headers: None, + compression: None, + }, +} diff --git a/crates/bsnext_core/tests/server.rs b/crates/bsnext_core/tests/server.rs index 687f082..56ca312 100644 --- a/crates/bsnext_core/tests/server.rs +++ b/crates/bsnext_core/tests/server.rs @@ -3,7 +3,7 @@ use bsnext_core::server::actor::ServerActor; use bsnext_core::server::handler_listen::Listen; use bsnext_core::servers_supervisor::file_changed_handler::FilesChanged; use bsnext_core::servers_supervisor::get_servers_handler::{GetServersMessage, IncomingEvents}; -use bsnext_dto::GetServersMessageResponse; +use bsnext_dto::GetServersMessageResponseDTO; use bsnext_input::route::{JsonWrapper, Route, RouteKind}; use bsnext_input::server_config::{ServerConfig, ServerIdentity}; use http::header::ACCEPT; @@ -16,13 +16,13 @@ mod tests; async fn system_test_01() { let route1 = Route { - path: "/".to_string(), + path: "/".parse().unwrap(), kind: RouteKind::new_html("hello world!"), ..Default::default() }; let value: Value = serde_json::from_str("[]").expect("json"); let route2 = Route { - path: "/j".to_string(), + path: "/j".parse().unwrap(), kind: RouteKind::new_json(JsonWrapper(value)), ..Default::default() }; @@ -60,7 +60,7 @@ async fn system_test_01() { async fn system_test_02() { let route1 = Route { - path: "/".to_string(), + path: "/".parse().unwrap(), kind: RouteKind::new_html("hello world!"), ..Default::default() }; @@ -133,7 +133,7 @@ impl actix::Actor for ServerParent { } impl actix::Handler for ServerParent { - type Result = GetServersMessageResponse; + type Result = GetServersMessageResponseDTO; fn handle(&mut self, _msg: GetServersMessage, _ctx: &mut Self::Context) -> Self::Result { todo!("woop!") diff --git a/crates/bsnext_dto/src/internal.rs b/crates/bsnext_dto/src/internal.rs index 543354a..557cebe 100644 --- a/crates/bsnext_dto/src/internal.rs +++ b/crates/bsnext_dto/src/internal.rs @@ -1,19 +1,29 @@ -use crate::{ExternalEvents, GetServersMessageResponse}; +use crate::{ExternalEventsDTO, GetServersMessageResponseDTO}; use bsnext_input::server_config::ServerIdentity; +use bsnext_input::startup::StartupError; +use bsnext_input::InputError; use std::net::SocketAddr; use typeshare::typeshare; #[derive(Debug)] pub enum AnyEvent { Internal(InternalEvents), - External(ExternalEvents), + External(ExternalEventsDTO), } #[derive(Debug)] pub enum InternalEvents { ServersChanged { - server_resp: GetServersMessageResponse, + server_resp: GetServersMessageResponseDTO, child_results: Vec, }, + InputError(InputError), + StartupError(StartupError), +} + +#[derive(Debug)] +pub enum StartupEvent { + Started, + FailedStartup(StartupError), } /// public version of internal events @@ -22,7 +32,7 @@ pub enum InternalEvents { #[derive(Debug, Clone, serde::Serialize)] #[serde(tag = "kind", content = "payload")] pub enum InternalEventsDTO { - ServersChanged(GetServersMessageResponse), + ServersChanged(GetServersMessageResponseDTO), } #[derive(Debug, Clone)] diff --git a/crates/bsnext_dto/src/lib.rs b/crates/bsnext_dto/src/lib.rs index d2380fc..78156dd 100644 --- a/crates/bsnext_dto/src/lib.rs +++ b/crates/bsnext_dto/src/lib.rs @@ -4,6 +4,7 @@ use bsnext_input::InputError; use std::fmt::{Display, Formatter}; use std::path::Path; +use crate::internal::StartupEvent; use bsnext_fs::Debounce; use bsnext_input::client_config::ClientConfig; use bsnext_input::route::{DirRoute, ProxyRoute, RawRoute, Route, RouteKind}; @@ -30,7 +31,7 @@ pub struct RouteDTO { impl From for RouteDTO { fn from(value: Route) -> Self { Self { - path: value.path.to_owned(), + path: value.path.as_str().to_owned(), kind: RouteKindDTO::from(value.kind), } } @@ -38,7 +39,7 @@ impl From for RouteDTO { impl From<&Route> for RouteDTO { fn from(value: &Route) -> Self { Self { - path: value.path.to_owned(), + path: value.path.as_str().to_owned(), kind: RouteKindDTO::from(value.kind.clone()), } } @@ -78,8 +79,8 @@ impl From for RouteKindDTO { #[typeshare] #[derive(Debug, Clone, serde::Serialize)] -pub struct ServersChanged { - pub servers_resp: GetServersMessageResponse, +pub struct ServersChangedDTO { + pub servers_resp: GetServersMessageResponseDTO, } #[typeshare] @@ -89,59 +90,47 @@ pub enum EventLevel { External, } -#[typeshare] -#[derive(Debug, serde::Serialize)] -pub struct ExternalEvent { - pub level: EventLevel, - pub fields: ExternalEvents, -} - #[typeshare] #[derive(Debug, Clone, serde::Serialize)] #[serde(tag = "kind", content = "payload")] -pub enum ExternalEvents { - ServersChanged(ServersChanged), - Watching(Watching), - WatchingStopped(StoppedWatching), - FileChanged(FileChanged), +pub enum ExternalEventsDTO { + ServersChanged(ServersChangedDTO), + Watching(WatchingDTO), + WatchingStopped(StoppedWatchingDTO), + FileChanged(FileChangedDTO), FilesChanged(FilesChangedDTO), - InputFileChanged(FileChanged), - InputAccepted(InputAccepted), - InputError(InputErrorDTO), + InputFileChanged(FileChangedDTO), + InputAccepted(InputAcceptedDTO), } #[typeshare] #[derive(Debug, serde::Serialize)] #[serde(tag = "kind", content = "payload")] -pub enum StartupEvent { +pub enum StartupEventDTO { Started, - FailedStartup(StartupErrorDTO), -} - -#[typeshare] -#[derive(Debug, PartialEq, Hash, Eq, Clone, serde::Deserialize, serde::Serialize)] -#[serde(tag = "kind", content = "payload")] -pub enum StartupErrorDTO { - InputError(InputErrorDTO), + FailedStartup(String), } -impl From<&StartupError> for StartupErrorDTO { - fn from(value: &StartupError) -> Self { +impl From<&StartupEvent> for StartupEventDTO { + fn from(value: &StartupEvent) -> Self { match value { - StartupError::InputError(e) => StartupErrorDTO::InputError(e.into()), + StartupEvent::Started => StartupEventDTO::Started, + StartupEvent::FailedStartup(StartupError::InputError(err)) => { + StartupEventDTO::FailedStartup(err.to_string()) + } } } } #[typeshare] #[derive(Debug, serde::Serialize, Clone)] -pub struct InputAccepted { +pub struct InputAcceptedDTO { pub path: String, } #[typeshare] #[derive(Debug, serde::Serialize, Clone)] -pub struct FileChanged { +pub struct FileChangedDTO { pub path: String, } @@ -153,18 +142,18 @@ pub struct FilesChangedDTO { #[typeshare] #[derive(Debug, Clone, serde::Serialize)] -pub struct Watching { +pub struct WatchingDTO { pub paths: Vec, pub debounce: DebounceDTO, } #[typeshare] #[derive(Debug, Clone, serde::Serialize)] -pub struct StoppedWatching { +pub struct StoppedWatchingDTO { pub paths: Vec, } -impl StoppedWatching { +impl StoppedWatchingDTO { pub fn from_path_buf(p: &Path) -> Self { Self { paths: vec![p.to_string_lossy().to_string()], @@ -172,7 +161,7 @@ impl StoppedWatching { } } -impl Display for Watching { +impl Display for WatchingDTO { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "yo") } @@ -206,7 +195,7 @@ impl From for DebounceDTO { } } -impl FileChanged { +impl FileChangedDTO { pub fn from_path_buf(p: &Path) -> Self { Self { path: p.to_string_lossy().to_string(), @@ -214,7 +203,7 @@ impl FileChanged { } } -impl Watching { +impl WatchingDTO { pub fn from_path_buf(p: &Path, debounce: Debounce) -> Self { Self { paths: vec![p.to_string_lossy().to_string()], @@ -247,7 +236,7 @@ pub struct ServerChangeSet { #[typeshare::typeshare] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, MessageResponse)] -pub struct GetServersMessageResponse { +pub struct GetServersMessageResponseDTO { pub servers: Vec, } @@ -302,6 +291,7 @@ pub enum InputErrorDTO { UnsupportedExtension(String), MissingExtension(String), EmptyInput(String), + BsLiveRules(String), } impl From<&InputError> for InputErrorDTO { @@ -322,6 +312,7 @@ impl From<&InputError> for InputErrorDTO { } e @ InputError::MissingExtension(_) => InputErrorDTO::MissingExtension(e.to_string()), e @ InputError::EmptyInput => InputErrorDTO::EmptyInput(e.to_string()), + e @ InputError::BsLiveRules(..) => InputErrorDTO::BsLiveRules(e.to_string()), } } } diff --git a/crates/bsnext_example/src/basic.rs b/crates/bsnext_example/src/basic.rs index 50dd89c..58e6f6f 100644 --- a/crates/bsnext_example/src/basic.rs +++ b/crates/bsnext_example/src/basic.rs @@ -14,28 +14,28 @@ impl BasicExample { identity: identity.unwrap_or_else(ServerIdentity::named), routes: vec![ Route { - path: "/".to_string(), + path: "/".to_string().parse().unwrap(), kind: RouteKind::new_html(include_str!( "../../../examples/basic/public/index.html" )), ..Default::default() }, Route { - path: "/styles.css".to_string(), + path: "/styles.css".to_string().parse().unwrap(), kind: RouteKind::new_raw(include_str!( "../../../examples/basic/public/styles.css" )), ..Default::default() }, Route { - path: "/script.js".to_string(), + path: "/script.js".to_string().parse().unwrap(), kind: RouteKind::new_raw(include_str!( "../../../examples/basic/public/script.js" )), ..Default::default() }, Route { - path: "/reset.css".to_string(), + path: "/reset.css".to_string().parse().unwrap(), kind: RouteKind::new_raw(include_str!( "../../../examples/basic/public/reset.css" )), diff --git a/crates/bsnext_example/src/lit.rs b/crates/bsnext_example/src/lit.rs index 9470f3b..8e9242c 100644 --- a/crates/bsnext_example/src/lit.rs +++ b/crates/bsnext_example/src/lit.rs @@ -14,12 +14,12 @@ impl LitExample { identity: identity.unwrap_or_else(ServerIdentity::named), routes: vec![ Route { - path: "/".to_string(), + path: "/".to_string().parse().unwrap(), kind: RouteKind::new_html(include_str!("../../../examples/lit/index.html")), ..Default::default() }, Route { - path: "/lit.js".to_string(), + path: "/lit.js".to_string().parse().unwrap(), kind: RouteKind::new_raw(include_str!("../../../examples/lit/lit.js")), ..Default::default() }, diff --git a/crates/bsnext_input/Cargo.toml b/crates/bsnext_input/Cargo.toml index 8431c53..4a17362 100644 --- a/crates/bsnext_input/Cargo.toml +++ b/crates/bsnext_input/Cargo.toml @@ -10,8 +10,10 @@ bsnext_resp = { path = "../bsnext_resp" } bsnext_tracing = { path = "../bsnext_tracing" } markdown = { version = "1.0.0-alpha.16" } +miette = { workspace = true } random_word = { workspace = true } +matchit = { workspace = true } mime_guess = { workspace = true } serde = { workspace = true } clap = { workspace = true } diff --git a/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap b/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap index 1491ddc..3ab8e8a 100644 --- a/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap +++ b/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers.snap @@ -5,7 +5,9 @@ expression: c Config { items: [ Route { - path: "/api", + path: PathDef { + inner: "/api", + }, kind: Raw( Json { json: JsonWrapper( diff --git a/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap b/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap index 745d5f8..153357d 100644 --- a/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap +++ b/crates/bsnext_input/src/input_test/snapshots/bsnext_input__input_test__deserialize_3_headers_control.snap @@ -5,7 +5,9 @@ expression: c Config { items: [ Route { - path: "/api", + path: PathDef { + inner: "/api", + }, kind: Raw( Json { json: JsonWrapper( diff --git a/crates/bsnext_input/src/lib.rs b/crates/bsnext_input/src/lib.rs index 52bc2e1..4d096f0 100644 --- a/crates/bsnext_input/src/lib.rs +++ b/crates/bsnext_input/src/lib.rs @@ -2,6 +2,7 @@ use crate::target::TargetKind; use crate::md::MarkdownError; use crate::yml::YamlError; +use miette::{JSONReportHandler, NamedSource}; use std::fmt::{Display, Formatter}; use std::fs; use std::fs::read_to_string; @@ -13,6 +14,7 @@ pub mod client_config; #[cfg(test)] pub mod input_test; pub mod md; +pub mod path_def; pub mod paths; pub mod route; pub mod route_manifest; @@ -53,50 +55,54 @@ impl FromStr for Input { } impl Input { - pub fn from_input_path>(path: P) -> Result { + pub fn from_input_path>(path: P) -> Result> { match path.as_ref().extension().and_then(|x| x.to_str()) { - None => Err(InputError::MissingExtension(path.as_ref().to_owned())), + None => Err(Box::new(InputError::MissingExtension( + path.as_ref().to_owned(), + ))), Some("yml") | Some("yaml") => Input::from_yaml_path(path), Some("md") | Some("markdown") => Input::from_md_path(path), - Some(other) => Err(InputError::UnsupportedExtension(other.to_string())), + Some(other) => Err(Box::new(InputError::UnsupportedExtension( + other.to_string(), + ))), } } - fn from_yaml_path>(path: P) -> Result { - let str = read_to_string(&path)?; + fn from_yaml_path>(path: P) -> Result> { + let str = read_to_string(&path).map_err(|e| Box::new(e.into()))?; if str.trim().is_empty() { - return Err(InputError::YamlError(YamlError::EmptyError { + return Err(Box::new(InputError::YamlError(YamlError::EmptyError { path: path.as_ref().to_string_lossy().to_string(), - })); + }))); } - let output = serde_yaml::from_str::(str.as_str()).map_err(move |e| { - if let Some(location) = e.location() { - YamlError::ParseErrorWithLocation { - serde_error: e, - input: str, - path: path.as_ref().to_string_lossy().to_string(), - line: location.line(), - column: location.column(), + let output = serde_yaml::from_str::(str.as_str()) + .map_err(move |e| { + if let Some(loc) = e.location() { + BsLiveRulesError { + err_span: (loc.index()..loc.index() + 1).into(), + src: NamedSource::new( + "/Users/shaneosbourne/WebstormProjects/bslive/bslive.yml", + str, + ), + message: e.to_string(), + summary: None, + } + } else { + unreachable!("handle later") } - } else { - YamlError::ParseError { - serde_error: e, - input: str, - path: path.as_ref().to_string_lossy().to_string(), - } - } - })?; + }) + .map_err(|e| Box::new(e.into()))?; // todo: don't allow duplicates?. Ok(output) } - fn from_md_path>(path: P) -> Result { - let str = read_to_string(path)?; - let input = md::md_to_input(&str)?; + fn from_md_path>(path: P) -> Result> { + let str = read_to_string(path).map_err(|e| Box::new(e.into()))?; + let input = md::md_to_input(&str).map_err(|e| Box::new(e.into()))?; // todo: don't allow duplicates. Ok(input) } } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, miette::Diagnostic, thiserror::Error)] pub enum InputError { #[error("no suitable inputs could be found")] MissingInputs, @@ -124,6 +130,9 @@ pub enum InputError { MarkdownError(#[from] MarkdownError), #[error("{0}")] YamlError(#[from] YamlError), + #[error(transparent)] + #[diagnostic(transparent)] + BsLiveRules(#[from] BsLiveRulesError), } #[derive(Debug, thiserror::Error)] @@ -162,6 +171,37 @@ pub enum DirError { CannotMove { path: PathBuf }, } +#[derive(miette::Diagnostic, Debug, thiserror::Error)] +#[error("bslive rules violated")] +#[diagnostic()] +pub struct BsLiveRulesError { + // Note: label but no source code + #[label = "{message}"] + err_span: miette::SourceSpan, + #[source_code] + src: miette::NamedSource, + message: String, + #[help] + summary: Option, +} + +impl BsLiveRulesError { + pub fn as_string(&self) -> String { + let n = miette::GraphicalReportHandler::new(); + let mut inner = String::new(); + n.render_report(&mut inner, self).expect("write?"); + inner + } + + pub fn as_json(&self) -> String { + let mut out = String::new(); + JSONReportHandler::new() + .render_report(&mut out, self) + .unwrap(); + out + } +} + #[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)] pub struct PathDefs(pub Vec); diff --git a/crates/bsnext_input/src/md.rs b/crates/bsnext_input/src/md.rs index f752bae..53f21da 100644 --- a/crates/bsnext_input/src/md.rs +++ b/crates/bsnext_input/src/md.rs @@ -2,6 +2,7 @@ use crate::route::{RawRoute, Route, RouteKind}; use crate::server_config::ServerConfig; use crate::Input; +use crate::path_def::PathDef; use markdown::mdast::Node; use markdown::{Constructs, ParseOptions}; use mime_guess::get_mime_extensions_str; @@ -12,6 +13,7 @@ use nom::sequence::separated_pair; use nom::{error::ParseError, IResult}; use serde_json::json; use std::cmp::PartialEq; +use std::str::FromStr; fn parser_for(k: BsLiveKinds) -> impl Fn(&[Node]) -> IResult<&[Node], &Node> { move |input: &[Node]| { @@ -99,7 +101,7 @@ impl TryInto for (&Node, &Node) { let r: PathOnly = serde_yaml::from_str(&code.value)?; let route_kind = route_kind_from_body_node(self.1)?; let route = Route { - path: r.path, + path: PathDef::from_str(&r.path)?, kind: route_kind, ..Default::default() }; @@ -312,7 +314,7 @@ body { assert_eq!( server_1.routes[0], Route { - path: "/app.css".into(), + path: "/app.css".parse()?, kind: RouteKind::new_raw("body {\n background: blue\n}"), ..Default::default() } @@ -352,7 +354,7 @@ body { assert_eq!( server_1.routes[0], Route { - path: "/app.css".into(), + path: PathDef::from_str("/app.css")?, kind: RouteKind::new_raw("body {\n background: blue\n}"), ..Default::default() } @@ -360,7 +362,7 @@ body { assert_eq!( server_1.routes[1], Route { - path: "/app2.css".into(), + path: PathDef::from_str("/app2.css")?, kind: RouteKind::new_raw("body {\n background: blue\n}"), ..Default::default() } @@ -410,7 +412,7 @@ path: /abc assert_eq!( server_1.routes[0], Route { - path: "/health".into(), + path: PathDef::from_str("/health")?, kind: RouteKind::new_raw("OK"), ..Default::default() } @@ -418,7 +420,7 @@ path: /abc assert_eq!( server_1.routes[1], Route { - path: "/".into(), + path: PathDef::from_str("/")?, kind: RouteKind::new_html("

hello world

"), ..Default::default() } @@ -426,7 +428,7 @@ path: /abc assert_eq!( server_1.routes[2], Route { - path: "/abc".into(), + path: PathDef::from_str("/abc")?, kind: RouteKind::new_html("

hello world 2

"), ..Default::default() } @@ -480,10 +482,10 @@ pub fn input_to_str(input: &Input) -> String { chunks.push(fenced_input(&yml)); for route in &server_config.routes { - let path_only = json!({"path": route.path}); + let path_only = json!({"path": route.path.as_str()}); let route_yaml = serde_yaml::to_string(&path_only).expect("never fail here on route?"); chunks.push(fenced_route(&route_yaml)); - chunks.push(route_to_markdown(&route.kind, &route.path)); + chunks.push(route_to_markdown(&route.kind, route.path.as_str())); } } for _x in input.servers.iter().skip(1) { diff --git a/crates/bsnext_input/src/path_def.rs b/crates/bsnext_input/src/path_def.rs new file mode 100644 index 0000000..aa6f2d6 --- /dev/null +++ b/crates/bsnext_input/src/path_def.rs @@ -0,0 +1,97 @@ +use crate::route::PathDefError; +use serde::{de, Deserialize, Deserializer, Serializer}; +use std::path::{Component, Path, PathBuf}; +use std::str::FromStr; + +#[derive(Debug, PartialEq, Hash, Clone)] +pub struct PathDef { + inner: String, +} + +impl FromStr for PathDef { + type Err = PathDefError; + + fn from_str(s: &str) -> Result { + Self::try_new(s) + } +} + +pub fn deserialize<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + FromStr::from_str(&s).map_err(de::Error::custom) +} + +pub fn serialize(x: &PathDef, s: S) -> Result +where + S: Serializer, +{ + s.serialize_str(x.inner.as_str()) +} + +impl PathDef { + pub fn as_pb(&self) -> PathBuf { + PathBuf::from(&self.inner) + } + pub fn as_str(&self) -> &str { + self.inner.as_str() + } + pub fn try_new>(input: A) -> Result { + let str = input.as_ref(); + let is_path = str.starts_with("/"); + let p = Path::new(str); + let has_star = p.components().any(|c| match c { + Component::Prefix(_) => false, + Component::RootDir => false, + Component::CurDir => false, + Component::ParentDir => false, + Component::Normal(n) => n.to_str().is_some_and(|str| str.contains("*")), + }); + let route = match (is_path, has_star) { + (true, false) => Ok(Self { + inner: str.to_string(), + }), + (true, true) => Err(PathDefError::ContainsStar), + (false, _) => Err(PathDefError::StartsWithSlash), + }?; + + let mut r = matchit::Router::new(); + match r.insert(&route.inner, true) { + Ok(_) => Ok(route), + Err(e) => Err(PathDefError::InsertError(e)), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + #[test] + fn test_verify() -> anyhow::Result<()> { + let input = "/abc/one/*rest"; + let actual = PathDef::try_new(input).unwrap_err(); + assert_eq!(actual, PathDefError::ContainsStar); + + let input = "abc/one"; + let actual = PathDef::try_new(input).unwrap_err(); + assert_eq!(actual, PathDefError::StartsWithSlash); + + let input = "/:"; + let actual = PathDef::try_new(input).unwrap_err(); + assert_eq!( + actual, + PathDefError::InsertError(matchit::InsertError::UnnamedParam) + ); + + let input = "/:abc:abc"; + let actual = PathDef::try_new(input).unwrap_err(); + assert_eq!( + actual, + PathDefError::InsertError(matchit::InsertError::TooManyParams) + ); + + Ok(()) + } +} diff --git a/crates/bsnext_input/src/paths.rs b/crates/bsnext_input/src/paths.rs index 6f373db..1b345ae 100644 --- a/crates/bsnext_input/src/paths.rs +++ b/crates/bsnext_input/src/paths.rs @@ -50,7 +50,7 @@ pub fn from_paths>( .map(|p| -> Route { let str = p.as_ref(); Route { - path: "/".to_string(), + path: "/".to_string().parse().unwrap(), kind: RouteKind::Dir(DirRoute { dir: str.into(), base: None, diff --git a/crates/bsnext_input/src/route.rs b/crates/bsnext_input/src/route.rs index 5237ae9..a841444 100644 --- a/crates/bsnext_input/src/route.rs +++ b/crates/bsnext_input/src/route.rs @@ -1,14 +1,18 @@ +use crate::path_def::PathDef; use crate::watch_opts::WatchOpts; use bsnext_resp::inject_opts::InjectOpts; +use matchit::InsertError; use std::collections::BTreeMap; use std::fmt::{Display, Formatter}; use std::hash::{Hash, Hasher}; use std::ops::Deref; use std::path::PathBuf; +use std::str::FromStr; #[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)] pub struct Route { - pub path: String, + #[serde(with = "crate::path_def")] + pub path: PathDef, #[serde(flatten)] pub kind: RouteKind, #[serde(flatten)] @@ -16,6 +20,16 @@ pub struct Route { pub fallback: Option, } +#[derive(Debug, PartialEq, thiserror::Error)] +pub enum PathDefError { + #[error("Paths must start with a slash")] + StartsWithSlash, + #[error("Paths cannot contain a `*`")] + ContainsStar, + #[error("matchit error {0}")] + InsertError(#[from] InsertError), +} + #[derive(Debug, PartialEq, Hash, Clone, serde::Deserialize, serde::Serialize)] pub struct FallbackRoute { #[serde(flatten)] @@ -41,7 +55,7 @@ pub struct Opts { impl Default for Route { fn default() -> Self { Self { - path: "/".to_string(), + path: PathDef::from_str("/").unwrap(), kind: RouteKind::new_html("default"), opts: Opts { headers: None, diff --git a/crates/bsnext_input/src/route_manifest.rs b/crates/bsnext_input/src/route_manifest.rs index 0e0299a..8b9ba74 100644 --- a/crates/bsnext_input/src/route_manifest.rs +++ b/crates/bsnext_input/src/route_manifest.rs @@ -54,7 +54,7 @@ pub struct RouteIdentity { impl From<&Route> for RouteIdentity { fn from(value: &Route) -> Self { Self { - path: value.path.clone(), + path: value.path.as_str().to_string(), kind_str: match &value.kind { RouteKind::Raw(raw) => match raw { RawRoute::Html { .. } => "RouteKind::Raw::Html", @@ -80,11 +80,14 @@ pub struct RouteChangeSet { mod test { use super::*; + use crate::path_def::PathDef; use std::hash::DefaultHasher; + use std::str::FromStr; + #[test] fn test_route_hash() -> anyhow::Result<()> { let r1 = Route { - path: "/".to_string(), + path: PathDef::from_str("/")?, kind: RouteKind::new_html("hello world!"), ..Default::default() }; diff --git a/crates/bsnext_input/src/startup.rs b/crates/bsnext_input/src/startup.rs index 85b9d8b..037b47a 100644 --- a/crates/bsnext_input/src/startup.rs +++ b/crates/bsnext_input/src/startup.rs @@ -44,7 +44,7 @@ pub enum SystemStartArgs { } pub trait SystemStart { - fn input(&self, ctx: &StartupContext) -> Result; + fn input(&self, ctx: &StartupContext) -> Result>; } impl Default for StartupContext { diff --git a/crates/bsnext_input/src/yml.rs b/crates/bsnext_input/src/yml.rs index 1cfc5e7..6d0b942 100644 --- a/crates/bsnext_input/src/yml.rs +++ b/crates/bsnext_input/src/yml.rs @@ -7,7 +7,7 @@ could not parse yaml file: input content was: (error at line: {line}, column: {column}) -{input} +{src} original error: @@ -16,7 +16,7 @@ original error: )] ParseErrorWithLocation { path: String, - input: String, + src: String, serde_error: serde_yaml::Error, line: usize, column: usize, diff --git a/crates/bsnext_output/Cargo.toml b/crates/bsnext_output/Cargo.toml index f7a8b45..78dd496 100644 --- a/crates/bsnext_output/Cargo.toml +++ b/crates/bsnext_output/Cargo.toml @@ -8,9 +8,7 @@ edition = "2021" [dependencies] bsnext_core = { path = "../bsnext_core" } bsnext_dto = { path = "../bsnext_dto" } - -ansi_term = { version = "0.12.1" } -indent = { version = "0.1.1" } +bsnext_input = { path = "../bsnext_input" } anyhow = { workspace = true } clap = { workspace = true } @@ -18,6 +16,10 @@ serde = { workspace = true } serde_json = { workspace = true } insta = { workspace = true } tracing = { workspace = true } +miette = { workspace = true } + +ansi_term = { version = "0.12.1" } +indent = { version = "0.1.1" } ratatui = { version = "0.27.0" } crossterm = { version = "0.27.0" } color-eyre = { version = "0.6.3" } diff --git a/crates/bsnext_output/src/json.rs b/crates/bsnext_output/src/json.rs index 68306d4..588a05f 100644 --- a/crates/bsnext_output/src/json.rs +++ b/crates/bsnext_output/src/json.rs @@ -1,6 +1,6 @@ use crate::OutputWriter; -use bsnext_dto::internal::{InternalEvents, InternalEventsDTO}; -use bsnext_dto::{ExternalEvents, StartupEvent}; +use bsnext_dto::internal::{InternalEvents, InternalEventsDTO, StartupEvent}; +use bsnext_dto::{ExternalEventsDTO, StartupEventDTO}; use std::io::Write; pub struct JsonPrint; @@ -9,7 +9,7 @@ impl OutputWriter for JsonPrint { fn handle_external_event( &self, sink: &mut W, - evt: &ExternalEvents, + evt: &ExternalEventsDTO, ) -> anyhow::Result<()> { writeln!(sink, "{}", serde_json::to_string(&evt)?) .map_err(|e| anyhow::anyhow!(e.to_string())) @@ -26,6 +26,8 @@ impl OutputWriter for JsonPrint { writeln!(sink, "{}", serde_json::to_string(&output)?) .map_err(|e| anyhow::anyhow!(e.to_string()))?; } + InternalEvents::InputError(_) => {} + InternalEvents::StartupError(_) => {} } Ok(()) } @@ -35,7 +37,8 @@ impl OutputWriter for JsonPrint { sink: &mut W, evt: &StartupEvent, ) -> anyhow::Result<()> { - writeln!(sink, "{}", serde_json::to_string(&evt)?) + let as_dto = StartupEventDTO::from(evt); + writeln!(sink, "{}", serde_json::to_string(&as_dto)?) .map_err(|e| anyhow::anyhow!(e.to_string())) } } diff --git a/crates/bsnext_output/src/lib.rs b/crates/bsnext_output/src/lib.rs index 58cb6c4..2862d55 100644 --- a/crates/bsnext_output/src/lib.rs +++ b/crates/bsnext_output/src/lib.rs @@ -1,8 +1,8 @@ use crate::json::JsonPrint; use crate::pretty::PrettyPrint; use crate::ratatui::RatatuiSender; -use bsnext_dto::internal::InternalEvents; -use bsnext_dto::{ExternalEvents, StartupEvent}; +use bsnext_dto::internal::{InternalEvents, StartupEvent}; +use bsnext_dto::ExternalEventsDTO; use std::io::Write; pub mod json; @@ -15,7 +15,7 @@ pub trait OutputWriter { fn handle_external_event( &self, _sink: &mut W, - _evt: &ExternalEvents, + _evt: &ExternalEventsDTO, ) -> anyhow::Result<()> { Ok(()) } @@ -45,7 +45,7 @@ impl OutputWriter for Writers { fn handle_external_event( &self, sink: &mut W, - evt: &ExternalEvents, + evt: &ExternalEventsDTO, ) -> anyhow::Result<()> { match self { Writers::Pretty => PrettyPrint.handle_external_event(sink, evt), diff --git a/crates/bsnext_output/src/pretty.rs b/crates/bsnext_output/src/pretty.rs index f793573..709bc8b 100644 --- a/crates/bsnext_output/src/pretty.rs +++ b/crates/bsnext_output/src/pretty.rs @@ -1,9 +1,11 @@ use crate::OutputWriter; -use bsnext_dto::internal::{ChildResult, InternalEvents}; +use bsnext_dto::internal::{ChildResult, InternalEvents, StartupEvent}; use bsnext_dto::{ - ExternalEvents, FileChanged, FilesChangedDTO, InputAccepted, InputErrorDTO, ServerIdentityDTO, - ServersChanged, StartupErrorDTO, StartupEvent, StoppedWatching, Watching, + ExternalEventsDTO, FileChangedDTO, FilesChangedDTO, InputAcceptedDTO, ServerIdentityDTO, + ServersChangedDTO, StoppedWatchingDTO, WatchingDTO, }; +use bsnext_input::startup::StartupError; +use bsnext_input::InputError; use std::io::Write; use std::marker::PhantomData; use std::path::PathBuf; @@ -14,23 +16,22 @@ impl OutputWriter for PrettyPrint { fn handle_external_event( &self, sink: &mut W, - evt: &ExternalEvents, + evt: &ExternalEventsDTO, ) -> anyhow::Result<()> { match &evt { - ExternalEvents::ServersChanged(servers_started) => { + ExternalEventsDTO::ServersChanged(servers_started) => { print_servers_changed(sink, servers_started) } - ExternalEvents::InputError(input_err) => { - print_input_error(sink, Indent::None, input_err) - } - ExternalEvents::Watching(watching) => print_watching(sink, watching), - ExternalEvents::WatchingStopped(watching) => print_stopped_watching(sink, watching), - ExternalEvents::InputAccepted(input_accepted) => { + ExternalEventsDTO::Watching(watching) => print_watching(sink, watching), + ExternalEventsDTO::WatchingStopped(watching) => print_stopped_watching(sink, watching), + ExternalEventsDTO::InputAccepted(input_accepted) => { print_input_accepted(sink, input_accepted) } - ExternalEvents::FileChanged(file_changed) => print_file_changed(sink, file_changed), - ExternalEvents::FilesChanged(files_changed) => print_files_changed(sink, files_changed), - ExternalEvents::InputFileChanged(file_changed) => { + ExternalEventsDTO::FileChanged(file_changed) => print_file_changed(sink, file_changed), + ExternalEventsDTO::FilesChanged(files_changed) => { + print_files_changed(sink, files_changed) + } + ExternalEventsDTO::InputFileChanged(file_changed) => { print_input_file_changed(sink, file_changed) } } @@ -53,6 +54,18 @@ impl OutputWriter for PrettyPrint { } } } + InternalEvents::InputError(InputError::BsLiveRules(bs_rules)) => { + let n = miette::GraphicalReportHandler::new(); + let mut inner = String::new(); + n.render_report(&mut inner, &bs_rules).expect("write?"); + writeln!(sink, "{}", inner)?; + } + InternalEvents::InputError(err) => { + writeln!(sink, "{}", err)?; + } + InternalEvents::StartupError(err) => { + writeln!(sink, "{}", err)?; + } } Ok(()) } @@ -74,8 +87,14 @@ impl OutputWriter for PrettyPrint { )?; writeln!(sink)?; match err { - StartupErrorDTO::InputError(input_err) => { - print_input_error(sink, Indent::Some(4), input_err)?; + StartupError::InputError(InputError::BsLiveRules(bs_rules)) => { + let n = miette::GraphicalReportHandler::new(); + let mut inner = String::new(); + n.render_report(&mut inner, bs_rules).expect("write?"); + writeln!(sink, "{}", inner)?; + } + StartupError::InputError(err) => { + writeln!(sink, "{}", err)?; } } } @@ -107,12 +126,12 @@ impl Line { } } impl Line { - pub fn unprefixed() -> Line { - Line { - indent: Indent::None, - _state: PhantomData, - } - } + // pub fn unprefixed() -> Line { + // Line { + // indent: Indent::None, + // _state: PhantomData, + // } + // } } impl Line { pub fn info(self, str: &str) -> String { @@ -120,19 +139,19 @@ impl Line { } } impl Line { - pub fn indent(self, size: Indent) -> Self { - Self { - indent: size, - _state: PhantomData, - } - } - pub fn error(self, str: &str) -> String { - let coloured = ansi_term::Color::Red.paint(str); - indent::indent_all_by(self.indent.indent_size(), coloured.to_string()) - } + // pub fn indent(self, size: Indent) -> Self { + // Self { + // indent: size, + // _state: PhantomData, + // } + // } + // pub fn error(self, str: &str) -> String { + // let coloured = ansi_term::Color::Red.paint(str); + // indent::indent_all_by(self.indent.indent_size(), coloured.to_string()) + // } } -pub fn print_file_changed(w: &mut W, evt: &FileChanged) -> anyhow::Result<()> { +pub fn print_file_changed(w: &mut W, evt: &FileChangedDTO) -> anyhow::Result<()> { writeln!(w, "[change] {}", evt.path)?; Ok(()) } @@ -168,17 +187,17 @@ fn short_file_list>(paths: &[A]) -> String { file_names.join(", ") } -pub fn print_input_file_changed(w: &mut W, evt: &FileChanged) -> anyhow::Result<()> { +pub fn print_input_file_changed(w: &mut W, evt: &FileChangedDTO) -> anyhow::Result<()> { writeln!(w, "[change:input] {}", evt.path)?; Ok(()) } -pub fn print_input_accepted(w: &mut W, evt: &InputAccepted) -> anyhow::Result<()> { +pub fn print_input_accepted(w: &mut W, evt: &InputAcceptedDTO) -> anyhow::Result<()> { writeln!(w, "[input] {}", evt.path)?; Ok(()) } -pub fn print_watching(w: &mut W, evt: &Watching) -> anyhow::Result<()> { +pub fn print_watching(w: &mut W, evt: &WatchingDTO) -> anyhow::Result<()> { for x in &evt.paths { writeln!(w, "[watching {}] {}", evt.debounce, x)?; } @@ -187,54 +206,30 @@ pub fn print_watching(w: &mut W, evt: &Watching) -> anyhow::Result<()> enum Indent { None, - Some(usize), + // Some(usize), } impl Indent { - pub fn indent_size(&self) -> usize { - match self { - Indent::None => 0, - Indent::Some(size) => *size, - } - } -} - -fn print_input_error( - w: &mut W, - indent: Indent, - evt: &InputErrorDTO, -) -> anyhow::Result<()> { - let v = match evt { - InputErrorDTO::MissingInputs(evt) => evt, - InputErrorDTO::InvalidInput(evt) => evt, - InputErrorDTO::NotFound(evt) => evt, - InputErrorDTO::InputWriteError(evt) => evt, - InputErrorDTO::PathError(evt) => evt, - InputErrorDTO::PortError(evt) => evt, - InputErrorDTO::DirError(evt) => evt, - InputErrorDTO::YamlError(evt) => evt, - InputErrorDTO::MarkdownError(evt) => evt, - InputErrorDTO::Io(evt) => evt, - InputErrorDTO::UnsupportedExtension(evt) => evt, - InputErrorDTO::MissingExtension(evt) => evt, - InputErrorDTO::EmptyInput(evt) => evt, - }; - writeln!(w, "{}", Line::unprefixed().indent(indent).error(v))?; - Ok(()) + // pub fn indent_size(&self) -> usize { + // match self { + // Indent::None => 0, + // Indent::Some(size) => *size, + // } + // } } -pub fn print_stopped_watching(w: &mut W, evt: &StoppedWatching) -> anyhow::Result<()> { +pub fn print_stopped_watching(w: &mut W, evt: &StoppedWatchingDTO) -> anyhow::Result<()> { for x in &evt.paths { writeln!(w, "[watching:stopped] {}", x)?; } Ok(()) } -fn print_servers_changed(w: &mut W, servers_started: &ServersChanged) -> anyhow::Result<()> +fn print_servers_changed(w: &mut W, servers_started: &ServersChangedDTO) -> anyhow::Result<()> where W: Write, { - let ServersChanged { + let ServersChangedDTO { servers_resp, // changeset, } = servers_started; diff --git a/crates/bsnext_output/src/ratatui.rs b/crates/bsnext_output/src/ratatui.rs index 35b980e..1a0400a 100644 --- a/crates/bsnext_output/src/ratatui.rs +++ b/crates/bsnext_output/src/ratatui.rs @@ -1,6 +1,6 @@ use self::common::{init_terminal, install_hooks, restore_terminal, Tui}; use crate::OutputWriter; -use bsnext_dto::{ExternalEvents, GetServersMessageResponse, StartupEvent}; +use bsnext_dto::{ExternalEventsDTO, GetServersMessageResponseDTO}; use std::io::{BufWriter, Write}; use std::sync::mpsc; use std::sync::mpsc::Sender; @@ -12,7 +12,7 @@ use std::{ }; use crate::pretty::{print_server_updates, server_display, PrettyPrint}; -use bsnext_dto::internal::{AnyEvent, InternalEvents}; +use bsnext_dto::internal::{AnyEvent, InternalEvents, StartupEvent}; use ratatui::{ buffer::Buffer, crossterm::event::{self, Event}, @@ -30,7 +30,7 @@ impl OutputWriter for RatatuiSender { fn handle_external_event( &self, _sink: &mut W, - evt: &ExternalEvents, + evt: &ExternalEventsDTO, ) -> anyhow::Result<()> { match self .0 @@ -134,7 +134,7 @@ struct App { scroll: u16, last_tick: Instant, events: FixedSizeQueue, - server_status: Option, + server_status: Option, } enum RatatuiEvent { @@ -194,6 +194,12 @@ impl App { InternalEvents::ServersChanged { server_resp, .. } => { self.server_status = Some(server_resp.clone()); } + InternalEvents::InputError(_) => { + todo!("InternalEvents::InputError") + } + InternalEvents::StartupError(_) => { + todo!("InternalEvents::StartupError") + } } self.events.add(RecordedEvent::new(AnyEvent::Internal(evt))); } @@ -258,6 +264,12 @@ impl App { InternalEvents::ServersChanged { child_results, .. } => { (evt.now, print_server_updates(child_results)) } + InternalEvents::InputError(_) => { + todo!("InternalEvents::InputError") + } + InternalEvents::StartupError(_) => { + todo!("InternalEvents::StartupError") + } }, AnyEvent::External(ext) => { let mut writer = BufWriter::new(Vec::new()); diff --git a/crates/bsnext_output/src/tests.rs b/crates/bsnext_output/src/tests.rs index 3817c60..f19b575 100644 --- a/crates/bsnext_output/src/tests.rs +++ b/crates/bsnext_output/src/tests.rs @@ -1,8 +1,9 @@ use crate::pretty::PrettyPrint; use crate::OutputWriter; +use bsnext_dto::internal::StartupEvent; use bsnext_dto::{ - ExternalEvents, GetServersMessageResponse, ServerDTO, ServerIdentityDTO, ServersChanged, - StartupEvent, + ExternalEventsDTO, GetServersMessageResponseDTO, ServerDTO, ServerIdentityDTO, + ServersChangedDTO, }; use std::io::BufWriter; @@ -31,7 +32,7 @@ fn server_2() -> ServerDTO { } } -fn exec(evt: &ExternalEvents) -> anyhow::Result { +fn exec(evt: &ExternalEventsDTO) -> anyhow::Result { let mut writer = BufWriter::new(Vec::new()); PrettyPrint.handle_external_event(&mut writer, &evt)?; Ok(String::from_utf8(writer.into_inner()?).unwrap()) @@ -48,8 +49,8 @@ fn exec_startup(evt: &StartupEvent) -> anyhow::Result<(String, String)> { #[test] fn test_servers_started() -> anyhow::Result<()> { - let evt = ExternalEvents::ServersChanged(ServersChanged { - servers_resp: GetServersMessageResponse { + let evt = ExternalEventsDTO::ServersChanged(ServersChangedDTO { + servers_resp: GetServersMessageResponseDTO { servers: vec![server_1(), server_2()], }, }); diff --git a/crates/bsnext_system/examples/manual_fs_events.rs b/crates/bsnext_system/examples/manual_fs_events.rs index 97c23e2..02c0045 100644 --- a/crates/bsnext_system/examples/manual_fs_events.rs +++ b/crates/bsnext_system/examples/manual_fs_events.rs @@ -73,6 +73,8 @@ servers: dbg!(server_resp); } AnyEvent::External(_) => {} + AnyEvent::Internal(InternalEvents::InputError(_)) => {} + AnyEvent::Internal(InternalEvents::StartupError(_)) => {} } } } diff --git a/crates/bsnext_system/src/cli.rs b/crates/bsnext_system/src/cli.rs index 5afd1ca..043b8a1 100644 --- a/crates/bsnext_system/src/cli.rs +++ b/crates/bsnext_system/src/cli.rs @@ -3,7 +3,7 @@ use crate::start_kind::StartKind; use crate::{AnyEvent, BsSystem, Start}; use actix::Actor; -use bsnext_dto::StartupEvent; +use bsnext_dto::internal::StartupEvent; use bsnext_input::startup::DidStart; use bsnext_output::ratatui::Ratatui; use bsnext_output::{OutputWriter, Writers}; @@ -77,7 +77,7 @@ where }; } Err(e) => { - let evt = StartupEvent::FailedStartup((&e).into()); + let evt = StartupEvent::FailedStartup(e); match start_printer.handle_startup_event(stdout, &evt) { Ok(_) => {} Err(e) => tracing::error!(?e), @@ -86,7 +86,7 @@ where Ok(_) => {} Err(e) => tracing::error!("could not flush {e}"), }; - return Err(e.into()); + return Err(anyhow::anyhow!("could not flush")); } }; diff --git a/crates/bsnext_system/src/lib.rs b/crates/bsnext_system/src/lib.rs index 21a0e13..4230e12 100644 --- a/crates/bsnext_system/src/lib.rs +++ b/crates/bsnext_system/src/lib.rs @@ -7,7 +7,7 @@ use bsnext_input::Input; use std::collections::HashMap; use actix_rt::Arbiter; -use bsnext_dto::ExternalEvents; +use bsnext_dto::ExternalEventsDTO; use std::path::PathBuf; use std::sync::Arc; @@ -41,7 +41,7 @@ pub mod start_kind; pub struct BsSystem { self_addr: Option>, servers_addr: Option>, - external_event_sender: Option>, + any_event_sender: Option>, input_monitors: Vec>, any_monitors: HashMap, cwd: Option, @@ -49,7 +49,7 @@ pub struct BsSystem { #[derive(Debug)] pub struct EventWithSpan { - pub evt: ExternalEvents, + pub evt: ExternalEventsDTO, } impl Default for BsSystem { @@ -71,7 +71,7 @@ impl BsSystem { BsSystem { self_addr: None, servers_addr: None, - external_event_sender: None, + any_event_sender: None, input_monitors: vec![], any_monitors: Default::default(), cwd: None, @@ -140,7 +140,7 @@ impl BsSystem { }; Arbiter::current().spawn({ let addr = servers_addr.clone(); - let external_event_sender = self.external_event_sender.as_ref().unwrap().clone(); + let external_event_sender = self.any_event_sender.as_ref().unwrap().clone(); let inner = debug_span!("inform_servers"); let _g = inner.enter(); @@ -148,7 +148,8 @@ impl BsSystem { let results = addr.send(InputChanged { input }).await; let Ok(result_set) = results else { - unreachable!("?1") + let e = results.unwrap_err(); + unreachable!("?1 {:?}", e); }; for (maybe_addr, x) in &result_set { @@ -216,16 +217,14 @@ impl BsSystem { } #[tracing::instrument(skip(self))] - fn publish_external_event(&mut self, evt: ExternalEvents) { - match evt { - ExternalEvents::InputError(_) => tracing::error!(?evt), - _ => tracing::debug!(?evt), - } - if let Some(external_event_sender) = &self.external_event_sender { + fn publish_any_event(&mut self, evt: AnyEvent) { + tracing::debug!(?evt); + + if let Some(any_event_sender) = &self.any_event_sender { Arbiter::current().spawn({ - let events_sender = external_event_sender.clone(); + let events_sender = any_event_sender.clone(); async move { - match events_sender.send(AnyEvent::External(evt)).await { + match events_sender.send(evt).await { Ok(_) => {} Err(_) => tracing::error!("could not send"), } @@ -253,7 +252,7 @@ impl Actor for BsSystem { tracing::trace!(actor.name = "BsSystem", actor.lifecyle = "stopped"); self.self_addr = None; self.servers_addr = None; - self.external_event_sender = None; + self.any_event_sender = None; } } @@ -274,7 +273,7 @@ impl Handler for BsSystem { type Result = StartupResult; fn handle(&mut self, msg: Start, ctx: &mut Self::Context) -> Self::Result { - self.external_event_sender = Some(msg.events_sender.clone()); + self.any_event_sender = Some(msg.events_sender.clone()); self.cwd = msg.cwd; let Some(cwd) = &self.cwd else { @@ -311,12 +310,12 @@ impl Handler for BsSystem { path: path.clone(), cwd: cwd.clone(), }); - self.publish_external_event(ExternalEvents::InputError(input_error.into())); + self.publish_any_event(AnyEvent::Internal(InternalEvents::InputError(input_error))); Ok(DidStart::Started) } Err(e) => { tracing::error!(%e); - Err(StartupError::InputError(e)) + Err(StartupError::InputError(*e)) } } } diff --git a/crates/bsnext_system/src/monitor.rs b/crates/bsnext_system/src/monitor.rs index 4424e62..b765825 100644 --- a/crates/bsnext_system/src/monitor.rs +++ b/crates/bsnext_system/src/monitor.rs @@ -3,7 +3,7 @@ use actix::{Actor, Addr, AsyncContext}; use std::hash::Hash; use bsnext_core::servers_supervisor::file_changed_handler::{FileChanged, FilesChanged}; -use bsnext_dto::{ExternalEvents, StoppedWatching, Watching}; +use bsnext_dto::{ExternalEventsDTO, StoppedWatchingDTO, WatchingDTO}; use bsnext_fs::watch_path_handler::RequestWatchPath; use bsnext_fs::{ BufferedChangeEvent, ChangeEvent, Debounce, FsEvent, FsEventKind, PathAddedEvent, PathEvent, @@ -17,6 +17,7 @@ use std::time::Duration; use bsnext_fs::actor::FsWatcher; +use bsnext_dto::internal::{AnyEvent, InternalEvents}; use bsnext_input::watch_opts::WatchOpts; use tracing::trace_span; @@ -63,11 +64,7 @@ impl actix::Handler for BsSystem { impl BsSystem { #[tracing::instrument(skip(self))] - fn handle_buffered( - &mut self, - msg: &FsEvent, - buf: &BufferedChangeEvent, - ) -> Option { + fn handle_buffered(&mut self, msg: &FsEvent, buf: &BufferedChangeEvent) -> Option { tracing::debug!(msg.event_count = buf.events.len(), msg.ctx = ?msg.ctx, ?buf); let paths = buf .events @@ -85,11 +82,11 @@ impl BsSystem { }) } // todo(alpha): need to exclude changes to the input file if this event has captured it - Some(ExternalEvents::FilesChanged(bsnext_dto::FilesChangedDTO { - paths: as_strings, - })) + Some(AnyEvent::External(ExternalEventsDTO::FilesChanged( + bsnext_dto::FilesChangedDTO { paths: as_strings }, + ))) } - fn handle_change(&mut self, msg: &FsEvent, inner: &ChangeEvent) -> Option { + fn handle_change(&mut self, msg: &FsEvent, inner: &ChangeEvent) -> Option { let span = trace_span!("handle_change", ?inner.absolute_path); let _guard = span.enter(); match msg.ctx.id() { @@ -99,15 +96,15 @@ impl BsSystem { let Ok(input) = input else { let err = input.unwrap_err(); - return Some(ExternalEvents::InputError(err.into())); + return Some(AnyEvent::Internal(InternalEvents::InputError(*err))); }; self.accept_watchables(&input); self.resolve_servers(input); - Some(ExternalEvents::InputFileChanged( - bsnext_dto::FileChanged::from_path_buf(&inner.path), - )) + Some(AnyEvent::External(ExternalEventsDTO::InputFileChanged( + bsnext_dto::FileChangedDTO::from_path_buf(&inner.path), + ))) } _id => { tracing::trace!(?inner, "Other file changed"); @@ -118,29 +115,28 @@ impl BsSystem { ctx: msg.ctx.clone(), }) } - Some(ExternalEvents::FileChanged( - bsnext_dto::FileChanged::from_path_buf(&inner.path), - )) + Some(AnyEvent::External(ExternalEventsDTO::FileChanged( + bsnext_dto::FileChangedDTO::from_path_buf(&inner.path), + ))) } } } #[tracing::instrument(skip(self))] - fn handle_path_added(&mut self, path: &PathAddedEvent) -> Option { - Some(ExternalEvents::Watching(Watching::from_path_buf( - &path.path, - path.debounce, + fn handle_path_added(&mut self, path: &PathAddedEvent) -> Option { + Some(AnyEvent::External(ExternalEventsDTO::Watching( + WatchingDTO::from_path_buf(&path.path, path.debounce), ))) } #[tracing::instrument(skip(self))] - fn handle_path_removed(&mut self, path: &PathEvent) -> Option { - Some(ExternalEvents::WatchingStopped( - StoppedWatching::from_path_buf(&path.path), - )) + fn handle_path_removed(&mut self, path: &PathEvent) -> Option { + Some(AnyEvent::External(ExternalEventsDTO::WatchingStopped( + StoppedWatchingDTO::from_path_buf(&path.path), + ))) } #[tracing::instrument(skip(self))] - fn handle_path_not_found(&mut self, pdo: &PathEvent) -> Option { + fn handle_path_not_found(&mut self, pdo: &PathEvent) -> Option { let as_str = pdo.path.to_string_lossy().to_string(); let cwd = self.cwd.clone().unwrap(); let abs = cwd.join(&as_str); @@ -152,7 +148,7 @@ impl BsSystem { let e = InputError::PathError(PathError::MissingPaths { paths: PathDefs(vec![def]), }); - Some(ExternalEvents::InputError(e.into())) + Some(AnyEvent::Internal(InternalEvents::InputError(e))) } } @@ -183,7 +179,7 @@ impl actix::Handler for BsSystem { FsEventKind::PathNotFoundError(pdo) => self.handle_path_not_found(pdo), }; if let Some(ext) = next { - self.publish_external_event(ext) + self.publish_any_event(ext) } } } @@ -270,7 +266,7 @@ pub fn to_route_watchables(input: &Input) -> Vec { let spec = to_spec(&r.opts.watch); Some(RouteWatchable { server_identity: server_config.identity.clone(), - route_path: r.path.to_string(), + route_path: r.path.as_str().to_owned(), dir: PathBuf::from(dir), spec, }) diff --git a/crates/bsnext_system/src/start_kind.rs b/crates/bsnext_system/src/start_kind.rs index e235d16..4579401 100644 --- a/crates/bsnext_system/src/start_kind.rs +++ b/crates/bsnext_system/src/start_kind.rs @@ -49,7 +49,7 @@ impl StartKind { } impl SystemStart for StartKind { - fn input(&self, ctx: &StartupContext) -> Result { + fn input(&self, ctx: &StartupContext) -> Result> { match self { StartKind::FromInputPaths(from_inputs) => from_inputs.input(ctx), StartKind::FromExample(from_example) => from_example.input(ctx), diff --git a/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap b/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap index d3fbbce..bb031ef 100644 --- a/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +++ b/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap @@ -10,7 +10,9 @@ Input { }, routes: [ Route { - path: "/", + path: PathDef { + inner: "/", + }, kind: Dir( DirRoute { dir: ".", diff --git a/crates/bsnext_system/src/start_kind/start_from_example.rs b/crates/bsnext_system/src/start_kind/start_from_example.rs index ba47ce2..f43be5d 100644 --- a/crates/bsnext_system/src/start_kind/start_from_example.rs +++ b/crates/bsnext_system/src/start_kind/start_from_example.rs @@ -16,8 +16,9 @@ pub struct StartFromExample { } impl SystemStart for StartFromExample { - fn input(&self, ctx: &StartupContext) -> Result { - let identity = ServerIdentity::from_port_or_named(self.port)?; + fn input(&self, ctx: &StartupContext) -> Result> { + let identity = + ServerIdentity::from_port_or_named(self.port).map_err(|e| Box::new(e.into()))?; let input = self.example.into_input(identity); let name = self.name.clone(); let dir = if self.temp { @@ -34,12 +35,15 @@ impl SystemStart for StartFromExample { path: next_dir.clone(), }) }) - .map(|_| next_dir.clone())? + .map(|_| next_dir.clone()) + .map_err(|e| Box::new(e.into()))? } else { ctx.cwd.to_path_buf() }; if self.write_input { - let path = fs_write_input(&dir, &input, self.target_kind.clone())?; + let path = fs_write_input(&dir, &input, self.target_kind.clone()) + .map_err(|e| Box::new(e.into()))?; + Ok(SystemStartArgs::PathWithInput { path, input }) } else { Ok(SystemStartArgs::InputOnly { input }) diff --git a/crates/bsnext_system/src/start_kind/start_from_inputs.rs b/crates/bsnext_system/src/start_kind/start_from_inputs.rs index c49cac9..7870c40 100644 --- a/crates/bsnext_system/src/start_kind/start_from_inputs.rs +++ b/crates/bsnext_system/src/start_kind/start_from_inputs.rs @@ -9,7 +9,7 @@ pub struct StartFromInputPaths { } impl SystemStart for StartFromInputPaths { - fn input(&self, ctx: &StartupContext) -> Result { + fn input(&self, ctx: &StartupContext) -> Result> { from_yml_paths(&ctx.cwd, &self.input_paths) } } @@ -20,14 +20,17 @@ pub struct StartFromInput { } impl SystemStart for StartFromInput { - fn input(&self, _ctx: &StartupContext) -> Result { + fn input(&self, _ctx: &StartupContext) -> Result> { Ok(SystemStartArgs::InputOnly { input: self.input.clone(), }) } } -fn from_yml_paths>(cwd: &Path, inputs: &[T]) -> Result { +fn from_yml_paths>( + cwd: &Path, + inputs: &[T], +) -> Result> { let input_candidates = inputs .iter() .map(|path| cwd.join(path.as_ref())) @@ -57,16 +60,16 @@ fn from_yml_paths>(cwd: &Path, inputs: &[T]) -> Result>(cwd: &Path, inputs: &[T]) -> Result Ok(SystemStartArgs::PathWithInvalidInput { - path: input_path.to_path_buf(), - input_error: InputError::YamlError(yaml_error), - }), - Err(e) => { - tracing::error!("cannot continue"); - Err(e) - } + Err(e) => match *e { + InputError::YamlError(yaml_error) => Ok(SystemStartArgs::PathWithInvalidInput { + path: input_path.to_path_buf(), + input_error: InputError::YamlError(yaml_error), + }), + _ => { + tracing::error!("cannot continue"); + Err(e) + } + }, } } diff --git a/crates/bsnext_system/src/start_kind/start_from_paths.rs b/crates/bsnext_system/src/start_kind/start_from_paths.rs index 1fa9c6d..1cb3576 100644 --- a/crates/bsnext_system/src/start_kind/start_from_paths.rs +++ b/crates/bsnext_system/src/start_kind/start_from_paths.rs @@ -12,11 +12,13 @@ pub struct StartFromPaths { } impl SystemStart for StartFromPaths { - fn input(&self, ctx: &StartupContext) -> Result { - let identity = ServerIdentity::from_port_or_named(self.port)?; - let input = from_paths(&ctx.cwd, &self.paths, identity)?; + fn input(&self, ctx: &StartupContext) -> Result> { + let identity = + ServerIdentity::from_port_or_named(self.port).map_err(|e| Box::new(e.into()))?; + let input = from_paths(&ctx.cwd, &self.paths, identity).map_err(|e| Box::new(e.into()))?; if self.write_input { - let path = fs_write_input(&ctx.cwd, &input, TargetKind::Yaml)?; + let path = fs_write_input(&ctx.cwd, &input, TargetKind::Yaml) + .map_err(|e| Box::new(e.into()))?; Ok(SystemStartArgs::PathWithInput { input, path }) } else { Ok(SystemStartArgs::InputOnly { input })