Link resolved for lib/boards manager.

Closes #1442

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta
2022-09-21 18:41:46 +02:00
committed by Akos Kitta
parent 019b2d5588
commit 0c49709f26
8 changed files with 360 additions and 28 deletions

View File

@@ -3,7 +3,14 @@ import { Searchable } from './searchable';
import { Installable } from './installable';
import { ArduinoComponent } from './arduino-component';
import { nls } from '@theia/core/lib/common/nls';
import { All, Contributed, Partner, Type, Updatable } from '../nls';
import {
All,
Contributed,
Partner,
Type as TypeLabel,
Updatable,
} from '../nls';
import URI from '@theia/core/lib/common/uri';
export type AvailablePorts = Record<string, [Port, Array<Board>]>;
export namespace AvailablePorts {
@@ -151,6 +158,7 @@ export interface BoardSearch extends Searchable.Options {
readonly type?: BoardSearch.Type;
}
export namespace BoardSearch {
export const Default: BoardSearch = { type: 'All' };
export const TypeLiterals = [
'All',
'Updatable',
@@ -161,6 +169,11 @@ export namespace BoardSearch {
'Arduino@Heart',
] as const;
export type Type = typeof TypeLiterals[number];
export namespace Type {
export function is(arg: unknown): arg is Type {
return typeof arg === 'string' && TypeLiterals.includes(arg as Type);
}
}
export const TypeLabels: Record<Type, string> = {
All: All,
Updatable: Updatable,
@@ -177,8 +190,41 @@ export namespace BoardSearch {
keyof Omit<BoardSearch, 'query'>,
string
> = {
type: Type,
type: TypeLabel,
};
export namespace UriParser {
export const authority = 'boardsmanager';
export function parse(uri: URI): BoardSearch | undefined {
if (uri.scheme !== 'http') {
throw new Error(
`Invalid 'scheme'. Expected 'http'. URI was: ${uri.toString()}.`
);
}
if (uri.authority !== authority) {
throw new Error(
`Invalid 'authority'. Expected: '${authority}'. URI was: ${uri.toString()}.`
);
}
const segments = Searchable.UriParser.normalizedSegmentsOf(uri);
if (segments.length !== 1) {
return undefined;
}
let searchOptions: BoardSearch | undefined = undefined;
const [type] = segments;
if (!type) {
searchOptions = BoardSearch.Default;
} else if (BoardSearch.Type.is(type)) {
searchOptions = { type };
}
if (searchOptions) {
return {
...searchOptions,
...Searchable.UriParser.parseQuery(uri),
};
}
return undefined;
}
}
}
export interface Port {

View File

@@ -8,9 +8,10 @@ import {
Partner,
Recommended,
Retired,
Type,
Type as TypeLabel,
Updatable,
} from '../nls';
import URI from '@theia/core/lib/common/uri';
export const LibraryServicePath = '/services/library-service';
export const LibraryService = Symbol('LibraryService');
@@ -55,6 +56,7 @@ export interface LibrarySearch extends Searchable.Options {
readonly topic?: LibrarySearch.Topic;
}
export namespace LibrarySearch {
export const Default: LibrarySearch = { type: 'All', topic: 'All' };
export const TypeLiterals = [
'All',
'Updatable',
@@ -66,6 +68,11 @@ export namespace LibrarySearch {
'Retired',
] as const;
export type Type = typeof TypeLiterals[number];
export namespace Type {
export function is(arg: unknown): arg is Type {
return typeof arg === 'string' && TypeLiterals.includes(arg as Type);
}
}
export const TypeLabels: Record<Type, string> = {
All: All,
Updatable: Updatable,
@@ -90,6 +97,11 @@ export namespace LibrarySearch {
'Uncategorized',
] as const;
export type Topic = typeof TopicLiterals[number];
export namespace Topic {
export function is(arg: unknown): arg is Topic {
return typeof arg === 'string' && TopicLiterals.includes(arg as Topic);
}
}
export const TopicLabels: Record<Topic, string> = {
All: All,
Communication: nls.localize(
@@ -126,8 +138,60 @@ export namespace LibrarySearch {
string
> = {
topic: nls.localize('arduino/librarySearchProperty/topic', 'Topic'),
type: Type,
type: TypeLabel,
};
export namespace UriParser {
export const authority = 'librarymanager';
export function parse(uri: URI): LibrarySearch | undefined {
if (uri.scheme !== 'http') {
throw new Error(
`Invalid 'scheme'. Expected 'http'. URI was: ${uri.toString()}.`
);
}
if (uri.authority !== authority) {
throw new Error(
`Invalid 'authority'. Expected: '${authority}'. URI was: ${uri.toString()}.`
);
}
const segments = Searchable.UriParser.normalizedSegmentsOf(uri);
// Special magic handling for `Signal Input/Output`.
// TODO: IDE2 deserves a better lib/boards URL spec.
// https://github.com/arduino/arduino-ide/issues/1442#issuecomment-1252136377
if (segments.length === 3) {
const [type, topicHead, topicTail] = segments;
const maybeTopic = `${topicHead}/${topicTail}`;
if (
LibrarySearch.Topic.is(maybeTopic) &&
maybeTopic === 'Signal Input/Output' &&
LibrarySearch.Type.is(type)
) {
return {
type,
topic: maybeTopic,
...Searchable.UriParser.parseQuery(uri),
};
}
}
let searchOptions: LibrarySearch | undefined = undefined;
const [type, topic] = segments;
if (!type && !topic) {
searchOptions = LibrarySearch.Default;
} else if (LibrarySearch.Type.is(type)) {
if (!topic) {
searchOptions = { ...LibrarySearch.Default, type };
} else if (LibrarySearch.Topic.is(topic)) {
searchOptions = { type, topic };
}
}
if (searchOptions) {
return {
...searchOptions,
...Searchable.UriParser.parseQuery(uri),
};
}
return undefined;
}
}
}
export namespace LibraryService {

View File

@@ -1,3 +1,5 @@
import URI from '@theia/core/lib/common/uri';
export interface Searchable<T, O extends Searchable.Options> {
search(options: O): Promise<T[]>;
}
@@ -8,4 +10,24 @@ export namespace Searchable {
*/
readonly query?: string;
}
export namespace UriParser {
/**
* Parses the `URI#fragment` into a query term.
*/
export function parseQuery(uri: URI): { query: string } {
return { query: uri.fragment };
}
/**
* Splits the `URI#path#toString` on the `/` POSIX separator into decoded segments. The first, empty segment representing the root is omitted.
* Examples:
* - `/` -> `['']`
* - `/All` -> `['All']`
* - `/All/Device%20Control` -> `['All', 'Device Control']`
* - `/All/Display` -> `['All', 'Display']`
* - `/Updatable/Signal%20Input%2FOutput` -> `['Updatable', 'Signal Input', 'Output']` (**caveat**!)
*/
export function normalizedSegmentsOf(uri: URI): string[] {
return uri.path.toString().split('/').slice(1).map(decodeURIComponent);
}
}
}