text:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:app="http://schemas.android.com/apk/res-auto"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context="ruolan.com.myhearts.ui.fragment.live.NewsFragment">

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:tabIndicatorColor="@android:color/holo_orange_light"
        app:tabSelectedTextColor="@android:color/holo_orange_light"
        app:tabTextColor="@color/colorPrimary"/>

    <ruolan.com.myhearts.widget.ThoughtViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"/>

</LinearLayout>


text:
<!DOCTYPE html>

<meta charset="UTF-8">
<meta http-equiv="refresh" content="0; URL='https://zircleui.github.io/docs/'" />
<title>Zircle-ui</title>

The documentation site was moved to https://zircleui.github.io/docs/

redpajama_set_name: RedPajamaGithub


text:
<?xml version="1.0" encoding="UTF-8"?>

<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
    id="can-translate"
    version="0.0.1">
    <name>Can Translate</name>
    <description>AngularJS Plugin</description>
    <license>MIT</license>
    <keywords>Translate, AngularJS, Localization, i18n l10n, internationalization, autotranslate</keywords>
    <repo>https://github.com/7everen/can-translate.git</repo>
    <issue>https://github.com/7everen/can-translate/issues</issue>

</plugin>


text:
import sys, getopt, errno

from iosupport import openFile
from parser import Parser
from generator import Generator

def main(argv):
    opened = []
    inFile = sys.stdin
    outFile = sys.stdout
    try:
        opts, args = getopt.getopt(argv,"hi:o:", ["help","ifile=","ofile="])
    except getopt.GetoptError:
        print "Bad args. Wait for: --ifile=<name> --ofile=<name>"
        sys.exit(0)
    for opt, arg in opts:
        if opt == '-h':
            print "TODO"
            sys.exit(0)
        elif opt in ("-i", "--ifile"):
            inFile = openFile(arg, "r")
            opened.append(inFile)
        elif opt in ("-o", "--ofile"):
            outFile = openFile(arg, "w")
            opened.append(outFile)
    if inFile == sys.stdin:
        print "please, enter a string"
    parser = Parser()
    gen = Generator()
    suite = parser.do(inFile)
    print "//parsed"
    gen.do(suite, outFile)
    print "//generated"
    for f in opened:
        assert (f)
        f.close()

if __name__ == "__main__":
    main(sys.argv[1:])

# input strings line by line from stdin


text:
package org.apache.tinkerpop.gremlin.structure.io.binary.types;

import org.apache.tinkerpop.gremlin.structure.io.binary.DataType;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryReader;
import org.apache.tinkerpop.gremlin.structure.io.binary.GraphBinaryWriter;
import org.apache.tinkerpop.gremlin.process.traversal.Operator;
import org.apache.tinkerpop.gremlin.process.traversal.Order;
import org.apache.tinkerpop.gremlin.process.traversal.Pop;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Scope;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.structure.Column;
import org.apache.tinkerpop.gremlin.structure.Direction;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.io.Buffer;

import java.io.IOException;
import java.util.function.Function;

/**
 * Generalized serializer for {#code Enum} value types.
 *
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public class EnumSerializer<E extends Enum> extends SimpleTypeSerializer<E> {

    public static final EnumSerializer<SackFunctions.Barrier> BarrierSerializer = new EnumSerializer<>(DataType.BARRIER, SackFunctions.Barrier::valueOf);
    public static final EnumSerializer<VertexProperty.Cardinality> CardinalitySerializer = new EnumSerializer<>(DataType.CARDINALITY, VertexProperty.Cardinality::valueOf);
    public static final EnumSerializer<Column> ColumnSerializer = new EnumSerializer<>(DataType.COLUMN, Column::valueOf);
    public static final EnumSerializer<Direction> DirectionSerializer = new EnumSerializer<>(DataType.DIRECTION, Direction::valueOf);
    public static final EnumSerializer<Operator> OperatorSerializer = new EnumSerializer<>(DataType.OPERATOR, Operator::valueOf);
    public static final EnumSerializer<Order> OrderSerializer = new EnumSerializer<>(DataType.ORDER, Order::valueOf);
    public static final EnumSerializer<TraversalOptionParent.Pick> PickSerializer = new EnumSerializer<>(DataType.PICK, TraversalOptionParent.Pick::valueOf);
    public static final EnumSerializer<Pop> PopSerializer = new EnumSerializer<>(DataType.POP, Pop::valueOf);
    public static final EnumSerializer<Scope> ScopeSerializer = new EnumSerializer<>(DataType.SCOPE, Scope::valueOf);
    public static final EnumSerializer<T> TSerializer = new EnumSerializer<>(DataType.T, T::valueOf);

    private final Function<String, E> readFunc;

    private EnumSerializer(final DataType dataType, final Function<String, E> readFunc) {
        super(dataType);
        this.readFunc = readFunc;
    }

    @Override
    protected E readValue(final Buffer buffer, final GraphBinaryReader context) throws IOException {
        return readFunc.apply(context.read(buffer));
    }

    @Override
    protected void writeValue(final E value, final Buffer buffer, final GraphBinaryWriter context) throws IOException {
        context.write(value.name(), buffer);
    }
}


text:
Every project has its own documentation site. Every Aggregator module summarizes the purpose with some general ideas of the ones to follow. In every Parent module will be reports on dependency management, licence info, plugin management... and a small summary of the child examples. Lastly every example module will have reports on every aspect of its nature and a detailed blog entry providing necessary explanations when needed.

[More info »][more-info]

[more-info]: info.html

## In Action

Description of how to run build the examples.


text:
package org.bremersee.geojson;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import org.bremersee.geojson.utils.GeometryUtils;
import org.locationtech.jts.geom.Geometry;

/**
 * A GeoJSON object with the type {@code Feature} (see
 * rfc7946 section 3.2).
 *
 * @author Christian Bremer
 */
@Schema(description = "A GeoJSON object with type 'Feature'.")
public class GeoJsonFeature extends AbstractGeoJsonFeature<Geometry, Map<String, Object>>
    implements Serializable {

  private static final long serialVersionUID = 2L;

  @JsonIgnore
  private String id;

  @JsonIgnore
  private Geometry geometry;

  /**
   * Default constructor.
   */
  public GeoJsonFeature() {
    super();
  }

  /**
   * Constructs a GeoJSON feature with the specified parameters.
   *
   * @param id an optional id
   * @param geometry the geometry of the GeoJson feature
   * @param calculateBounds if {@code true} the bounding box will be calculated otherwise the bounding box will be
   *     {@code null}
   * @param properties a map with named objects that are associated with the GeoJSON feature
   */
  public GeoJsonFeature(
      final String id,
      final Geometry geometry,
      final boolean calculateBounds,
      final Map<String, Object> properties) {

    if (id != null && id.trim().length() > 0) {
      setId(id);
    }
    setGeometry(geometry);
    if (calculateBounds && geometry != null) {
      setBbox(GeometryUtils.getBoundingBox(geometry));
    }
    if (properties != null) {
      getProperties().putAll(properties);
    }
  }

  @Override
  public String getId() {
    return id;
  }

  @Override
  public void setId(String id) {
    this.id = id;
  }

  @Schema(description = "GeoJSON", implementation = org.bremersee.geojson.model.Geometry.class)
  @JsonSerialize(using = GeometrySerializer.class)
  @Override
  public Geometry getGeometry() {
    return geometry;
  }

  @Schema(description = "GeoJSON", implementation = org.bremersee.geojson.model.Geometry.class)
  @JsonDeserialize(using = GeometryDeserializer.class)
  @Override
  public void setGeometry(final Geometry geometry) {
    this.geometry = geometry;
  }

  @Override
  public Map<String, Object> getProperties() {
    if (super.getProperties() == null) {
      super.setProperties(new LinkedHashMap<>());
    }
    return super.getProperties();
  }

  @Override
  protected boolean equals(final Geometry g1, final Object g2) {
    if (g1 == g2) {
      return true;
    } else if (g1 != null && g2 instanceof Geometry) {
      return GeometryUtils.equals(g1, (Geometry) g2);
    } else {
      return false;
    }
  }

}


