Renamed SharpConfig to FileConfig.
This commit is contained in:
parent
a04cc1b63d
commit
e40e393b0c
4 changed files with 160 additions and 142 deletions
136
src/FileConfig.php
Normal file
136
src/FileConfig.php
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
<?php
|
||||||
|
// FileConfig.php
|
||||||
|
// Created: 2023-10-20
|
||||||
|
// Updated: 2024-06-03
|
||||||
|
|
||||||
|
namespace Syokuhou;
|
||||||
|
|
||||||
|
use InvalidArgumentException;
|
||||||
|
use Index\IO\FileStream;
|
||||||
|
use Index\IO\Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a configuration in Sharp Chat format.
|
||||||
|
*/
|
||||||
|
class FileConfig implements IConfig {
|
||||||
|
use ImmutableConfigTrait, GetValueInfoTrait, GetValuesTrait;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, FileConfigValueInfo> $values
|
||||||
|
*/
|
||||||
|
public function __construct(private array $values) {}
|
||||||
|
|
||||||
|
public function getSeparator(): string {
|
||||||
|
return ':';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function scopeTo(string ...$prefix): IConfig {
|
||||||
|
return new ScopedConfig($this, $prefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasValues(string|array $names): bool {
|
||||||
|
if(is_string($names))
|
||||||
|
return array_key_exists($names, $this->values);
|
||||||
|
|
||||||
|
foreach($names as $name)
|
||||||
|
if(!array_key_exists($name, $this->values))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllValueInfos(int $range = 0, int $offset = 0): array {
|
||||||
|
if($range === 0)
|
||||||
|
return array_values($this->values);
|
||||||
|
|
||||||
|
if($range < 0)
|
||||||
|
throw new InvalidArgumentException('$range must be a positive integer.');
|
||||||
|
if($offset < 0)
|
||||||
|
throw new InvalidArgumentException('$offset must be greater than zero if a range is specified.');
|
||||||
|
|
||||||
|
return array_slice($this->values, $offset, $range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValueInfos(string|array $names): array {
|
||||||
|
if(is_string($names))
|
||||||
|
return array_key_exists($names, $this->values) ? [$this->values[$names]] : [];
|
||||||
|
|
||||||
|
$infos = [];
|
||||||
|
|
||||||
|
foreach($names as $name)
|
||||||
|
if(array_key_exists($name, $this->values))
|
||||||
|
$infos[] = $this->values[$name];
|
||||||
|
|
||||||
|
return $infos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of FileConfig from an array of lines.
|
||||||
|
*
|
||||||
|
* @param string[] $lines Config lines.
|
||||||
|
* @return FileConfig
|
||||||
|
*/
|
||||||
|
public static function fromLines(array $lines): self {
|
||||||
|
$values = [];
|
||||||
|
|
||||||
|
foreach($lines as $line) {
|
||||||
|
$line = trim($line);
|
||||||
|
if($line === '' || $line[0] === '#' || $line[0] === ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$info = new FileConfigValueInfo(...explode(' ', $line, 2));
|
||||||
|
$values[$info->getName()] = $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileConfig($values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of FileConfig from a string.
|
||||||
|
*
|
||||||
|
* @param string $lines Config lines.
|
||||||
|
* @param non-empty-string $newLine Line separator character.
|
||||||
|
* @return FileConfig
|
||||||
|
*/
|
||||||
|
public static function fromString(string $lines, string $newLine = "\n"): self {
|
||||||
|
return self::fromLines(explode($newLine, $lines));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of FileConfig from a file.
|
||||||
|
*
|
||||||
|
* @param string $path Config file path.
|
||||||
|
* @throws InvalidArgumentException If $path does not exist.
|
||||||
|
* @return FileConfig
|
||||||
|
*/
|
||||||
|
public static function fromFile(string $path): self {
|
||||||
|
if(!is_file($path))
|
||||||
|
throw new InvalidArgumentException('$path does not exist.');
|
||||||
|
|
||||||
|
return self::fromStream(FileStream::openRead($path));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an instance of FileConfig from a readable stream.
|
||||||
|
*
|
||||||
|
* @param Stream $stream Config file stream.
|
||||||
|
* @throws InvalidArgumentException If $stream is not readable.
|
||||||
|
* @return FileConfig
|
||||||
|
*/
|
||||||
|
public static function fromStream(Stream $stream): self {
|
||||||
|
if(!$stream->canRead())
|
||||||
|
throw new InvalidArgumentException('$stream must be readable.');
|
||||||
|
|
||||||
|
$values = [];
|
||||||
|
while(($line = $stream->readLine()) !== null) {
|
||||||
|
$line = trim($line);
|
||||||
|
if($line === '' || $line[0] === '#' || $line[0] === ';')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
$info = new FileConfigValueInfo(...explode(' ', $line, 2));
|
||||||
|
$values[$info->getName()] = $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new FileConfig($values);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,11 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
// SharpConfigValueInfo.php
|
// FileConfigValueInfo.php
|
||||||
// Created: 2023-10-20
|
// Created: 2023-10-20
|
||||||
// Updated: 2023-10-20
|
// Updated: 2024-06-03
|
||||||
|
|
||||||
namespace Syokuhou;
|
namespace Syokuhou;
|
||||||
|
|
||||||
class SharpConfigValueInfo implements IConfigValueInfo {
|
class FileConfigValueInfo implements IConfigValueInfo {
|
||||||
private string $name;
|
private string $name;
|
||||||
private string $value;
|
private string $value;
|
||||||
|
|
|
@ -1,136 +1,11 @@
|
||||||
<?php
|
<?php
|
||||||
// SharpConfig.php
|
// SharpConfig.php
|
||||||
// Created: 2023-10-20
|
// Created: 2024-06-03
|
||||||
// Updated: 2023-10-20
|
// Updated: 2024-06-03
|
||||||
|
|
||||||
namespace Syokuhou;
|
namespace Syokuhou;
|
||||||
|
|
||||||
use InvalidArgumentException;
|
|
||||||
use Index\IO\FileStream;
|
|
||||||
use Index\IO\Stream;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a configuration in SharpChat format.
|
* Provides an alias for FileConfig.
|
||||||
*/
|
*/
|
||||||
class SharpConfig implements IConfig {
|
class SharpConfig extends FileConfig {}
|
||||||
use ImmutableConfigTrait, GetValueInfoTrait, GetValuesTrait;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array<string, SharpConfigValueInfo> $values
|
|
||||||
*/
|
|
||||||
public function __construct(private array $values) {}
|
|
||||||
|
|
||||||
public function getSeparator(): string {
|
|
||||||
return ':';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function scopeTo(string ...$prefix): IConfig {
|
|
||||||
return new ScopedConfig($this, $prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hasValues(string|array $names): bool {
|
|
||||||
if(is_string($names))
|
|
||||||
return array_key_exists($names, $this->values);
|
|
||||||
|
|
||||||
foreach($names as $name)
|
|
||||||
if(!array_key_exists($name, $this->values))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAllValueInfos(int $range = 0, int $offset = 0): array {
|
|
||||||
if($range === 0)
|
|
||||||
return array_values($this->values);
|
|
||||||
|
|
||||||
if($range < 0)
|
|
||||||
throw new InvalidArgumentException('$range must be a positive integer.');
|
|
||||||
if($offset < 0)
|
|
||||||
throw new InvalidArgumentException('$offset must be greater than zero if a range is specified.');
|
|
||||||
|
|
||||||
return array_slice($this->values, $offset, $range);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getValueInfos(string|array $names): array {
|
|
||||||
if(is_string($names))
|
|
||||||
return array_key_exists($names, $this->values) ? [$this->values[$names]] : [];
|
|
||||||
|
|
||||||
$infos = [];
|
|
||||||
|
|
||||||
foreach($names as $name)
|
|
||||||
if(array_key_exists($name, $this->values))
|
|
||||||
$infos[] = $this->values[$name];
|
|
||||||
|
|
||||||
return $infos;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of SharpConfig from an array of lines.
|
|
||||||
*
|
|
||||||
* @param string[] $lines Config lines.
|
|
||||||
* @return SharpConfig
|
|
||||||
*/
|
|
||||||
public static function fromLines(array $lines): self {
|
|
||||||
$values = [];
|
|
||||||
|
|
||||||
foreach($lines as $line) {
|
|
||||||
$line = trim($line);
|
|
||||||
if($line === '' || $line[0] === '#' || $line[0] === ';')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$info = new SharpConfigValueInfo(...explode(' ', $line, 2));
|
|
||||||
$values[$info->getName()] = $info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SharpConfig($values);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of SharpConfig from a string.
|
|
||||||
*
|
|
||||||
* @param string $lines Config lines.
|
|
||||||
* @param non-empty-string $newLine Line separator character.
|
|
||||||
* @return SharpConfig
|
|
||||||
*/
|
|
||||||
public static function fromString(string $lines, string $newLine = "\n"): self {
|
|
||||||
return self::fromLines(explode($newLine, $lines));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of SharpConfig from a file.
|
|
||||||
*
|
|
||||||
* @param string $path Config file path.
|
|
||||||
* @throws InvalidArgumentException If $path does not exist.
|
|
||||||
* @return SharpConfig
|
|
||||||
*/
|
|
||||||
public static function fromFile(string $path): self {
|
|
||||||
if(!is_file($path))
|
|
||||||
throw new InvalidArgumentException('$path does not exist.');
|
|
||||||
|
|
||||||
return self::fromStream(FileStream::openRead($path));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance of SharpConfig from a readable stream.
|
|
||||||
*
|
|
||||||
* @param Stream $stream Config file stream.
|
|
||||||
* @throws InvalidArgumentException If $stream is not readable.
|
|
||||||
* @return SharpConfig
|
|
||||||
*/
|
|
||||||
public static function fromStream(Stream $stream): self {
|
|
||||||
if(!$stream->canRead())
|
|
||||||
throw new InvalidArgumentException('$stream must be readable.');
|
|
||||||
|
|
||||||
$values = [];
|
|
||||||
while(($line = $stream->readLine()) !== null) {
|
|
||||||
$line = trim($line);
|
|
||||||
if($line === '' || $line[0] === '#' || $line[0] === ';')
|
|
||||||
continue;
|
|
||||||
|
|
||||||
$info = new SharpConfigValueInfo(...explode(' ', $line, 2));
|
|
||||||
$values[$info->getName()] = $info;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new SharpConfig($values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,53 +1,60 @@
|
||||||
<?php
|
<?php
|
||||||
// SharpConfigTest.php
|
// FileConfigTest.php
|
||||||
// Created: 2023-10-20
|
// Created: 2023-10-20
|
||||||
// Updated: 2023-10-20
|
// Updated: 2024-06-03
|
||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \Syokuhou\SharpConfig
|
* @covers \Syokuhou\FileConfig
|
||||||
* @covers \Syokuhou\SharpConfigValueInfo
|
* @covers \Syokuhou\FileConfigValueInfo
|
||||||
* @covers \Syokuhou\ImmutableConfigTrait
|
* @covers \Syokuhou\ImmutableConfigTrait
|
||||||
* @covers \Syokuhou\GetValueInfoTrait
|
* @covers \Syokuhou\GetValueInfoTrait
|
||||||
* @covers \Syokuhou\GetValuesTrait
|
* @covers \Syokuhou\GetValuesTrait
|
||||||
|
* @covers \Syokuhou\SharpConfig
|
||||||
*/
|
*/
|
||||||
final class SharpConfigTest extends TestCase {
|
final class FileConfigTest extends TestCase {
|
||||||
public function testImmutableRemove(): void {
|
public function testImmutableRemove(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
\Syokuhou\FileConfig::fromString('test value')->removeValues('test');
|
||||||
\Syokuhou\SharpConfig::fromString('test value')->removeValues('test');
|
\Syokuhou\SharpConfig::fromString('test value')->removeValues('test');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImmutableSetString(): void {
|
public function testImmutableSetString(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
\Syokuhou\FileConfig::fromString('test value')->setString('test', 'the');
|
||||||
\Syokuhou\SharpConfig::fromString('test value')->setString('test', 'the');
|
\Syokuhou\SharpConfig::fromString('test value')->setString('test', 'the');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImmutableSetInteger(): void {
|
public function testImmutableSetInteger(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
\Syokuhou\FileConfig::fromString('test 1234')->setInteger('test', 5678);
|
||||||
\Syokuhou\SharpConfig::fromString('test 1234')->setInteger('test', 5678);
|
\Syokuhou\SharpConfig::fromString('test 1234')->setInteger('test', 5678);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImmutableSetFloat(): void {
|
public function testImmutableSetFloat(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
\Syokuhou\FileConfig::fromString('test 56.78')->setFloat('test', 12.34);
|
||||||
\Syokuhou\SharpConfig::fromString('test 56.78')->setFloat('test', 12.34);
|
\Syokuhou\SharpConfig::fromString('test 56.78')->setFloat('test', 12.34);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImmutableSetBoolean(): void {
|
public function testImmutableSetBoolean(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
\Syokuhou\FileConfig::fromString('test true')->setBoolean('test', false);
|
||||||
\Syokuhou\SharpConfig::fromString('test true')->setBoolean('test', false);
|
\Syokuhou\SharpConfig::fromString('test true')->setBoolean('test', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImmutableSetArray(): void {
|
public function testImmutableSetArray(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
|
\Syokuhou\FileConfig::fromString('test words words words')->setArray('test', ['meow', 'meow', 'meow']);
|
||||||
\Syokuhou\SharpConfig::fromString('test words words words')->setArray('test', ['meow', 'meow', 'meow']);
|
\Syokuhou\SharpConfig::fromString('test words words words')->setArray('test', ['meow', 'meow', 'meow']);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testImmutableSetValues(): void {
|
public function testImmutableSetValues(): void {
|
||||||
$this->expectException(\RuntimeException::class);
|
$this->expectException(\RuntimeException::class);
|
||||||
\Syokuhou\SharpConfig::fromString('')->setValues([
|
\Syokuhou\FileConfig::fromString('')->setValues([
|
||||||
'stringval' => 'the',
|
'stringval' => 'the',
|
||||||
'intval' => 1234,
|
'intval' => 1234,
|
||||||
'floatval' => 56.78,
|
'floatval' => 56.78,
|
||||||
|
@ -57,7 +64,7 @@ final class SharpConfigTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testScoping(): void {
|
public function testScoping(): void {
|
||||||
$config = \Syokuhou\SharpConfig::fromLines([
|
$config = \Syokuhou\FileConfig::fromLines([
|
||||||
'test Inaccessible',
|
'test Inaccessible',
|
||||||
'scoped:test Accessible',
|
'scoped:test Accessible',
|
||||||
]);
|
]);
|
||||||
|
@ -70,7 +77,7 @@ final class SharpConfigTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testHasValues(): void {
|
public function testHasValues(): void {
|
||||||
$config = \Syokuhou\SharpConfig::fromLines([
|
$config = \Syokuhou\FileConfig::fromLines([
|
||||||
'test 123',
|
'test 123',
|
||||||
'scoped:test true',
|
'scoped:test true',
|
||||||
'scoped:meow meow',
|
'scoped:meow meow',
|
||||||
|
@ -85,7 +92,7 @@ final class SharpConfigTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetAllValueInfos(): void {
|
public function testGetAllValueInfos(): void {
|
||||||
$config = \Syokuhou\SharpConfig::fromFile(__DIR__ . '/sharpchat.cfg');
|
$config = \Syokuhou\FileConfig::fromFile(__DIR__ . '/sharpchat.cfg');
|
||||||
|
|
||||||
$all = $config->getAllValueInfos();
|
$all = $config->getAllValueInfos();
|
||||||
$expected = [
|
$expected = [
|
||||||
|
@ -131,7 +138,7 @@ final class SharpConfigTest extends TestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGetValues(): void {
|
public function testGetValues(): void {
|
||||||
$config = \Syokuhou\SharpConfig::fromFile(__DIR__ . '/sharpchat.cfg');
|
$config = \Syokuhou\FileConfig::fromFile(__DIR__ . '/sharpchat.cfg');
|
||||||
|
|
||||||
$this->assertNull($config->getValueInfo('doesnotexist'));
|
$this->assertNull($config->getValueInfo('doesnotexist'));
|
||||||
|
|
Reference in a new issue