mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 13:02:07 +03:00
Update the Language Server API (#3308)
This commit is contained in:
parent
500aed9d86
commit
40f44be858
@ -1,4 +1,4 @@
|
||||
name: Base
|
||||
name: Searcher
|
||||
namespace: Standard
|
||||
version: 0.0.0-dev
|
||||
license: APLv2
|
||||
|
@ -62,12 +62,9 @@ transport formats, please look [here](./protocol-architecture).
|
||||
- [`LibraryVersion`](#libraryversion)
|
||||
- [`Contact`](#contact)
|
||||
- [`EditionReference`](#editionreference)
|
||||
- [`ComponentGroups`](#componentgroups)
|
||||
- [`ComponentGroup`](#componentgroup)
|
||||
- [`ExtendedComponentGroup`](#extendedcomponentgroup)
|
||||
- [`ModuleReference`](#modulereference)
|
||||
- [`Component`](#component)
|
||||
- [`LibraryComponentGroups`](#librarycomponentgroups)
|
||||
- [`LibraryComponentGroup`](#librarycomponentgroup)
|
||||
- [`LibraryComponent`](#librarycomponent)
|
||||
- [Connection Management](#connection-management)
|
||||
- [`session/initProtocolConnection`](#sessioninitprotocolconnection)
|
||||
- [`session/initBinaryConnection`](#sessioninitbinaryconnection)
|
||||
@ -1427,84 +1424,18 @@ interface NamedEdition {
|
||||
}
|
||||
```
|
||||
|
||||
### `ComponentGroups`
|
||||
### `LibraryComponentGroups`
|
||||
|
||||
The description of component groups provided by the package. Object fields can
|
||||
be omitted if the corresponding list is empty.
|
||||
|
||||
```typescript
|
||||
interface ComponentGroups {
|
||||
interface LibraryComponentGroups {
|
||||
/** The list of component groups provided by the package. */
|
||||
newGroups?: ComponentGroup[];
|
||||
newGroups?: LibraryComponentGroup[];
|
||||
|
||||
/** The list of component groups that this package extends.*/
|
||||
extendedGroups?: ExtendedComponentGroup[];
|
||||
}
|
||||
```
|
||||
|
||||
### `ComponentGroup`
|
||||
|
||||
The definition of a single component group.
|
||||
|
||||
```typescript
|
||||
interface ComponentGroup {
|
||||
/** The module name containing the declared componennts. */
|
||||
module: string;
|
||||
|
||||
color?: string;
|
||||
|
||||
icon?: string;
|
||||
|
||||
/** The list of components provided by this component group. */
|
||||
exports: Component[];
|
||||
}
|
||||
```
|
||||
|
||||
### `ExtendedComponentGroup`
|
||||
|
||||
The definition of a component group that extends an existing one.
|
||||
|
||||
```typescript
|
||||
interface ExtendedComponentGroup {
|
||||
/** The reference to the component group module being extended. */
|
||||
module: ModuleReference;
|
||||
|
||||
/** The list of components provided by this component group. */
|
||||
exports: Component[];
|
||||
}
|
||||
```
|
||||
|
||||
### `ModuleReference`
|
||||
|
||||
The reference to a module.
|
||||
|
||||
```typescript
|
||||
interface ModuleReference {
|
||||
/**
|
||||
* A string consisting of a namespace and a lirary name separated by the dot
|
||||
* <namespace>.<library name>, i.e. `Standard.Base`.
|
||||
*/
|
||||
libraryName: string;
|
||||
|
||||
/** The module name without the library name prefix.
|
||||
* E.g. given the `Standard.Base.Data.Vector` module reference,
|
||||
* the `moduleName` field contains `Data.Vector`.
|
||||
*/
|
||||
moduleName: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `Component`
|
||||
|
||||
A single component of a component group.
|
||||
|
||||
```typescript
|
||||
interface Component {
|
||||
/** The component name. */
|
||||
name: string;
|
||||
|
||||
/** The component shortcut. */
|
||||
shortcut?: string;
|
||||
extendedGroups?: LibraryComponentGroup[];
|
||||
}
|
||||
```
|
||||
|
||||
@ -1515,8 +1446,9 @@ The component group provided by a library.
|
||||
```typescript
|
||||
interface LibraryComponentGroup {
|
||||
/**
|
||||
* A string consisting of a namespace and a lirary name separated by the dot
|
||||
* <namespace>.<library name>, i.e. `Standard.Base`.
|
||||
* Thf fully qualified module name. A string consisting of a namespace and
|
||||
* a library name separated by the dot <namespace>.<library name>,
|
||||
* i.e. `Standard.Base`.
|
||||
*/
|
||||
library: string;
|
||||
|
||||
@ -1531,7 +1463,21 @@ interface LibraryComponentGroup {
|
||||
icon?: string;
|
||||
|
||||
/** The list of components provided by this component group. */
|
||||
exports: Component[];
|
||||
exports: LibraryComponent[];
|
||||
}
|
||||
```
|
||||
|
||||
### `LibraryComponent`
|
||||
|
||||
A single component of a component group.
|
||||
|
||||
```typescript
|
||||
interface LibraryComponent {
|
||||
/** The component name. */
|
||||
name: string;
|
||||
|
||||
/** The component shortcut. */
|
||||
shortcut?: string;
|
||||
}
|
||||
```
|
||||
|
||||
@ -4620,7 +4566,7 @@ All returned fields are optional, as they may be missing.
|
||||
```typescript
|
||||
{
|
||||
license?: String;
|
||||
componentGroups?: ComponentGroups;
|
||||
componentGroups?: LibraryComponentGroups;
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -2,7 +2,6 @@ package org.enso.languageserver.libraries
|
||||
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.pkg.{
|
||||
ComponentGroup,
|
||||
ComponentGroups,
|
||||
Config,
|
||||
ExtendedComponentGroup,
|
||||
@ -53,7 +52,9 @@ final class ComponentGroupsResolver {
|
||||
val newLibraryComponentGroups: View[LibraryComponentGroup] =
|
||||
libraryComponents.view
|
||||
.flatMap { case (libraryName, componentGroups) =>
|
||||
componentGroups.newGroups.map(toLibraryComponentGroup(libraryName, _))
|
||||
componentGroups.newGroups.map(
|
||||
LibraryComponentGroup.fromComponentGroup(libraryName, _)
|
||||
)
|
||||
}
|
||||
val newLibraryComponentGroupsMap
|
||||
: Map[ModuleReference, LibraryComponentGroup] =
|
||||
@ -114,29 +115,9 @@ final class ComponentGroupsResolver {
|
||||
extendedComponentGroup: ExtendedComponentGroup
|
||||
): LibraryComponentGroup =
|
||||
libraryComponentGroup.copy(
|
||||
exports = libraryComponentGroup.exports :++ extendedComponentGroup.exports
|
||||
exports = libraryComponentGroup.exports :++
|
||||
extendedComponentGroup.exports.map(LibraryComponent.fromComponent)
|
||||
)
|
||||
|
||||
/** Convert [[ComponentGroup]] to [[LibraryComponentGroup]] representation.
|
||||
*
|
||||
* @param libraryName the library name defining this component group
|
||||
* @param componentGroup the component group to convert
|
||||
* @return the [[LibraryComponentGroup]] representation of this component
|
||||
* group
|
||||
*/
|
||||
private def toLibraryComponentGroup(
|
||||
libraryName: LibraryName,
|
||||
componentGroup: ComponentGroup
|
||||
): LibraryComponentGroup = {
|
||||
LibraryComponentGroup(
|
||||
libraryName,
|
||||
componentGroup.module,
|
||||
componentGroup.color,
|
||||
componentGroup.icon,
|
||||
componentGroup.exports
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object ComponentGroupsResolver {
|
||||
|
@ -1,10 +1,10 @@
|
||||
package org.enso.languageserver.libraries
|
||||
|
||||
import io.circe.{Json, JsonObject}
|
||||
import io.circe.literal.JsonStringContext
|
||||
import io.circe.{Json, JsonObject}
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.jsonrpc.{Error, HasParams, HasResult, Method, Unused}
|
||||
import org.enso.pkg.{ComponentGroups, Contact}
|
||||
import org.enso.pkg.Contact
|
||||
|
||||
object LibraryApi {
|
||||
case object EditionsListAvailable extends Method("editions/listAvailable") {
|
||||
@ -190,7 +190,7 @@ object LibraryApi {
|
||||
// should be removed when the integration with IDE is finished
|
||||
case class Result(
|
||||
license: Option[String],
|
||||
componentGroups: Option[ComponentGroups],
|
||||
componentGroups: Option[LibraryComponentGroups],
|
||||
raw: JsonObject
|
||||
)
|
||||
|
||||
|
@ -3,9 +3,77 @@ package org.enso.languageserver.libraries
|
||||
import io.circe._
|
||||
import io.circe.syntax._
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.pkg.{Component, ModuleName}
|
||||
import org.enso.pkg.{
|
||||
Component,
|
||||
ComponentGroup,
|
||||
ComponentGroups,
|
||||
ExtendedComponentGroup,
|
||||
ModuleName,
|
||||
Shortcut
|
||||
}
|
||||
|
||||
/** The component group definition of a library.
|
||||
/** The description of component groups provided by the package. This
|
||||
* representation is used in the JSONRPC API.
|
||||
*
|
||||
* @param newGroups the list of component groups provided by the package
|
||||
* @param extendedGroups the list of component groups that this package extends
|
||||
*/
|
||||
case class LibraryComponentGroups(
|
||||
newGroups: List[LibraryComponentGroup],
|
||||
extendedGroups: List[LibraryComponentGroup]
|
||||
)
|
||||
object LibraryComponentGroups {
|
||||
|
||||
/** Create a [[LibraryComponentGroups]] from the provided [[ComponentGroups]].
|
||||
*
|
||||
* @param libraryName the library name defining these component groups
|
||||
* @param componentGroups the component groups to convert
|
||||
* @return the [[LibraryComponentGroups]] representation of the provided
|
||||
* component groups
|
||||
*/
|
||||
def fromComponentGroups(
|
||||
libraryName: LibraryName,
|
||||
componentGroups: ComponentGroups
|
||||
): LibraryComponentGroups =
|
||||
LibraryComponentGroups(
|
||||
componentGroups.newGroups.map(
|
||||
LibraryComponentGroup.fromComponentGroup(libraryName, _)
|
||||
),
|
||||
componentGroups.extendedGroups.map(
|
||||
LibraryComponentGroup.fromExtendedComponentGroup
|
||||
)
|
||||
)
|
||||
|
||||
/** Fields for use when serializing the [[LibraryComponentGroups]]. */
|
||||
private object Fields {
|
||||
val newGroups = "newGroups"
|
||||
val extendedGroups = "extendedGroups"
|
||||
}
|
||||
|
||||
/** [[Encoder]] instance for the [[LibraryComponentGroups]]. */
|
||||
implicit val encoder: Encoder[LibraryComponentGroups] = { componentGroups =>
|
||||
val newGroups = Option.unless(componentGroups.newGroups.isEmpty)(
|
||||
Fields.newGroups -> componentGroups.newGroups.asJson
|
||||
)
|
||||
val extendedGroups = Option.unless(componentGroups.extendedGroups.isEmpty)(
|
||||
Fields.extendedGroups -> componentGroups.extendedGroups.asJson
|
||||
)
|
||||
Json.obj(newGroups.toSeq ++ extendedGroups.toSeq: _*)
|
||||
}
|
||||
|
||||
/** [[Decoder]] instance for the [[LibraryComponentGroups]]. */
|
||||
implicit val decoder: Decoder[LibraryComponentGroups] = { json =>
|
||||
for {
|
||||
newGroups <- json
|
||||
.getOrElse[List[LibraryComponentGroup]](Fields.newGroups)(List())
|
||||
extendedGroups <- json
|
||||
.getOrElse[List[LibraryComponentGroup]](Fields.extendedGroups)(List())
|
||||
} yield LibraryComponentGroups(newGroups, extendedGroups)
|
||||
}
|
||||
}
|
||||
|
||||
/** The component group definition of a library. This representation is used in
|
||||
* the JSONRPC API.
|
||||
*
|
||||
* @param library the library name
|
||||
* @param module the module name
|
||||
@ -18,10 +86,47 @@ case class LibraryComponentGroup(
|
||||
module: ModuleName,
|
||||
color: Option[String],
|
||||
icon: Option[String],
|
||||
exports: Seq[Component]
|
||||
exports: Seq[LibraryComponent]
|
||||
)
|
||||
object LibraryComponentGroup {
|
||||
|
||||
/** create a [[LibraryComponentGroup]] from the provided [[ComponentGroup]].
|
||||
*
|
||||
* @param libraryName the library name defining this component group
|
||||
* @param componentGroup the component group to convert
|
||||
* @return the [[LibraryComponentGroup]] representation of this component
|
||||
* group
|
||||
*/
|
||||
def fromComponentGroup(
|
||||
libraryName: LibraryName,
|
||||
componentGroup: ComponentGroup
|
||||
): LibraryComponentGroup =
|
||||
LibraryComponentGroup(
|
||||
library = libraryName,
|
||||
module = componentGroup.module,
|
||||
color = componentGroup.color,
|
||||
icon = componentGroup.icon,
|
||||
exports = componentGroup.exports.map(LibraryComponent.fromComponent)
|
||||
)
|
||||
|
||||
/** Create a [[LibraryComponentGroup]] from the [[ExtendedComponentGroup]].
|
||||
*
|
||||
* @param extendedComponentGroup the extended component group to convert
|
||||
* @return the [[LibraryComponentGroup]] representation of the provided
|
||||
* extended component group
|
||||
*/
|
||||
def fromExtendedComponentGroup(
|
||||
extendedComponentGroup: ExtendedComponentGroup
|
||||
): LibraryComponentGroup =
|
||||
LibraryComponentGroup(
|
||||
library = extendedComponentGroup.module.libraryName,
|
||||
module = extendedComponentGroup.module.moduleName,
|
||||
color = None,
|
||||
icon = None,
|
||||
exports =
|
||||
extendedComponentGroup.exports.map(LibraryComponent.fromComponent)
|
||||
)
|
||||
|
||||
/** Fields for use when serializing the [[LibraryComponentGroup]]. */
|
||||
private object Fields {
|
||||
val Library = "library"
|
||||
@ -52,8 +157,47 @@ object LibraryComponentGroup {
|
||||
module <- json.get[ModuleName](Fields.Module)
|
||||
color <- json.get[Option[String]](Fields.Color)
|
||||
icon <- json.get[Option[String]](Fields.Icon)
|
||||
exports <- json.getOrElse[List[Component]](Fields.Exports)(List())
|
||||
exports <- json.getOrElse[List[LibraryComponent]](Fields.Exports)(List())
|
||||
} yield LibraryComponentGroup(library, module, color, icon, exports)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** A single component of a component group. This representation is used in
|
||||
* the JSONRPC API.
|
||||
*
|
||||
* @param name the component name
|
||||
* @param shortcut the component shortcut
|
||||
*/
|
||||
case class LibraryComponent(name: String, shortcut: Option[Shortcut])
|
||||
object LibraryComponent {
|
||||
|
||||
/** Create a [[LibraryComponent]] from the provided [[Component]].
|
||||
*
|
||||
* @param component the component to convert
|
||||
* @return the [[LibraryComponent]] representation of this component
|
||||
*/
|
||||
def fromComponent(component: Component): LibraryComponent =
|
||||
LibraryComponent(component.name, component.shortcut)
|
||||
|
||||
object Fields {
|
||||
val Name = "name"
|
||||
val Shortcut = "shortcut"
|
||||
}
|
||||
|
||||
/** [[Encoder]] instance for the [[LibraryComponent]]. */
|
||||
implicit val encoder: Encoder[LibraryComponent] = { component =>
|
||||
val shortcut = component.shortcut.map(Fields.Shortcut -> _.asJson)
|
||||
Json.obj(
|
||||
Seq(Fields.Name -> component.name.asJson) ++
|
||||
shortcut.toSeq: _*
|
||||
)
|
||||
}
|
||||
|
||||
/** [[Decoder]] instance for the [[LibraryComponent]]. */
|
||||
implicit val decoder: Decoder[LibraryComponent] = { json =>
|
||||
for {
|
||||
name <- json.get[String](Fields.Name)
|
||||
shortcut <- json.getOrElse[Option[Shortcut]](Fields.Shortcut)(None)
|
||||
} yield LibraryComponent(name, shortcut)
|
||||
}
|
||||
}
|
||||
|
@ -202,12 +202,16 @@ class LocalLibraryManager(
|
||||
configPath = libraryRootPath / Package.configFileName
|
||||
config <- loadPackageConfig(configPath)
|
||||
} yield {
|
||||
if (config.componentGroups.isLeft) {
|
||||
logger.error(
|
||||
s"Failed to parse library [$libraryName] component groups."
|
||||
)
|
||||
config.componentGroups match {
|
||||
case Left(error) =>
|
||||
logger.error(
|
||||
s"Failed to parse library [$libraryName] component groups " +
|
||||
s"(reason: ${error.message})."
|
||||
)
|
||||
case _ =>
|
||||
}
|
||||
GetPackageResponse(
|
||||
libraryName = LibraryName(config.namespace, config.name),
|
||||
license = config.license,
|
||||
componentGroups = config.componentGroups.toOption,
|
||||
rawPackage = config.originalJson
|
||||
|
@ -1,6 +1,8 @@
|
||||
package org.enso.languageserver.libraries
|
||||
|
||||
import org.enso.editions.provider.EditionNotFound
|
||||
import org.enso.jsonrpc
|
||||
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||
|
||||
/** The object mapping the [[LocalLibraryManagerProtocol]] failures into the
|
||||
* corresponding JSONRPC error messages.
|
||||
@ -18,4 +20,20 @@ object LocalLibraryManagerFailureMapper {
|
||||
case LocalLibraryManagerProtocol.InvalidSemverVersionError(version) =>
|
||||
LibraryApi.InvalidSemverVersion(version)
|
||||
}
|
||||
|
||||
/** Convert the exceptions raised in the library management api to the
|
||||
* corresponding JSONRPC errors.
|
||||
*
|
||||
* @param error the raised exception
|
||||
* @return the JSONRPC error message
|
||||
*/
|
||||
def mapException(error: Throwable): jsonrpc.Error =
|
||||
error match {
|
||||
case ex: LocalLibraryManagerProtocol.LocalLibraryNotFoundError =>
|
||||
LibraryApi.LocalLibraryNotFound(ex.libraryName)
|
||||
case ex: EditionNotFound =>
|
||||
LibraryApi.EditionNotFoundError(ex.editionName)
|
||||
case _ =>
|
||||
FileSystemError(error.getMessage)
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@ object LocalLibraryManagerProtocol {
|
||||
|
||||
/** A response to the [[GetPackage]] request. */
|
||||
case class GetPackageResponse(
|
||||
libraryName: LibraryName,
|
||||
license: String,
|
||||
componentGroups: Option[ComponentGroups],
|
||||
rawPackage: JsonObject
|
||||
|
@ -5,8 +5,10 @@ import akka.pattern.pipe
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.editions.updater.EditionManager
|
||||
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||
import org.enso.languageserver.libraries.BlockingOperation
|
||||
import org.enso.languageserver.libraries.{
|
||||
BlockingOperation,
|
||||
LocalLibraryManagerFailureMapper
|
||||
}
|
||||
import org.enso.languageserver.libraries.LibraryApi._
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
|
||||
@ -49,7 +51,7 @@ class EditionsListAvailableHandler(editionManager: EditionManager)
|
||||
case Status.Failure(exception) =>
|
||||
replyTo ! ResponseError(
|
||||
Some(id),
|
||||
FileSystemError(exception.toString)
|
||||
LocalLibraryManagerFailureMapper.mapException(exception)
|
||||
)
|
||||
context.stop(self)
|
||||
}
|
||||
|
@ -5,14 +5,14 @@ import akka.pattern.pipe
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.editions.{LibraryName, LibraryVersion}
|
||||
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||
import org.enso.languageserver.libraries.LibraryApi._
|
||||
import org.enso.languageserver.libraries.{
|
||||
BlockingOperation,
|
||||
ComponentGroupsResolver,
|
||||
ComponentGroupsValidator,
|
||||
EditionReferenceResolver,
|
||||
LibraryComponentGroup
|
||||
LibraryComponentGroup,
|
||||
LocalLibraryManagerFailureMapper
|
||||
}
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||
@ -50,7 +50,7 @@ class EditionsListDefinedComponentsHandler(
|
||||
BlockingOperation
|
||||
.run {
|
||||
val edition = editionReferenceResolver.resolveEdition(reference).get
|
||||
val definedLibraries = edition.getAllDefinedLibraries.view
|
||||
val definedLibraries = edition.getAllDefinedLibraries.toSeq
|
||||
.map { case (name, version) =>
|
||||
readLocalPackage(name, version)
|
||||
}
|
||||
@ -109,7 +109,7 @@ class EditionsListDefinedComponentsHandler(
|
||||
case Status.Failure(exception) =>
|
||||
replyTo ! ResponseError(
|
||||
Some(id),
|
||||
FileSystemError(exception.getMessage)
|
||||
LocalLibraryManagerFailureMapper.mapException(exception)
|
||||
)
|
||||
context.stop(self)
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ import akka.pattern.pipe
|
||||
import com.typesafe.scalalogging.LazyLogging
|
||||
import org.enso.editions.LibraryVersion
|
||||
import org.enso.jsonrpc.{Id, Request, ResponseError, ResponseResult}
|
||||
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||
import org.enso.languageserver.libraries.LibraryApi._
|
||||
import org.enso.languageserver.libraries.{
|
||||
BlockingOperation,
|
||||
EditionReferenceResolver,
|
||||
LibraryEntry
|
||||
LibraryEntry,
|
||||
LocalLibraryManagerFailureMapper
|
||||
}
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
import org.enso.librarymanager.local.LocalLibraryProvider
|
||||
@ -74,10 +74,9 @@ class EditionsListDefinedLibrariesHandler(
|
||||
context.stop(self)
|
||||
|
||||
case Status.Failure(exception) =>
|
||||
// TODO [RW] more detailed errors
|
||||
replyTo ! ResponseError(
|
||||
Some(id),
|
||||
FileSystemError(exception.getMessage)
|
||||
LocalLibraryManagerFailureMapper.mapException(exception)
|
||||
)
|
||||
context.stop(self)
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||
import org.enso.languageserver.libraries.LibraryApi._
|
||||
import org.enso.languageserver.libraries.{
|
||||
LibraryEntry,
|
||||
@ -101,7 +100,10 @@ class LibraryGetMetadataHandler(
|
||||
context.stop(self)
|
||||
|
||||
case Status.Failure(exception) =>
|
||||
replyTo ! ResponseError(Some(id), FileSystemError(exception.getMessage))
|
||||
replyTo ! ResponseError(
|
||||
Some(id),
|
||||
LocalLibraryManagerFailureMapper.mapException(exception)
|
||||
)
|
||||
cancellable.cancel()
|
||||
context.stop(self)
|
||||
}
|
||||
|
@ -7,13 +7,8 @@ import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.editions.Editions.Repository
|
||||
import org.enso.editions.LibraryName
|
||||
import org.enso.jsonrpc._
|
||||
import org.enso.languageserver.filemanager.FileManagerApi.FileSystemError
|
||||
import org.enso.languageserver.libraries.LibraryApi._
|
||||
import org.enso.languageserver.libraries.{
|
||||
LibraryEntry,
|
||||
LocalLibraryManagerFailureMapper,
|
||||
LocalLibraryManagerProtocol
|
||||
}
|
||||
import org.enso.languageserver.libraries._
|
||||
import org.enso.languageserver.requesthandler.RequestTimeout
|
||||
import org.enso.languageserver.util.UnhandledLogging
|
||||
import org.enso.librarymanager.published.PublishedLibraryCache
|
||||
@ -83,6 +78,7 @@ class LibraryGetPackageHandler(
|
||||
context.stop(self)
|
||||
|
||||
case LocalLibraryManagerProtocol.GetPackageResponse(
|
||||
libraryName,
|
||||
license,
|
||||
componentGroups,
|
||||
rawPackage
|
||||
@ -92,7 +88,11 @@ class LibraryGetPackageHandler(
|
||||
id,
|
||||
LibraryGetPackage.Result(
|
||||
Option.unless(license.isEmpty)(license),
|
||||
componentGroups,
|
||||
componentGroups.flatMap { groups =>
|
||||
Option.unless(
|
||||
groups.newGroups.isEmpty && groups.extendedGroups.isEmpty
|
||||
)(LibraryComponentGroups.fromComponentGroups(libraryName, groups))
|
||||
},
|
||||
rawPackage
|
||||
)
|
||||
)
|
||||
@ -105,7 +105,10 @@ class LibraryGetPackageHandler(
|
||||
context.stop(self)
|
||||
|
||||
case Status.Failure(exception) =>
|
||||
replyTo ! ResponseError(Some(id), FileSystemError(exception.getMessage))
|
||||
replyTo ! ResponseError(
|
||||
Some(id),
|
||||
LocalLibraryManagerFailureMapper.mapException(exception)
|
||||
)
|
||||
cancellable.cancel()
|
||||
context.stop(self)
|
||||
}
|
||||
@ -133,6 +136,7 @@ class LibraryGetPackageHandler(
|
||||
.readPackage()
|
||||
.map(config =>
|
||||
LocalLibraryManagerProtocol.GetPackageResponse(
|
||||
LibraryName(config.namespace, config.name),
|
||||
config.license,
|
||||
config.componentGroups.toOption,
|
||||
config.originalJson
|
||||
@ -150,6 +154,7 @@ class LibraryGetPackageHandler(
|
||||
.fetchPackageConfig()
|
||||
.toFuture
|
||||
} yield LocalLibraryManagerProtocol.GetPackageResponse(
|
||||
libraryName = LibraryName(config.namespace, config.name),
|
||||
license = config.license,
|
||||
componentGroups = config.componentGroups.toOption,
|
||||
rawPackage = config.originalJson
|
||||
|
@ -311,6 +311,6 @@ object ComponentGroupsResolverSpec {
|
||||
module = ModuleName(module),
|
||||
color = None,
|
||||
icon = None,
|
||||
exports = exports.map(Component(_, None))
|
||||
exports = exports.map(LibraryComponent(_, None))
|
||||
)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ import io.circe.{Json, JsonObject}
|
||||
import nl.gn0s1s.bump.SemVer
|
||||
import org.enso.distribution.FileSystem
|
||||
import org.enso.editions.{Editions, LibraryName}
|
||||
import org.enso.languageserver.libraries.LibraryEntry
|
||||
import org.enso.languageserver.libraries.{LibraryComponentGroups, LibraryEntry}
|
||||
import org.enso.languageserver.libraries.LibraryEntry.PublishedLibraryVersion
|
||||
import org.enso.librarymanager.published.bundles.LocalReadOnlyRepository
|
||||
import org.enso.librarymanager.published.repository.{
|
||||
@ -227,6 +227,32 @@ class LibrariesTest extends BaseServerTest {
|
||||
""")
|
||||
}
|
||||
|
||||
"return LibraryNotFound error when getting the metadata of unknown library" in {
|
||||
val client = getInitialisedWsClient()
|
||||
client.send(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "library/getMetadata",
|
||||
"id": 0,
|
||||
"params": {
|
||||
"namespace": "user",
|
||||
"name": "Get_Package_Unknown",
|
||||
"version": {
|
||||
"type": "LocalLibraryVersion"
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
client.expectJson(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"error": {
|
||||
"code": 8007,
|
||||
"message": "Local library [user.Get_Package_Unknown] has not been found."
|
||||
}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
"get the package config" in {
|
||||
val client = getInitialisedWsClient()
|
||||
val testComponentGroups = ComponentGroups(
|
||||
@ -313,8 +339,37 @@ class LibrariesTest extends BaseServerTest {
|
||||
response.hcursor
|
||||
.downField("result")
|
||||
.downField("componentGroups")
|
||||
.as[ComponentGroups]
|
||||
.rightValue shouldEqual testComponentGroups
|
||||
.as[LibraryComponentGroups]
|
||||
.rightValue shouldEqual LibraryComponentGroups.fromComponentGroups(
|
||||
LibraryName("user", "Get_Package_Test_Lib"),
|
||||
testComponentGroups
|
||||
)
|
||||
}
|
||||
|
||||
"return LibraryNotFound error when getting the package of unknown library" in {
|
||||
val client = getInitialisedWsClient()
|
||||
client.send(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"method": "library/getPackage",
|
||||
"id": 0,
|
||||
"params": {
|
||||
"namespace": "user",
|
||||
"name": "Get_Package_Unknown",
|
||||
"version": {
|
||||
"type": "LocalLibraryVersion"
|
||||
}
|
||||
}
|
||||
}
|
||||
""")
|
||||
client.expectJson(json"""
|
||||
{ "jsonrpc": "2.0",
|
||||
"id": 0,
|
||||
"error": {
|
||||
"code": 8007,
|
||||
"message": "Local library [user.Get_Package_Unknown] has not been found."
|
||||
}
|
||||
}
|
||||
""")
|
||||
}
|
||||
|
||||
"create, publish a library and fetch its manifest from the server" in {
|
||||
|
@ -33,12 +33,12 @@ class UpdatingEditionProvider(
|
||||
name: String
|
||||
): Either[EditionLoadingError, Editions.Raw.Edition] = {
|
||||
provider.findEditionForName(name) match {
|
||||
case Left(EditionNotFound()) =>
|
||||
case Left(EditionNotFound(_)) =>
|
||||
if (!wasUpdateTried) {
|
||||
wasUpdateTried = true
|
||||
updater.updateEditions()
|
||||
provider.findEditionForName(name)
|
||||
} else Left(EditionNotFound())
|
||||
} else Left(EditionNotFound(name))
|
||||
case Left(otherError) => Left(otherError)
|
||||
case Right(value) => Right(value)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ sealed class EditionLoadingError(message: String, cause: Throwable = null)
|
||||
extends RuntimeException(message, cause)
|
||||
|
||||
/** Indicates that the requested edition was not found in the caches. */
|
||||
case class EditionNotFound()
|
||||
case class EditionNotFound(editionName: String)
|
||||
extends EditionLoadingError("The edition was not found.")
|
||||
|
||||
/** Indicates that the edition was found but could not be read, for example due
|
||||
|
@ -28,11 +28,11 @@ class FileSystemEditionProvider(searchPaths: List[Path])
|
||||
case head :: tail =>
|
||||
val headResult = loadEdition(name, head)
|
||||
headResult match {
|
||||
case Left(EditionNotFound()) =>
|
||||
case Left(EditionNotFound(_)) =>
|
||||
findEdition(name, tail)
|
||||
case _ => headResult
|
||||
}
|
||||
case Nil => Left(EditionNotFound())
|
||||
case Nil => Left(EditionNotFound(name))
|
||||
}
|
||||
|
||||
private def loadEdition(
|
||||
@ -47,7 +47,7 @@ class FileSystemEditionProvider(searchPaths: List[Path])
|
||||
.toEither
|
||||
.left
|
||||
.map(EditionReadError)
|
||||
} else Left(EditionNotFound())
|
||||
} else Left(EditionNotFound(name))
|
||||
}
|
||||
|
||||
/** Finds all editions available on the [[searchPaths]]. */
|
||||
|
@ -52,7 +52,7 @@ class EditionResolverSpec
|
||||
override def findEditionForName(
|
||||
name: String
|
||||
): Either[EditionLoadingError, Editions.Raw.Edition] =
|
||||
editions.get(name).toRight(EditionNotFound())
|
||||
editions.get(name).toRight(EditionNotFound(name))
|
||||
|
||||
override def findAvailableEditions(): Seq[String] = editions.keys.toSeq
|
||||
}
|
||||
|
@ -63,7 +63,6 @@ object ComponentGroup {
|
||||
|
||||
/** Fields for use when serializing the [[ComponentGroup]]. */
|
||||
private object Fields {
|
||||
val Module = "module"
|
||||
val Color = "color"
|
||||
val Icon = "icon"
|
||||
val Exports = "exports"
|
||||
@ -77,26 +76,45 @@ object ComponentGroup {
|
||||
Fields.Exports -> componentGroup.exports.asJson
|
||||
)
|
||||
Json.obj(
|
||||
(Fields.Module -> componentGroup.module.asJson) +:
|
||||
(color.toSeq ++ icon.toSeq ++ exports.toSeq): _*
|
||||
componentGroup.module.name -> Json.obj(
|
||||
color.toSeq ++ icon.toSeq ++ exports.toSeq: _*
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** [[Decoder]] instance for the [[ComponentGroup]]. */
|
||||
implicit val decoder: Decoder[ComponentGroup] = { json =>
|
||||
for {
|
||||
name <- ConfigCodecs
|
||||
.getFromObject[ModuleName](
|
||||
"component group name",
|
||||
Fields.Module,
|
||||
json
|
||||
)
|
||||
color <- json.get[Option[String]](Fields.Color)
|
||||
icon <- json.get[Option[String]](Fields.Icon)
|
||||
exports <- json.getOrElse[List[Component]](Fields.Exports)(List())
|
||||
} yield ComponentGroup(name, color, icon, exports)
|
||||
name <- decodeName(json)
|
||||
componentGroup <- decodeComponentGroup(
|
||||
ModuleName(name),
|
||||
json.downField(name)
|
||||
)
|
||||
} yield componentGroup
|
||||
}
|
||||
|
||||
private def decodeName(cursor: ACursor): Decoder.Result[String] =
|
||||
ConfigCodecs
|
||||
.getNameFromKey(cursor)
|
||||
.toRight(decodingFailure(cursor.history))
|
||||
|
||||
private def decodeComponentGroup(
|
||||
name: ModuleName,
|
||||
cursor: ACursor
|
||||
): Decoder.Result[ComponentGroup] = {
|
||||
if (cursor.keys.nonEmpty) {
|
||||
for {
|
||||
color <- cursor.get[Option[String]](Fields.Color)
|
||||
icon <- cursor.get[Option[String]](Fields.Icon)
|
||||
exports <- cursor.getOrElse[List[Component]](Fields.Exports)(List())
|
||||
} yield ComponentGroup(name, color, icon, exports)
|
||||
} else {
|
||||
Left(decodingFailure(cursor.history))
|
||||
}
|
||||
}
|
||||
|
||||
private def decodingFailure(history: List[CursorOp]): DecodingFailure =
|
||||
DecodingFailure("Failed to decode component group", history)
|
||||
}
|
||||
|
||||
/** The definition of a component group that extends an existing one.
|
||||
@ -112,7 +130,6 @@ object ExtendedComponentGroup {
|
||||
|
||||
/** Fields for use when serializing the [[ExtendedComponentGroup]]. */
|
||||
private object Fields {
|
||||
val Module = "module"
|
||||
val Exports = "exports"
|
||||
}
|
||||
|
||||
@ -123,22 +140,54 @@ object ExtendedComponentGroup {
|
||||
Fields.Exports -> extendedComponentGroup.exports.asJson
|
||||
)
|
||||
Json.obj(
|
||||
(Fields.Module -> extendedComponentGroup.module.asJson) +: exports.toSeq: _*
|
||||
extendedComponentGroup.module.qualifiedName -> Json.obj(
|
||||
exports.toSeq: _*
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** [[Decoder]] instance for the [[ExtendedComponentGroup]]. */
|
||||
implicit val decoder: Decoder[ExtendedComponentGroup] = { json =>
|
||||
for {
|
||||
reference <- ConfigCodecs
|
||||
.getFromObject[ModuleReference](
|
||||
"extended component group reference",
|
||||
Fields.Module,
|
||||
json
|
||||
moduleName <- decodeModuleName(json)
|
||||
moduleReference <- ModuleReference
|
||||
.fromModuleName(moduleName)
|
||||
.toRight(
|
||||
DecodingFailure(
|
||||
s"Failed to decode '$moduleName' as a module reference. " +
|
||||
s"Module reference should consist of a namespace (author), " +
|
||||
s"library name and a module name (e.g. Standard.Base.Data).",
|
||||
json.history
|
||||
)
|
||||
)
|
||||
exports <- json.getOrElse[List[Component]](Fields.Exports)(List())
|
||||
} yield ExtendedComponentGroup(reference, exports)
|
||||
componentGroup <-
|
||||
decodeExtendedComponentGroup(
|
||||
moduleReference,
|
||||
json.downField(moduleName)
|
||||
)
|
||||
} yield componentGroup
|
||||
}
|
||||
|
||||
private def decodeModuleName(cursor: ACursor): Decoder.Result[String] =
|
||||
ConfigCodecs
|
||||
.getNameFromKey(cursor)
|
||||
.toRight(decodingFailure(cursor.history))
|
||||
|
||||
private def decodeExtendedComponentGroup(
|
||||
reference: ModuleReference,
|
||||
cursor: ACursor
|
||||
): Decoder.Result[ExtendedComponentGroup] =
|
||||
if (cursor.keys.nonEmpty) {
|
||||
for {
|
||||
exports <- cursor.getOrElse[List[Component]](Fields.Exports)(List())
|
||||
} yield ExtendedComponentGroup(reference, exports)
|
||||
} else {
|
||||
Left(decodingFailure(cursor.history))
|
||||
}
|
||||
|
||||
private def decodingFailure(history: List[CursorOp]): DecodingFailure =
|
||||
DecodingFailure("Failed to decode extended component group", history)
|
||||
|
||||
}
|
||||
|
||||
/** A single component of a component group.
|
||||
@ -150,7 +199,6 @@ case class Component(name: String, shortcut: Option[Shortcut])
|
||||
object Component {
|
||||
|
||||
object Fields {
|
||||
val Name = "name"
|
||||
val Shortcut = "shortcut"
|
||||
}
|
||||
|
||||
@ -159,8 +207,9 @@ object Component {
|
||||
component.shortcut match {
|
||||
case Some(shortcut) =>
|
||||
Json.obj(
|
||||
Fields.Name -> component.name.asJson,
|
||||
Fields.Shortcut -> shortcut.asJson
|
||||
component.name -> Json.obj(
|
||||
Fields.Shortcut -> shortcut.asJson
|
||||
)
|
||||
)
|
||||
case None =>
|
||||
component.name.asJson
|
||||
@ -175,11 +224,28 @@ object Component {
|
||||
case Left(_) =>
|
||||
for {
|
||||
name <- ConfigCodecs
|
||||
.getFromObject[String]("component name", Fields.Name, json)
|
||||
shortcut <- json.getOrElse[Option[Shortcut]](Fields.Shortcut)(None)
|
||||
} yield Component(name, shortcut)
|
||||
.getNameFromKey(json)
|
||||
.toRight(decodingFailure(json.history))
|
||||
component <- decodeComponent(name, json.downField(name))
|
||||
} yield component
|
||||
}
|
||||
}
|
||||
|
||||
private def decodeComponent(
|
||||
name: String,
|
||||
cursor: ACursor
|
||||
): Decoder.Result[Component] = {
|
||||
if (cursor.keys.nonEmpty) {
|
||||
for {
|
||||
shortcut <- cursor.getOrElse[Option[Shortcut]](Fields.Shortcut)(None)
|
||||
} yield Component(name, shortcut)
|
||||
} else {
|
||||
Left(decodingFailure(cursor.history))
|
||||
}
|
||||
}
|
||||
|
||||
private def decodingFailure(history: List[CursorOp]): DecodingFailure =
|
||||
DecodingFailure("Failed to decode exported component", history)
|
||||
}
|
||||
|
||||
/** The shortcut reference to the component.
|
||||
@ -196,7 +262,12 @@ object Shortcut {
|
||||
|
||||
/** [[Decoder]] instance for the [[Shortcut]]. */
|
||||
implicit val decoder: Decoder[Shortcut] = { json =>
|
||||
ConfigCodecs.getScalar("shortcut", json).map(Shortcut(_))
|
||||
ConfigCodecs
|
||||
.getScalar(json)
|
||||
.map(Shortcut(_))
|
||||
.toRight(
|
||||
DecodingFailure("Failed to decode shortcut", json.history)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,41 +293,19 @@ case class ModuleReference(
|
||||
}
|
||||
object ModuleReference {
|
||||
|
||||
private def toModuleString(moduleReference: ModuleReference): String = {
|
||||
s"${moduleReference.libraryName.namespace}${LibraryName.separator}" +
|
||||
s"${moduleReference.libraryName.name}${LibraryName.separator}" +
|
||||
moduleReference.moduleName.name
|
||||
}
|
||||
|
||||
/** [[Encoder]] instance for the [[ModuleReference]]. */
|
||||
implicit val encoder: Encoder[ModuleReference] = { moduleReference =>
|
||||
toModuleString(moduleReference).asJson
|
||||
}
|
||||
|
||||
/** [[Decoder]] instance for the [[ModuleReference]]. */
|
||||
implicit val decoder: Decoder[ModuleReference] = { json =>
|
||||
json.as[String].flatMap { moduleString =>
|
||||
moduleString.split(LibraryName.separator).toList match {
|
||||
case namespace :: name :: module :: modules =>
|
||||
Right(
|
||||
ModuleReference(
|
||||
LibraryName(namespace, name),
|
||||
ModuleName.fromComponents(module, modules)
|
||||
)
|
||||
/** Create a [[ModuleReference]] from string. */
|
||||
def fromModuleName(moduleName: String): Option[ModuleReference] =
|
||||
moduleName.split(LibraryName.separator).toList match {
|
||||
case namespace :: name :: module :: modules =>
|
||||
Some(
|
||||
ModuleReference(
|
||||
LibraryName(namespace, name),
|
||||
ModuleName.fromComponents(module, modules)
|
||||
)
|
||||
case _ =>
|
||||
Left(
|
||||
DecodingFailure(
|
||||
s"Failed to decode '$moduleString' as module reference. " +
|
||||
s"Module reference should consist of a namespace (author), " +
|
||||
s"library name and a module name (e.g. Standard.Base.Data).",
|
||||
json.history
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
case _ =>
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** The module name.
|
||||
@ -266,6 +315,7 @@ object ModuleReference {
|
||||
case class ModuleName(name: String)
|
||||
object ModuleName {
|
||||
|
||||
/** Create a [[ModuleName]] from its components. */
|
||||
def fromComponents(item: String, items: List[String]): ModuleName =
|
||||
ModuleName((item :: items).mkString(LibraryName.separator.toString))
|
||||
|
||||
|
@ -1,74 +1,38 @@
|
||||
package org.enso.pkg
|
||||
|
||||
import io.circe._
|
||||
import io.circe.syntax._
|
||||
|
||||
/** A collection of utility codecs used in the [[Config]]. */
|
||||
object ConfigCodecs {
|
||||
|
||||
/** The common decoding failure.
|
||||
/** Get the decoded entity name.
|
||||
*
|
||||
* @param entity the name of decoded entity
|
||||
* @param history the list of JSON cursor operations
|
||||
*/
|
||||
private def decodingFailure(
|
||||
entity: String,
|
||||
history: List[CursorOp]
|
||||
): DecodingFailure =
|
||||
DecodingFailure(s"Failed to decode $entity", history)
|
||||
|
||||
/** Try to decode the entity `A` from a JSON object.
|
||||
*
|
||||
* The entity can be encoded either in the first key of the JSON object with
|
||||
* the Null value,
|
||||
* {{{
|
||||
* { entity: null }
|
||||
* }}}
|
||||
*
|
||||
* or as a value of the provided `keyName` argument
|
||||
* The entity name is encoded as a key of JSON object.
|
||||
*
|
||||
* {{{
|
||||
* { `keyName`: entity }
|
||||
* { `name`: entity }
|
||||
* }}}
|
||||
*
|
||||
* @param entity the name of decoded entity that is used in error reporting
|
||||
* @param keyName the key of the JSON object that contains the entity
|
||||
* @param cursor the current focus in the JSON document
|
||||
*/
|
||||
def getFromObject[A: Decoder](
|
||||
entity: String,
|
||||
keyName: String,
|
||||
cursor: HCursor
|
||||
): Decoder.Result[A] =
|
||||
cursor.keys match {
|
||||
case Some(keys) if keys.nonEmpty =>
|
||||
cursor
|
||||
.get[A](keyName)
|
||||
.orElse {
|
||||
cursor.get[Json](keys.head).flatMap { json =>
|
||||
if (json.isNull) {
|
||||
Decoder[A].decodeJson(keys.head.asJson)
|
||||
} else {
|
||||
Left(decodingFailure(entity, cursor.history))
|
||||
}
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
Left(decodingFailure(entity, cursor.history))
|
||||
def getNameFromKey(cursor: ACursor): Option[String] =
|
||||
cursor.keys.flatMap {
|
||||
case keys if keys.size == 1 => keys.headOption
|
||||
case _ => None
|
||||
}
|
||||
|
||||
/** Get the scalar value of the provided JSON element.
|
||||
*
|
||||
* @param entity the name of decoded entity
|
||||
* @param cursor the current focus in the JSON document
|
||||
*/
|
||||
def getScalar(entity: String, cursor: HCursor): Decoder.Result[String] =
|
||||
def getScalar(cursor: HCursor): Option[String] =
|
||||
cursor.value.fold(
|
||||
jsonNull = Left(decodingFailure(entity, cursor.history)),
|
||||
jsonBoolean = value => Right(value.toString),
|
||||
jsonNumber = value => Right(value.toString),
|
||||
jsonString = value => Right(value),
|
||||
jsonArray = _ => Left(decodingFailure(entity, cursor.history)),
|
||||
jsonObject = _ => Left(decodingFailure(entity, cursor.history))
|
||||
jsonNull = None,
|
||||
jsonBoolean = value => Some(value.toString),
|
||||
jsonNumber = value => Some(value.toString),
|
||||
jsonString = value => Some(value),
|
||||
jsonArray = _ => None,
|
||||
jsonObject = _ => None
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -102,17 +102,19 @@ class ConfigSpec
|
||||
"""name: FooBar
|
||||
|component-groups:
|
||||
| new:
|
||||
| - Group 1:
|
||||
| color: '#C047AB'
|
||||
| icon: icon-name
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: f
|
||||
| - bar
|
||||
| - Group 1:
|
||||
| color: '#C047AB'
|
||||
| icon: icon-name
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: f
|
||||
| - bar
|
||||
| extends:
|
||||
| - Standard.Base.Group 2:
|
||||
| exports:
|
||||
| - bax
|
||||
| - Standard.Base.Group 2:
|
||||
| exports:
|
||||
| - baz:
|
||||
| shortcut: k
|
||||
| - quux
|
||||
|""".stripMargin
|
||||
val parsed = Config.fromYaml(config).get
|
||||
|
||||
@ -134,7 +136,10 @@ class ConfigSpec
|
||||
LibraryName("Standard", "Base"),
|
||||
ModuleName("Group 2")
|
||||
),
|
||||
exports = List(Component("bax", None))
|
||||
exports = List(
|
||||
Component("baz", Some(Shortcut("k"))),
|
||||
Component("quux", None)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ -144,17 +149,19 @@ class ConfigSpec
|
||||
serialized should include(
|
||||
"""component-groups:
|
||||
| new:
|
||||
| - module: Group 1
|
||||
| color: '#C047AB'
|
||||
| icon: icon-name
|
||||
| exports:
|
||||
| - name: foo
|
||||
| shortcut: f
|
||||
| - bar
|
||||
| - Group 1:
|
||||
| color: '#C047AB'
|
||||
| icon: icon-name
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: f
|
||||
| - bar
|
||||
| extends:
|
||||
| - module: Standard.Base.Group 2
|
||||
| exports:
|
||||
| - bax""".stripMargin.linesIterator.mkString("\n")
|
||||
| - Standard.Base.Group 2:
|
||||
| exports:
|
||||
| - baz:
|
||||
| shortcut: k
|
||||
| - quux""".stripMargin.linesIterator.mkString("\n")
|
||||
)
|
||||
}
|
||||
|
||||
@ -188,15 +195,15 @@ class ConfigSpec
|
||||
|component-groups:
|
||||
| extends:
|
||||
| - Group 1:
|
||||
| exports:
|
||||
| - bax
|
||||
| exports:
|
||||
| - bax
|
||||
|""".stripMargin
|
||||
val parsed = Config.fromYaml(config).get
|
||||
|
||||
parsed.componentGroups match {
|
||||
case Left(f: DecodingFailure) =>
|
||||
Show[DecodingFailure].show(f) should include(
|
||||
"Failed to decode 'Group 1' as module reference"
|
||||
"Failed to decode 'Group 1' as a module reference"
|
||||
)
|
||||
case unexpected =>
|
||||
fail(s"Unexpected result: $unexpected")
|
||||
@ -209,16 +216,16 @@ class ConfigSpec
|
||||
|component-groups:
|
||||
| new:
|
||||
| - Group 1:
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: f
|
||||
| - bar:
|
||||
| shortcut: fgh
|
||||
| - baz:
|
||||
| shortcut: 0
|
||||
| - quux:
|
||||
| shortcut:
|
||||
| - hmmm:
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: f
|
||||
| - bar:
|
||||
| shortcut: fgh
|
||||
| - baz:
|
||||
| shortcut: 0
|
||||
| - quux:
|
||||
| shortcut:
|
||||
| - hmmm
|
||||
|""".stripMargin
|
||||
val parsed = Config.fromYaml(config).get
|
||||
val expectedComponentGroups = ComponentGroups(
|
||||
@ -248,9 +255,9 @@ class ConfigSpec
|
||||
|component-groups:
|
||||
| new:
|
||||
| - Group 1:
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: []
|
||||
| exports:
|
||||
| - foo:
|
||||
| shortcut: []
|
||||
|""".stripMargin
|
||||
val parsed = Config.fromYaml(config).get
|
||||
parsed.componentGroups match {
|
||||
@ -275,7 +282,7 @@ class ConfigSpec
|
||||
parsed.componentGroups match {
|
||||
case Left(f: DecodingFailure) =>
|
||||
Show[DecodingFailure].show(f) should include(
|
||||
"Failed to decode component group name"
|
||||
"Failed to decode component group"
|
||||
)
|
||||
case unexpected =>
|
||||
fail(s"Unexpected result: $unexpected")
|
||||
@ -288,14 +295,14 @@ class ConfigSpec
|
||||
|component-groups:
|
||||
| new:
|
||||
| - Group 1:
|
||||
| exports:
|
||||
| - one: two
|
||||
| exports:
|
||||
| - one: two
|
||||
|""".stripMargin
|
||||
val parsed = Config.fromYaml(config).get
|
||||
parsed.componentGroups match {
|
||||
case Left(f: DecodingFailure) =>
|
||||
Show[DecodingFailure].show(f) should include(
|
||||
"Failed to decode component name"
|
||||
"Failed to decode exported component"
|
||||
)
|
||||
case unexpected =>
|
||||
fail(s"Unexpected result: $unexpected")
|
||||
|
Loading…
Reference in New Issue
Block a user