text:
// @flow

/**
 * Creates the color tokens based on the color theme and the association map.
 * If a key is not found in the association map it defaults to the current value.
 *
 * @param {Object} colorMap - A map between the token name and the actual color value.
 * @param {Object} colors - An object containing all the theme colors.
 * @returns {Object}
 */
export function createColorTokens(colorMap: Object, colors: Object): Object {
    return Object.entries(colorMap)
        .reduce((result, [ token, value ]) =>
            Object.assign(result, { [token]: colors[value] || value }), {});
}


text:
import * as vscode from 'vscode';
import * as interfaces from './interfaces';
import ContentProvider from './contentProvider';
import { loadMessageBundle } from 'vscode-nls';
const localize = loadMessageBundle();

interface IDocumentMergeConflictNavigationResults {
	canNavigate: boolean;
	conflict?: interfaces.IDocumentMergeConflict;
}

enum NavigationDirection {
	Forwards,
	Backwards
}

export default class CommandHandler implements vscode.Disposable {

	private disposables: vscode.Disposable[] = [];
	private tracker: interfaces.IDocumentMergeConflictTracker;

	constructor(trackerService: interfaces.IDocumentMergeConflictTrackerService) {
		this.tracker = trackerService.createTracker('commands');
	}

	begin() {
		this.disposables.push(
			this.registerTextEditorCommand('merge-conflict.accept.current', this.acceptCurrent),
			this.registerTextEditorCommand('merge-conflict.accept.incoming', this.acceptIncoming),
			this.registerTextEditorCommand('merge-conflict.accept.selection', this.acceptSelection),
			this.registerTextEditorCommand('merge-conflict.accept.both', this.acceptBoth),
			this.registerTextEditorCommand('merge-conflict.accept.all-current', this.acceptAllCurrent, this.acceptAllCurrentResources),
			this.registerTextEditorCommand('merge-conflict.accept.all-incoming', this.acceptAllIncoming, this.acceptAllIncomingResources),
			this.registerTextEditorCommand('merge-conflict.accept.all-both', this.acceptAllBoth),
			this.registerTextEditorCommand('merge-conflict.next', this.navigateNext),
			this.registerTextEditorCommand('merge-conflict.previous', this.navigatePrevious),
			this.registerTextEditorCommand('merge-conflict.compare', this.compare)
		);
	}

	private registerTextEditorCommand(command: string, cb: (editor: vscode.TextEditor, ...args: any[]) => Promise<void>, resourceCB?: (uris: vscode.Uri[]) => Promise<void>) {
		return vscode.commands.registerCommand(command, (...args) => {
			if (resourceCB && args.length && args.every(arg => arg && arg.resourceUri)) {
				return resourceCB.call(this, args.map(arg => arg.resourceUri));
			}
			const editor = vscode.window.activeTextEditor;
			return editor && cb.call(this, editor, ...args);
		});
	}

	acceptCurrent(editor: vscode.TextEditor, ...args: any[]): Promise<void> {
		return this.accept(interfaces.CommitType.Current, editor, ...args);
	}

	acceptIncoming(editor: vscode.TextEditor, ...args: any[]): Promise<void> {
		return this.accept(interfaces.CommitType.Incoming, editor, ...args);
	}

	acceptBoth(editor: vscode.TextEditor, ...args: any[]): Promise<void> {
		return this.accept(interfaces.CommitType.Both, editor, ...args);
	}

	acceptAllCurrent(editor: vscode.TextEditor): Promise<void> {
		return this.acceptAll(interfaces.CommitType.Current, editor);
	}

	acceptAllIncoming(editor: vscode.TextEditor): Promise<void> {
		return this.acceptAll(interfaces.CommitType.Incoming, editor);
	}

	acceptAllCurrentResources(resources: vscode.Uri[]): Promise<void> {
		return this.acceptAllResources(interfaces.CommitType.Current, resources);
	}

	acceptAllIncomingResources(resources: vscode.Uri[]): Promise<void> {
		return this.acceptAllResources(interfaces.CommitType.Incoming, resources);
	}

	acceptAllBoth(editor: vscode.TextEditor): Promise<void> {
		return this.acceptAll(interfaces.CommitType.Both, editor);
	}

	async compare(editor: vscode.TextEditor, conflict: interfaces.IDocumentMergeConflict | null) {

		// No conflict, command executed from command palette
		if (!conflict) {
			conflict = await this.findConflictContainingSelection(editor);

			// Still failed to find conflict, warn the user and exit
			if (!conflict) {
				vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict'));
				return;
			}
		}

		const conflicts = await this.tracker.getConflicts(editor.document);

		// Still failed to find conflict, warn the user and exit
		if (!conflicts) {
			vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict'));
			return;
		}

		const scheme = editor.document.uri.scheme;
		let range = conflict.current.content;
		const leftRanges = conflicts.map(conflict => [conflict.current.content, conflict.range]);
		const rightRanges = conflicts.map(conflict => [conflict.incoming.content, conflict.range]);

		const leftUri = editor.document.uri.with({
			scheme: ContentProvider.scheme,
			query: JSON.stringify({ scheme, range: range, ranges: leftRanges })
		});

		range = conflict.incoming.content;
		const rightUri = leftUri.with({ query: JSON.stringify({ scheme, ranges: rightRanges }) });

		let mergeConflictLineOffsets = 0;
		for (const nextconflict of conflicts) {
			if (nextconflict.range.isEqual(conflict.range)) {
				break;
			} else {
				mergeConflictLineOffsets += (nextconflict.range.end.line - nextconflict.range.start.line) - (nextconflict.incoming.content.end.line - nextconflict.incoming.content.start.line);
			}
		}
		const selection = new vscode.Range(
			conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character,
			conflict.range.start.line - mergeConflictLineOffsets, conflict.range.start.character
		);

		const docPath = editor.document.uri.path;
		const fileName = docPath.substring(docPath.lastIndexOf('/') + 1); // avoid NodeJS path to keep browser webpack small
		const title = localize('compareChangesTitle', '{0}: Current Changes ↔ Incoming Changes', fileName);
		const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict');
		const openToTheSide = mergeConflictConfig.get<string>('diffViewPosition');
		const opts: vscode.TextDocumentShowOptions = {
			viewColumn: openToTheSide === 'Beside' ? vscode.ViewColumn.Beside : vscode.ViewColumn.Active,
			selection
		};

		if (openToTheSide === 'Below') {
			await vscode.commands.executeCommand('workbench.action.newGroupBelow');
		}

		await vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, title, opts);
	}

	navigateNext(editor: vscode.TextEditor): Promise<void> {
		return this.navigate(editor, NavigationDirection.Forwards);
	}

	navigatePrevious(editor: vscode.TextEditor): Promise<void> {
		return this.navigate(editor, NavigationDirection.Backwards);
	}

	async acceptSelection(editor: vscode.TextEditor): Promise<void> {
		const conflict = await this.findConflictContainingSelection(editor);

		if (!conflict) {
			vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict'));
			return;
		}

		let typeToAccept: interfaces.CommitType;
		let tokenAfterCurrentBlock: vscode.Range = conflict.splitter;

		if (conflict.commonAncestors.length > 0) {
			tokenAfterCurrentBlock = conflict.commonAncestors[0].header;
		}

		// Figure out if the cursor is in current or incoming, we do this by seeing if
		// the active position is before or after the range of the splitter or common
		// ancestors marker. We can use this trick as the previous check in
		// findConflictByActiveSelection will ensure it's within the conflict range, so
		// we don't falsely identify "current" or "incoming" if outside of a conflict range.
		if (editor.selection.active.isBefore(tokenAfterCurrentBlock.start)) {
			typeToAccept = interfaces.CommitType.Current;
		}
		else if (editor.selection.active.isAfter(conflict.splitter.end)) {
			typeToAccept = interfaces.CommitType.Incoming;
		}
		else if (editor.selection.active.isBefore(conflict.splitter.start)) {
			vscode.window.showWarningMessage(localize('cursorOnCommonAncestorsRange', 'Editor cursor is within the common ancestors block, please move it to either the "current" or "incoming" block'));
			return;
		}
		else {
			vscode.window.showWarningMessage(localize('cursorOnSplitterRange', 'Editor cursor is within the merge conflict splitter, please move it to either the "current" or "incoming" block'));
			return;
		}

		this.tracker.forget(editor.document);
		conflict.commitEdit(typeToAccept, editor);
	}

	dispose() {
		this.disposables.forEach(disposable => disposable.dispose());
		this.disposables = [];
	}

	private async navigate(editor: vscode.TextEditor, direction: NavigationDirection): Promise<void> {
		const navigationResult = await this.findConflictForNavigation(editor, direction);

		if (!navigationResult) {
			// Check for autoNavigateNextConflict, if it's enabled(which indicating no conflict remain), then do not show warning
			const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict');
			if (mergeConflictConfig.get<boolean>('autoNavigateNextConflict.enabled')) {
				return;
			}
			vscode.window.showWarningMessage(localize('noConflicts', 'No merge conflicts found in this file'));
			return;
		}
		else if (!navigationResult.canNavigate) {
			vscode.window.showWarningMessage(localize('noOtherConflictsInThisFile', 'No other merge conflicts within this file'));
			return;
		}
		else if (!navigationResult.conflict) {
			// TODO: Show error message?
			return;
		}

		// Move the selection to the first line of the conflict
		editor.selection = new vscode.Selection(navigationResult.conflict.range.start, navigationResult.conflict.range.start);
		editor.revealRange(navigationResult.conflict.range, vscode.TextEditorRevealType.Default);
	}

	private async accept(type: interfaces.CommitType, editor: vscode.TextEditor, ...args: any[]): Promise<void> {

		let conflict: interfaces.IDocumentMergeConflict | null;

		// If launched with known context, take the conflict from that
		if (args[0] === 'known-conflict') {
			conflict = args[1];
		}
		else {
			// Attempt to find a conflict that matches the current cursor position
			conflict = await this.findConflictContainingSelection(editor);
		}

		if (!conflict) {
			vscode.window.showWarningMessage(localize('cursorNotInConflict', 'Editor cursor is not within a merge conflict'));
			return;
		}

		// Tracker can forget as we know we are going to do an edit
		this.tracker.forget(editor.document);
		conflict.commitEdit(type, editor);

		// navigate to the next merge conflict
		const mergeConflictConfig = vscode.workspace.getConfiguration('merge-conflict');
		if (mergeConflictConfig.get<boolean>('autoNavigateNextConflict.enabled')) {
			this.navigateNext(editor);
		}

	}

	private async acceptAll(type: interfaces.CommitType, editor: vscode.TextEditor): Promise<void> {
		const conflicts = await this.tracker.getConflicts(editor.document);

		if (!conflicts || conflicts.length === 0) {
			vscode.window.showWarningMessage(localize('noConflicts', 'No merge conflicts found in this file'));
			return;
		}

		// For get the current state of the document, as we know we are doing to do a large edit
		this.tracker.forget(editor.document);

		// Apply all changes as one edit
		await editor.edit((edit) => conflicts.forEach(conflict => {
			conflict.applyEdit(type, editor.document, edit);
		}));
	}

	private async acceptAllResources(type: interfaces.CommitType, resources: vscode.Uri[]): Promise<void> {
		const documents = await Promise.all(resources.map(resource => vscode.workspace.openTextDocument(resource)));
		const edit = new vscode.WorkspaceEdit();
		for (const document of documents) {
			const conflicts = await this.tracker.getConflicts(document);

			if (!conflicts || conflicts.length === 0) {
				continue;
			}

			// For get the current state of the document, as we know we are doing to do a large edit
			this.tracker.forget(document);

			// Apply all changes as one edit
			conflicts.forEach(conflict => {
				conflict.applyEdit(type, document, { replace: (range, newText) => edit.replace(document.uri, range, newText) });
			});
		}
		vscode.workspace.applyEdit(edit);
	}

	private async findConflictContainingSelection(editor: vscode.TextEditor, conflicts?: interfaces.IDocumentMergeConflict[]): Promise<interfaces.IDocumentMergeConflict | null> {

		if (!conflicts) {
			conflicts = await this.tracker.getConflicts(editor.document);
		}

		if (!conflicts || conflicts.length === 0) {
			return null;
		}

		for (const conflict of conflicts) {
			if (conflict.range.contains(editor.selection.active)) {
				return conflict;
			}
		}

		return null;
	}

	private async findConflictForNavigation(editor: vscode.TextEditor, direction: NavigationDirection, conflicts?: interfaces.IDocumentMergeConflict[]): Promise<IDocumentMergeConflictNavigationResults | null> {
		if (!conflicts) {
			conflicts = await this.tracker.getConflicts(editor.document);
		}

		if (!conflicts || conflicts.length === 0) {
			return null;
		}

		const selection = editor.selection.active;
		if (conflicts.length === 1) {
			if (conflicts[0].range.contains(selection)) {
				return {
					canNavigate: false
				};
			}

			return {
				canNavigate: true,
				conflict: conflicts[0]
			};
		}

		let predicate: (_conflict: any) => boolean;
		let fallback: () => interfaces.IDocumentMergeConflict;
		let scanOrder: interfaces.IDocumentMergeConflict[];

		if (direction === NavigationDirection.Forwards) {
			predicate = (conflict) => selection.isBefore(conflict.range.start);
			fallback = () => conflicts![0];
			scanOrder = conflicts;
		} else if (direction === NavigationDirection.Backwards) {
			predicate = (conflict) => selection.isAfter(conflict.range.start);
			fallback = () => conflicts![conflicts!.length - 1];
			scanOrder = conflicts.slice().reverse();
		} else {
			throw new Error(`Unsupported direction ${direction}`);
		}

		for (const conflict of scanOrder) {
			if (predicate(conflict) && !conflict.range.contains(selection)) {
				return {
					canNavigate: true,
					conflict: conflict
				};
			}
		}

		// Went all the way to the end, return the head
		return {
			canNavigate: true,
			conflict: fallback()
		};
	}
}


text:
=======================================================
ASGI (Asynchronous Server Gateway Interface) Draft Spec
=======================================================

.. note::
  This is still in-progress, but is now mostly complete.

Abstract
========

This document proposes a standard interface between network protocol
servers (particularly web servers) and Python applications, intended
to allow handling of multiple common protocol styles (including HTTP, HTTP2,
and WebSocket).

This base specification is intended to fix in place the set of APIs by which
these servers interact and the guarantees and style of message delivery;
each supported protocol (such as HTTP) has a sub-specification that outlines
how to encode and decode that protocol into messages.

The set of sub-specifications is available :ref:`in the Message Formats section <asgi_sub_specifications>`.

Rationale
=========

The WSGI specification has worked well since it was introduced, and
allowed for great flexibility in Python framework and web server choice.
However, its design is irrevocably tied to the HTTP-style
request/response cycle, and more and more protocols are becoming a
standard part of web programming that do not follow this pattern
(most notably, WebSocket).

ASGI attempts to preserve a simple application interface, but provide
an abstraction that allows for data to be sent and received at any time,
and from different application threads or processes.

It also take the principle of turning protocols into Python-compatible,
asynchronous-friendly sets of messages and generalises it into two parts;
a standardised interface for communication and to build servers around (this
document), and a set of standard :ref:`message formats for each protocol <asgi_sub_specifications>`.

Its primary goal is to provide a way to write HTTP/2 and WebSocket code,
alongside normal HTTP handling code, however, and part of this design is
ensuring there is an easy path to use both existing WSGI servers and
applications, as a large majority of Python web usage relies on WSGI and
providing an easy path forwards is critical to adoption. Details on that
interoperability are covered in :doc:`/asgi/www`.

The end result of this process has been a specification for generalised
inter-process communication between Python processes, with a certain set of
guarantees and delivery styles that make it suited to low-latency protocol
processing and response. It is not intended to replace things like traditional
task queues, but it is intended that it could be used for things like
distributed systems communication, or as the backbone of a service-oriented
architecure for inter-service communication.

Overview
========

ASGI consists of three different components - *protocol servers*,
a *channel layer*, and *application code*. Channel layers are the core
part of the implementation, and provide an interface to both protocol
servers and applications.

A channel layer provides a protocol server or an application server
with a ``send`` callable, which takes a channel name and message
``dict``, and a ``receive`` callable, which takes a list of
channel names and returns the next message available on any named channel.

Thus, rather than under WSGI, where you point the protocol server to the
application, under ASGI you point both the protocol server and the application
to a channel layer instance. It is intended that applications and protocol
servers always run in separate processes or threads, and always communicate
via the channel layer.

ASGI tries to be as compatible as possible by default, and so the only
implementation of ``receive`` that must be provided is a fully-synchronous,
nonblocking one. Implementations can then choose to implement a blocking mode
in this method, and if they wish to go further, versions compatible with
the asyncio or Twisted frameworks (or other frameworks that may become
popular, thanks to the extension declaration mechanism).

The distinction between protocol servers and applications in this document
is mostly to distinguish their roles and to make illustrating concepts easier.
There is no code-level distinction between the two, and it's entirely possible
to have a process that does both, or middleware-like code that transforms
messages between two different channel layers or channel names. It is
expected, however, that most deployments will fall into this pattern.

There is even room for a WSGI-like application abstraction on the application
server side, with a callable which takes ``(channel, message, send_func)``,
but this would be slightly too restrictive for many use cases and does not
cover how to specify channel names to listen on. It is expected that
frameworks will cover this use case.

Channels and Messages
---------------------

All communication in an ASGI stack uses *messages* sent over *channels*.
All messages must be a ``dict`` at the top level of the object, and
contain only the following types to ensure serializability:

* Byte strings
* Unicode strings
* Integers (within the signed 64 bit range)
* Floating point numbers (within the IEEE 754 double precision range)
* Lists (tuples should be treated as lists)
* Dicts (keys must be unicode strings)
* Booleans
* None

Channels are identified by a unicode string name consisting only of ASCII
letters, ASCII numerical digits, periods (``.``), dashes (``-``) and
underscores (``_``), plus an optional type character (see below).

Channels are a first-in, first out queue with at-most-once delivery
semantics. They can have multiple writers and multiple readers; only a single
reader should get each written message. Implementations must never deliver
a message more than once or to more than one reader, and must drop messages if
this is necessary to achieve this restriction.

In order to aid with scaling and network architecture, a distinction
is made between channels that have multiple readers (such as the
``http.request`` channel that web applications would listen on from every
application worker process), *single-reader channels* that are read from a
single unknown location (such as ``http.request.body?ABCDEF``), and
*process-specific channels* (such as a ``http.response!ABCDEF`` channel
tied to a client socket).

*Normal channel* names contain no type characters, and can be routed however
the backend wishes; in particular, they do not have to appear globally
consistent, and backends may shard their contents out to different servers
so that a querying client only sees some portion of the messages. Calling
``receive`` on these channels does not guarantee that you will get the
messages in order or that you will get anything if the channel is non-empty.

*Single-reader channel* names contain a question mark
(``?``) character in order to indicate to the channel layer that it must make
these channels appear globally consistent. The ``?`` is always preceded by
the main channel name (e.g. ``http.response.body``) and followed by a
random portion. Channel layers may use the random portion to help pin the
channel to a server, but reads from this channel by a single process must
always be in-order and return messages if the channel is non-empty. These names
must be generated by the ``new_channel`` call.

*Process-specific channel* names contain an exclamation mark
(``!``) character in order to indicate to the channel layer that it may
have to route the data for these channels differently to ensure it reaches the
single process that needs it; these channels are nearly always tied to
incoming connections from the outside world. The ``!`` is always preceded by
the main channel name (e.g. ``http.response``) and followed by the
per-client/random portion - channel layers can split on the ``!`` and use just
the right hand part to route if they desire, or can ignore it if they don't
need to use different routing rules. Even if the right hand side contains
client routing information, it must still contain random parts too so that
each call to ``new_channel`` returns a new, unused name. These names
must be generated by the ``new_channel`` call; they are guaranteed to only
be read from the same process that calls ``new_channel``.

Messages should expire after a set time sitting unread in a channel;
the recommendation is one minute, though the best value depends on the
channel layer and the way it is deployed.

The maximum message size is 1MB if the message were encoded as JSON;
if more data than this needs to be transmitted it must be chunked or placed
onto its own single-reader or process-specific channel (see how HTTP request
bodies are done, for example). All channel layers must support messages up
to this size, but protocol specifications are encouraged to keep well below it.

Handling Protocols
------------------

ASGI messages represent two main things - internal application events
(for example, a channel might be used to queue thumbnails of previously
uploaded videos), and protocol events to/from connected clients.

As such, there are :ref:`sub-specifications <asgi_sub_specifications>` that
outline encodings to and from ASGI messages for common protocols like HTTP and
WebSocket; in particular, the HTTP one covers the WSGI/ASGI interoperability.
It is recommended that if a protocol becomes commonplace, it should gain
standardized formats in a sub-specification of its own.

The message formats are a key part of the specification; without them,
the protocol server and web application might be able to talk to each other,
but may not understand some of what the other is saying. It's equivalent to the
standard keys in the ``environ`` dict for WSGI.

The design pattern is that most protocols will share a few channels for
incoming data (for example, ``http.request``, ``websocket.connect`` and
``websocket.receive``), but will have individual channels for sending to
each client (such as ``http.response!kj2daj23``). This allows incoming
data to be dispatched into a cluster of application servers that can all
handle it, while responses are routed to the individual protocol server
that has the other end of the client's socket.

Some protocols, however, do not have the concept of a unique socket
connection; for example, an SMS gateway protocol server might just have
``sms.receive`` and ``sms.send``, and the protocol server cluster would
take messages from ``sms.send`` and route them into the normal phone
network based on attributes in the message (in this case, a telephone
number).

.. _asgi_extensions:

Extensions
----------

Extensions are functionality that is
not required for basic application code and nearly all protocol server
code, and so has been made optional in order to enable lightweight
channel layers for applications that don't need the full feature set defined
here.

The extensions defined here are:

* ``groups``: Allows grouping of channels to allow broadcast; see below for more.
* ``flush``: Allows easier testing and development with channel layers.
* ``statistics``: Allows channel layers to provide global and per-channel statistics.
* ``twisted``: Async compatibility with the Twisted framework.
* ``asyncio``: Async compatibility with Python 3's asyncio.

There is potential to add further extensions; these may be defined by
a separate specification, or a new version of this specification.

If application code requires an extension, it should check for it as soon
as possible, and hard error if it is not provided. Frameworks should
encourage optional use of extensions, while attempting to move any
extension-not-found errors to process startup rather than message handling.

Groups
------

While the basic channel model is sufficient to handle basic application
needs, many more advanced uses of asynchronous messaging require
notifying many users at once when an event occurs - imagine a live blog,
for example, where every viewer should get a long poll response or
WebSocket packet when a new entry is posted.

This concept could be kept external to the ASGI spec, and would be, if it
were not for the significant performance gains a channel layer implementation
could make on the send-group operation by having it included - the
alternative being a ``send_many`` callable that might have to take
tens of thousands of destination channel names in a single call. However,
the group feature is still optional; its presence is indicated by the
``supports_groups`` attribute on the channel layer object.

Thus, there is a simple Group concept in ASGI, which acts as the
broadcast/multicast mechanism across channels. Channels are added to a group,
and then messages sent to that group are sent to all members of the group.
Channels can be removed from a group manually (e.g. based on a disconnect
event), and the channel layer will garbage collect "old" channels in groups
on a periodic basis.

How this garbage collection happens is not specified here, as it depends on
the internal implementation of the channel layer. The recommended approach,
however, is when a message on a process-specific channel expires, the channel
layer should remove that channel from all groups it's currently a member of;
this is deemed an acceptable indication that the channel's listener is gone.

*Implementation of the group functionality is optional*. If it is not provided
and an application or protocol server requires it, they should hard error
and exit with an appropriate error message. It is expected that protocol
servers will not need to use groups.

Linearization
-------------

The design of ASGI is meant to enable a shared-nothing architecture,
where messages can be handled by any one of a set of threads, processes
or machines running application code.

This, of course, means that several different copies of the application
could be handling messages simultaneously, and those messages could even
be from the same client; in the worst case, two packets from a client
could even be processed out-of-order if one server is slower than another.

This is an existing issue with things like WSGI as well - a user could
open two different tabs to the same site at once and launch simultaneous
requests to different servers - but the nature of the new protocols
specified here mean that collisions are more likely to occur.

Solving this issue is left to frameworks and application code; there are
already solutions such as database transactions that help solve this,
and the vast majority of application code will not need to deal with this
problem. If ordering of incoming packets matters for a protocol, they should
be annotated with a packet number (as WebSocket is in its specification).

Single-reader and process-specific channels, such as those used for response
channels back to clients, are not subject to this problem; a single reader
on these must always receive messages in channel order.

Capacity
--------

To provide backpressure, each channel in a channel layer may have a capacity,
defined however the layer wishes (it is recommended that it is configurable
by the user using keyword arguments to the channel layer constructor, and
furthermore configurable per channel name or name prefix).

When a channel is at or over capacity, trying to send() to that channel
may raise ChannelFull, which indicates to the sender the channel is over
capacity. How the sender wishes to deal with this will depend on context;
for example, a web application trying to send a response body will likely
wait until it empties out again, while a HTTP interface server trying to
send in a request would drop the request and return a 503 error.

Sending to a group never raises ChannelFull; instead, it must silently drop
the message if it is over capacity, as per ASGI's at-most-once delivery
policy.

Specification Details
=====================

A *channel layer* must provide an object with these attributes
(all function arguments are positional):

* ``send(channel, message)``, a callable that takes two arguments: the
  channel to send on, as a unicode string, and the message
  to send, as a serializable ``dict``.

* ``receive(channels, block=False)``, a callable that takes a list of channel
  names as unicode strings, and returns with either ``(None, None)``
  or ``(channel, message)`` if a message is available. If ``block`` is True, then
  it will not return until after a built-in timeout or a message arrives; if
  ``block`` is false, it will always return immediately. It is perfectly
  valid to ignore ``block`` and always return immediately, or after a delay;
  ``block`` means that the call can take as long as it likes before returning
  a message or nothing, not that it must block until it gets one.

* ``new_channel(pattern)``, a callable that takes a unicode string pattern,
  and returns a new valid channel name that does not already exist, by
  adding a unicode string after the ``!`` or ``?`` character in ``pattern``,
  and checking for existence of that name in the channel layer. The ``pattern``
  must end with ``!`` or ``?`` or this function must error. If the character
  is ``!``, making it a process-specific channel, ``new_channel`` must be
  called on the same channel layer that intends to read the channel with
  ``receive``; any other channel layer instance may not receive
  messages on this channel due to client-routing portions of the appended string.

* ``MessageTooLarge``, the exception raised when a send operation fails
  because the encoded message is over the layer's size limit.

* ``ChannelFull``, the exception raised when a send operation fails
  because the destination channel is over capacity.

* ``extensions``, a list of unicode string names indicating which
  extensions this layer provides, or an empty list if it supports none.
  The possible extensions can be seen in :ref:`asgi_extensions`.

A channel layer implementing the ``groups`` extension must also provide:

* ``group_add(group, channel)``, a callable that takes a ``channel`` and adds
  it to the group given by ``group``. Both are unicode strings. If the channel
  is already in the group, the function should return normally.

* ``group_discard(group, channel)``, a callable that removes the ``channel``
  from the ``group`` if it is in it, and does nothing otherwise.

* ``group_channels(group)``, a callable that returns an iterable which yields
  all of the group's member channel names. The return value should be serializable
  with regards to local adds and discards, but best-effort with regards to
  adds and discards on other nodes.

* ``send_group(group, message)``, a callable that takes two positional
  arguments; the group to send to, as a unicode string, and the message
  to send, as a serializable ``dict``. It may raise MessageTooLarge but cannot
  raise ChannelFull.

* ``group_expiry``, an integer number of seconds that specifies how long group
  membership is valid for after the most recent ``group_add`` call (see
  *Persistence* below)

A channel layer implementing the ``statistics`` extension must also provide:

* ``global_statistics()``, a callable that returns statistics across all channels
* ``channel_statistics(channel)``, a callable that returns statistics for specified channel

* in both cases statistics are a dict with zero or more of (unicode string keys):

  * ``messages_count``, the number of messages processed since server start
  * ``messages_count_per_second``, the number of messages processed in the last second
  * ``messages_pending``, the current number of messages waiting
  * ``messages_max_age``, how long the oldest message has been waiting, in seconds
  * ``channel_full_count``, the number of times `ChannelFull` exception has been risen since server start
  * ``channel_full_count_per_second``, the number of times `ChannelFull` exception has been risen in the last second

* Implementation may provide total counts, counts per seconds or both.

A channel layer implementing the ``flush`` extension must also provide:

* ``flush()``, a callable that resets the channel layer to a blank state,
  containing no messages and no groups (if the groups extension is
  implemented). This call must block until the system is cleared and will
  consistently look empty to any client, if the channel layer is distributed.

A channel layer implementing the ``twisted`` extension must also provide:

* ``receive_twisted(channels)``, a function that behaves
  like ``receive`` but that returns a Twisted Deferred that eventually
  returns either ``(channel, message)`` or ``(None, None)``. It is not possible
  to run it in nonblocking mode; use the normal ``receive`` for that. The
  channel layer must be able to deal with this function being called from
  many different places in a codebase simultaneously - likely once from each
  Twisted Protocol instance - and so it is recommended that implementations
  use internal connection pooling and call merging or similar.

A channel layer implementing the ``asyncio`` extension must also provide:

* ``receive_asyncio(channels)``, a function that behaves
  like ``receive`` but that fulfills the asyncio coroutine contract to
  block until either a result is available or an internal timeout is reached
  and ``(None, None)`` is returned. The channel layer must be able to deal
  with this function being called from many different places in a codebase
  simultaneously, and so it is recommended that implementations
  use internal connection pooling and call merging or similar.

Channel Semantics
-----------------

Channels **must**:

* Preserve ordering of messages perfectly with only a single reader
  and writer if the channel is a *single-reader* or *process-specific* channel.

* Never deliver a message more than once.

* Never block on message send (though they may raise ChannelFull or
  MessageTooLarge)

* Be able to handle messages of at least 1MB in size when encoded as
  JSON (the implementation may use better encoding or compression, as long
  as it meets the equivalent size)

* Have a maximum name length of at least 100 bytes.

They should attempt to preserve ordering in all cases as much as possible,
but perfect global ordering is obviously not possible in the distributed case.

They are not expected to deliver all messages, but a success rate of at least
99.99% is expected under normal circumstances. Implementations may want to
have a "resilience testing" mode where they deliberately drop more messages
than usual so developers can test their code's handling of these scenarios.

Persistence
-----------

Channel layers do not need to persist data long-term; group
memberships only need to live as long as a connection does, and messages
only as long as the message expiry time, which is usually a couple of minutes.

That said, if a channel server goes down momentarily and loses all data,
persistent socket connections will continue to transfer incoming data and
send out new generated data, but will have lost all of their group memberships
and in-flight messages.

In order to avoid a nasty set of bugs caused by these half-deleted sockets,
protocol servers should quit and hard restart if they detect that the channel
layer has gone down or lost data; shedding all existing connections and letting
clients reconnect will immediately resolve the problem.

If a channel layer implements the ``groups`` extension, it must persist group
membership until at least the time when the member channel has a message
expire due to non-consumption, after which it may drop membership at any time.
If a channel subsequently has a successful delivery, the channel layer must
then not drop group membership until another message expires on that channel.

Channel layers must also drop group membership after a configurable long timeout
after the most recent ``group_add`` call for that membership, the default being
86,400 seconds (one day). The value of this timeout is exposed as the
``group_expiry`` property on the channel layer.

Protocol servers must have a configurable timeout value for every connection-based
protocol they serve that closes the connection after the timeout, and should
default this value to the value of ``group_expiry``, if the channel
layer provides it. This allows old group memberships to be cleaned up safely,
knowing that after the group expiry the original connection must have closed,
or is about to be in the next few seconds.

It's recommended that end developers put the timeout setting much lower - on
the order of hours or minutes - to enable better protocol design and testing.
Even with ASGI's separation of protocol server restart from business logic
restart, you will likely need to move and reprovision protocol servers, and
making sure your code can cope with this is important.

.. _asgi_sub_specifications:

Message Formats
---------------

These describe the standardized message formats for the protocols this
specification supports. All messages are ``dicts`` at the top level,
and all keys are required unless explicitly marked as optional. If a key is
marked optional, a default value is specified, which is to be assumed if
the key is missing. Keys are unicode strings.

The one common key across all protocols is ``reply_channel``, a way to indicate
the client-specific channel to send responses to. Protocols are generally
encouraged to have one message type and one reply channel type to ensure ordering.

A ``reply_channel`` should be unique per connection. If the protocol in question
can have any server service a response - e.g. a theoretical SMS protocol - it
should not have ``reply_channel`` attributes on messages, but instead a separate
top-level outgoing channel.

Messages are specified here along with the channel names they are expected
on; if a channel name can vary, such as with reply channels, the varying
portion will be represented by ``!``, such as ``http.response!``, which matches
the format the ``new_channel`` callable takes.

There is no label on message types to say what they are; their type is implicit
in the channel name they are received on. Two types that are sent on the same
channel, such as HTTP responses and response chunks, are distinguished apart
by their required fields.

Message formats can be found in the sub-specifications:

.. toctree::
   :maxdepth: 1

   /asgi/www
   /asgi/delay
   /asgi/udp

Protocol Format Guidelines
--------------------------

Message formats for protocols should follow these rules, unless
a very good performance or implementation reason is present:

* ``reply_channel`` should be unique per logical connection, and not per
  logical client.

* If the protocol has server-side state, entirely encapsulate that state in
  the protocol server; do not require the message consumers to use an external
  state store.

* If the protocol has low-level negotiation, keepalive or other features,
  handle these within the protocol server and don't expose them in ASGI
  messages.

* If the protocol has guaranteed ordering and does not use a specific channel
  for a given connection (as HTTP does for body data), ASGI messages should
  include an ``order`` field (0-indexed) that preserves the ordering as
  received by the protocol server (or as sent by the client, if available).
  This ordering should span all message types emitted by the client - for
  example, a connect message might have order ``0``, and the first two frames
  order ``1`` and ``2``.

* If the protocol is datagram-based, one datagram should equal one ASGI message
  (unless size is an issue)

Approximate Global Ordering
---------------------------

While maintaining true global (across-channels) ordering of messages is
entirely unreasonable to expect of many implementations, they should strive
to prevent busy channels from overpowering quiet channels.

For example, imagine two channels, ``busy``, which spikes to 1000 messages a
second, and ``quiet``, which gets one message a second. There's a single
consumer running ``receive(['busy', 'quiet'])`` which can handle
around 200 messages a second.

In a simplistic for-loop implementation, the channel layer might always check
``busy`` first; it always has messages available, and so the consumer never
even gets to see a message from ``quiet``, even if it was sent with the
first batch of ``busy`` messages.

A simple way to solve this is to randomize the order of the channel list when
looking for messages inside the channel layer; other, better methods are also
available, but whatever is chosen, it should try to avoid a scenario where
a message doesn't get received purely because another channel is busy.

Strings and Unicode
-------------------

In this document, and all sub-specifications, *byte string* refers to
``str`` on Python 2 and ``bytes`` on Python 3. If this type still supports
Unicode codepoints due to the underlying implementation, then any values
should be kept within the 0 - 255 range.

*Unicode string* refers to ``unicode`` on Python 2 and ``str`` on Python 3.
This document will never specify just *string* - all strings are one of the
two exact types.

Some serializers, such as ``json``, cannot differentiate between byte
strings and unicode strings; these should include logic to box one type as
the other (for example, encoding byte strings as base64 unicode strings with
a preceding special character, e.g. U+FFFF).

Channel and group names are always unicode strings, with the additional
limitation that they only use the following characters:

* ASCII letters
* The digits ``0`` through ``9``
* Hyphen ``-``
* Underscore ``_``
* Period ``.``
* Question mark ``?`` (only to delineiate single-reader channel names,
  and only one per name)
* Exclamation mark ``!`` (only to delineate process-specific channel names,
  and only one per name)

Common Questions
================

1. Why are messages ``dicts``, rather than a more advanced type?

   We want messages to be very portable, especially across process and
   machine boundaries, and so a simple encodable type seemed the best way.
   We expect frameworks to wrap each protocol-specific set of messages in
   custom classes (e.g. ``http.request`` messages become ``Request`` objects)

Copyright
=========

This document has been placed in the public domain.


text:
local Global = require "lua/global"

local Log = {}
Log.__index = Log

--	Log.new() - creates a new Log object, initializing its members with default
--	data; returns the Log object
--	dontDelete is an optional arg that should only be used for Global.logFilename
function Log.new(filename, dontDelete)
	local l = {}
	setmetatable(l, Log)

	--	logging only works when debugging is activated
	if Global.debug then
		--	Use append mode because log_printf in C appends to the same file
		--	Lua docs seem to say that "a" erases the file, but doesn't work for me.
		if not dontDelete then
			os.remove(filename)
		end
		l.file = io.open(filename, "a+")
	end

	return l
end

--	Log:write() - converts all arguments to strings, concatenates them, and
--	writes them to the attached log file; does not return anything.
function Log:write(...)
	--	logging only works when debugging is activated
	if not self.file then
		return nil
	end
	local text = ""
	for i = 1, select('#', ...) do
		text = text .. tostring(select(i, ...))
	end

	self.file:write(os.date() .. ": " .. text .. "\n")
	self.file:flush()
end

--	Log:terminate() - terminates the resources allocated by the creation of
--	the Log object; does not return anything
function Log:terminate()
	if not self.file then
		return nil
	end

	self.file:close()
	self.file = nil
end

--	initialize logging
Log = Log.new(Global.logFilename, true)
Log:write("Started logging.")

--	Can call .new() on this Log instance to create further Logs
return Log


text:
function  _ros1_defaults {
    export OS_CODE_NAME=${OS_CODE_NAME:-$1}
    export ROS1_DISTRO=${ROS1_DISTRO:-$ROS_DISTRO}
    export BUILDER=${BUILDER:-catkin_tools}
    export ROS_VERSION=1
    export ROS_VERSION_EOL=false
    export ROS_VERSION_FINAL="final"
    export ROS_PYTHON_VERSION=${ROS_PYTHON_VERSION:-2}
}

function  _ros2_defaults {
    export OS_CODE_NAME=${OS_CODE_NAME:-$1}
    export ROS2_DISTRO=${ROS2_DISTRO:-$ROS_DISTRO}
    export BUILDER=${BUILDER:-colcon}
    export ROS_VERSION=2
    export ROS_VERSION_EOL=false
    export ROS_VERSION_FINAL="final"
    export ROS_PYTHON_VERSION=3
}

function _set_ros_defaults {
    case "$ROS_DISTRO" in
    "indigo"|"jade")
        _ros1_defaults "trusty"
        export ROS_VERSION_EOL=true
        export _ROS_KEYRING=/etc/apt/trusted.gpg.d/ros-archive-keyring.gpg # signed-by is not supported
        ;;
    "kinetic")
        _ros1_defaults "xenial"
        export ROS_VERSION_EOL=true
        ;;
    "lunar")
        _ros1_defaults "xenial"
        export ROS_VERSION_EOL=true
        ;;
    "melodic")
        _ros1_defaults "bionic"
        ;;
    "noetic")
        _ros1_defaults "focal"
        export ROS_PYTHON_VERSION=3
        ;;
    "ardent")
        _ros2_defaults "xenial"
        export ROS_VERSION_EOL=true
        ;;
    "bouncy"|"crystal")
        _ros2_defaults "bionic"
        export ROS_VERSION_EOL=true
        ;;
    "dashing")
        _ros2_defaults "bionic"
        export ROS_VERSION_EOL=true
        ;;
    "eloquent")
        _ros2_defaults "bionic"
        export ROS_VERSION_EOL=true
        ;;
    "foxy")
        _ros2_defaults "focal"
        ;;
    "galactic")
        _ros2_defaults "focal"
        ;;
    "humble")
        _ros2_defaults "jammy"
        ;;
    "rolling")
        _ros2_defaults "jammy"
        ;;
    "false")
        unset ROS_DISTRO
        ;;
    *)
        ici_error "ROS_DISTRO '$ROS_DISTRO' is not supported"
        ;;
    esac

    if [ "$ROS_PYTHON_VERSION" = 2 ]; then
        export PYTHON_VERSION_NAME=python
    elif [ "$ROS_PYTHON_VERSION" = 3 ]; then
        export PYTHON_VERSION_NAME=python3
    fi

}

function _use_snapshot() {
    export ROS_REPOSITORY_PATH="http://snapshots.ros.org/${ROS_DISTRO?ROS_DISTRO needs to be set}/$1/ubuntu"
    export ROS_REPOSITORY_KEY="$ICI_SRC_PATH/keys/snapshots.asc"
}

function _use_repo_or_final_snapshot() {
    if [ "$ROS_VERSION_EOL" = true ]; then
        if [ "$ROS_REPO" != "testing" ]; then
            ici_warn "'$ROS_DISTRO' is in end-of-life state, ROS_REPO='$ROS_REPO' is superfluous"
        fi
        if [ -n "$ROS_VERSION_FINAL" ]; then
            _use_snapshot "$ROS_VERSION_FINAL"
            return
        fi
    fi
    export ROS_REPOSITORY_PATH="$1"
    if [ "${ROS_REPO}" = "ros-shadow-fixed" ]; then
        ici_warn "ROS_REPO='ros-shadow-fixed' was renamed to ROS_REPO='testing'"
    fi
}

function _get_prefix() {
    if [ "$ROS_VERSION" -eq 2 ]; then
        echo ros2
    else
        echo ros
    fi
}

function ici_set_ros_repository_path {
    local current_repository_path=$1; shift
    if [ -z "${ROS_REPOSITORY_PATH}" ]; then
        case "$ROS_REPO" in
        "building")
            _use_repo_or_final_snapshot "http://repositories.ros.org/ubuntu/building/"
            ;;
        "main")
            _use_repo_or_final_snapshot "http://packages.ros.org/$(_get_prefix)/ubuntu"
            ;;
        "ros")
            if [ "$ROS_VERSION" -eq 2 ]; then
                ici_warn "ROS_REPO=ros would select the ROS1 repository, please use ROS_REPO=main"
            fi
            _use_repo_or_final_snapshot "http://packages.ros.org/$(_get_prefix)/ubuntu"
            ;;
        "ros1")
            _use_repo_or_final_snapshot "http://packages.ros.org/ros/ubuntu"
            ;;
        "ros2")
            _use_repo_or_final_snapshot "http://packages.ros.org/ros2/ubuntu"
            ;;
        "")
            if [ -n "$current_repository_path" ]; then
                export ROS_REPOSITORY_PATH=$current_repository_path
                return
            fi
            ici_warn "Using default ROS_REPO=testing"
            export ROS_REPO=testing
            ;&
        "testing")
            _use_repo_or_final_snapshot "http://packages.ros.org/$(_get_prefix)-testing/ubuntu"
            ;;
        "ros-shadow-fixed"|"ros-testing")
            if [ "$ROS_VERSION" -eq 2 ]; then
                ici_warn "ROS_REPO=$ROS_REPO would select the ROS1 repository, please use ROS_REPO=testing"
            fi
            _use_repo_or_final_snapshot "http://packages.ros.org/$(_get_prefix)-testing/ubuntu"
            ;;
        "ros1-testing")
            _use_repo_or_final_snapshot "http://packages.ros.org/ros-testing/ubuntu"
            ;;
        "ros2-testing")
            _use_repo_or_final_snapshot "http://packages.ros.org/ros2-testing/ubuntu"
            ;;
        "final"|????-??-??)
            _use_snapshot "${ROS_REPO}"
            ;;
        "false")
                export ROS_REPOSITORY_PATH=$current_repository_path
            ;;
        *)
            ici_error "ROS repo '$ROS_REPO' is not supported"
            ;;
        esac
    fi
}

function ici_configure_ros() {
    if [ -n "${ROS_DISTRO}" ]; then
        _set_ros_defaults
    fi
}


text:
module Options.Applicative.Builder.Extra
  (boolFlags
  ,boolFlagsNoDefault
  ,maybeBoolFlags
  ,enableDisableFlags
  ,enableDisableFlagsNoDefault
  ,extraHelpOption
  ,execExtraHelp)
  where

import Control.Monad (when)
import Options.Applicative
import System.Environment (withArgs)
import System.FilePath (takeBaseName)

-- | Enable/disable flags for a @Bool@.
boolFlags :: Bool -> String -> String -> Mod FlagFields Bool -> Parser Bool
boolFlags defaultValue = enableDisableFlags defaultValue True False

-- | Enable/disable flags for a @Bool@, without a default case (to allow chaining @<|>@s).
boolFlagsNoDefault :: String -> String -> Mod FlagFields Bool -> Parser Bool
boolFlagsNoDefault = enableDisableFlagsNoDefault True False

-- | Enable/disable flags for a @(Maybe Bool)@.
maybeBoolFlags :: String -> String -> Mod FlagFields (Maybe Bool) -> Parser (Maybe Bool)
maybeBoolFlags = enableDisableFlags Nothing (Just True) (Just False)

-- | Enable/disable flags for any type.
enableDisableFlags :: a -> a -> a -> String -> String -> Mod FlagFields a -> Parser a
enableDisableFlags defaultValue enabledValue disabledValue name helpSuffix mods =
  enableDisableFlagsNoDefault enabledValue disabledValue name helpSuffix mods <|>
  pure defaultValue

-- | Enable/disable flags for any type, without a default (to allow chaining @<|>@s)
enableDisableFlagsNoDefault :: a -> a -> String -> String -> Mod FlagFields a -> Parser a
enableDisableFlagsNoDefault enabledValue disabledValue name helpSuffix mods =
  last <$> some (enableDisableFlagsNoDefault' enabledValue disabledValue name helpSuffix mods)

enableDisableFlagsNoDefault' :: a -> a -> String -> String -> Mod FlagFields a -> Parser a
enableDisableFlagsNoDefault' enabledValue disabledValue name helpSuffix mods =
  flag' enabledValue
        (long name <>
         help ("Enable " ++ helpSuffix) <>
         mods) <|>
  flag' enabledValue
        (internal <>
         long ("enable-" ++ name) <>
         help ("Enable " ++ helpSuffix) <>
         mods) <|>
  flag' disabledValue
        (long ("no-" ++ name) <>
         help ("Disable " ++ helpSuffix) <>
         mods) <|>
  flag' disabledValue
        (internal <>
         long ("disable-" ++ name) <>
         help ("Disable " ++ helpSuffix) <>
         mods)

-- | Show an extra help option (e.g. @--docker-help@ shows help for all @--docker*@ args).
-- To actually show have that help appear, use 'execExtraHelp' before executing the main parser.
extraHelpOption :: String -> String -> String -> Parser (a -> a)
extraHelpOption progName fakeName helpName =
    infoOption (optDesc ++ ".") (long helpName <> hidden <> internal) <*>
    infoOption (optDesc ++ ".") (long fakeName <> help optDesc)
  where optDesc = concat ["Run '", takeBaseName progName, " --", helpName, "' for details"]

-- | Display extra help if extea help option passed in arguments.
-- Since optparse-applicative doesn't allow an arbirary IO action for an 'abortOption', this
-- was the best way I found that doesn't require manually formatting the help.
execExtraHelp :: [String] -> String -> Parser a -> String -> IO ()
execExtraHelp args helpOpt parser pd = do
    when (args == ["--" ++ helpOpt]) $
      withArgs ["--help"] $ do
        _ <- execParser (info (hiddenHelper <*>
                               ((,) <$>
                                parser <*>
                                some (strArgument (metavar "OTHER ARGUMENTS"))))
                        (fullDesc <> progDesc pd))
        return ()
  where hiddenHelper = abortOption ShowHelpText (long "help" <> hidden <> internal)