Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,29 @@ A short version of read operation will also work:
clink '((($i:)) (($i:)))' --changes
```

## Export database as LiNo

Use `--out` to write the complete database to a `.lino` file after the query is processed. The older `--lino-output` option is also accepted.

```bash
clink '() ((child: father mother))' --out database.lino
```

`database.lino`:

```lino
(father: father father)
(mother: mother mother)
(child: father mother)
```

When links do not have names, exported references are plain link numbers:

```lino
(1: 1 1)
(2: 1 2)
```

## Update single link

Update link with index 1 and source 1 and target 1, changing target to 2.
Expand Down Expand Up @@ -282,44 +305,45 @@ clink '((1: 2 1) (2: 1 2)) ()' --changes --after
| `--before` | bool | `false` | `-b` | Print the state of the database before applying changes |
| `--changes` | bool | `false` | `-c` | Print the changes applied by the query |
| `--after` | bool | `false` | `--links`, `-a` | Print the state of the database after applying changes |
| `--out` | string | _None_ | `--lino-output` | Write the complete database as a LiNo file |

## For developers and debugging

### Execute from root

```bash
dotnet run --project Foundation.Data.Doublets.Cli -- '(((1: 1 1) (2: 2 2)) ((1: 1 2) (2: 2 1)))' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '(((1: 1 1) (2: 2 2)) ((1: 1 2) (2: 2 1)))' --changes --after
```

### Execute from folder

```bash
cd Foundation.Data.Doublets.Cli
cd csharp/Foundation.Data.Doublets.Cli
dotnet run -- '(((1: 1 1) (2: 2 2)) ((1: 1 2) (2: 2 1)))' --changes --after
```

### Complete examples:

```bash
dotnet run --project Foundation.Data.Doublets.Cli -- '() ((1 1) (2 2))' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '() ((1 1) (2 2))' --changes --after

dotnet run --project Foundation.Data.Doublets.Cli -- '((1: 1 1) (2: 2 2)) ((1: 1 2) (2: 2 1))' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '((1: 1 1) (2: 2 2)) ((1: 1 2) (2: 2 1))' --changes --after

dotnet run --project Foundation.Data.Doublets.Cli -- '((1 2) (2 1)) ()' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '((1 2) (2 1)) ()' --changes --after
```

```bash
dotnet run --project Foundation.Data.Doublets.Cli -- '() ((1 2) (2 1))' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '() ((1 2) (2 1))' --changes --after

dotnet run --project Foundation.Data.Doublets.Cli -- '((($index: $source $target)) (($index: $target $source)))' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '((($index: $source $target)) (($index: $target $source)))' --changes --after

dotnet run --project Foundation.Data.Doublets.Cli -- '((1: 2 1) (2: 1 2)) ()' --changes --after
dotnet run --project csharp/Foundation.Data.Doublets.Cli -- '((1: 2 1) (2: 1 2)) ()' --changes --after
```

### Publish next version:

```bash
VERSION=$(awk -F'[<>]' '/<Version>/ {print $3}' Foundation.Data.Doublets.Cli/Foundation.Data.Doublets.Cli.csproj) && git tag "v$VERSION" && git push origin "v$VERSION"
VERSION=$(awk -F'[<>]' '/<Version>/ {print $3}' csharp/Foundation.Data.Doublets.Cli/Foundation.Data.Doublets.Cli.csproj) && git tag "v$VERSION" && git push origin "v$VERSION"
```

## Running a Specific Test with Detailed Output
Expand Down
5 changes: 5 additions & 0 deletions csharp/.changeset/add-lino-output-export.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'Foundation.Data.Doublets.Cli': minor
---

Added `--out`/`--lino-output` database export support that writes the complete links database as LiNo with named references when available.
16 changes: 8 additions & 8 deletions csharp/Foundation.Data.Doublets.Cli.Tests/ChangesSimplifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ public void SimplifyChanges_SpecificExample_RemovesIntermediateStates()
{
// (1: 2 1) ↦ (1: 0 0)
(new Link<uint>(index: 1, source: 2, target: 1), new Link<uint>(index: 1, source: 0, target: 0)),

// (2: 1 2) ↦ (2: 0 0)
(new Link<uint>(index: 2, source: 1, target: 2), new Link<uint>(index: 2, source: 0, target: 0)),

// (2: 0 0) ↦ (0: 0 0)
(new Link<uint>(index: 2, source: 0, target: 0), new Link<uint>(index: 0, source: 0, target: 0)),

// (1: 0 0) ↦ (0: 0 0)
(new Link<uint>(index: 1, source: 0, target: 0), new Link<uint>(index: 0, source: 0, target: 0))
};
Expand Down Expand Up @@ -61,13 +61,13 @@ public void SimplifyChanges_MultipleChainsFromSameBefore_RemovesIntermediateStat
{
// (0: 0 0) ↦ (1: 0 0)
(new Link<uint>(index: 0, source: 0, target: 0), new Link<uint>(index: 1, source: 0, target: 0)),

// (1: 0 0) ↦ (1: 1 2)
(new Link<uint>(index: 1, source: 0, target: 0), new Link<uint>(index: 1, source: 1, target: 2)),

// (0: 0 0) ↦ (2: 0 0)
(new Link<uint>(index: 0, source: 0, target: 0), new Link<uint>(index: 2, source: 0, target: 0)),

// (2: 0 0) ↦ (2: 2 1)
(new Link<uint>(index: 2, source: 0, target: 0), new Link<uint>(index: 2, source: 2, target: 1))
};
Expand Down Expand Up @@ -337,10 +337,10 @@ public void SimplifyChanges_Issue26_UpdateOperationSimplification()
{
// Step 1: Link (1: 1 2) is first deleted (becomes null/empty)
(new Link<uint>(index: 1, source: 1, target: 2), new Link<uint>(index: 0, source: 0, target: 0)),

// Step 2: New link (1: 2 1) is created (swapped source and target)
(new Link<uint>(index: 0, source: 0, target: 0), new Link<uint>(index: 1, source: 2, target: 1)),

// Step 3: Link (2: 2 1) is directly updated to (2: 1 2)
(new Link<uint>(index: 2, source: 2, target: 1), new Link<uint>(index: 2, source: 1, target: 2)),
};
Expand Down
138 changes: 138 additions & 0 deletions csharp/Foundation.Data.Doublets.Cli.Tests/LinoDatabaseOutputTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
using Foundation.Data.Doublets.Cli;
using Platform.Data.Doublets;

namespace Foundation.Data.Doublets.Cli.Tests;

public class LinoDatabaseOutputTests
{
[Fact]
public void FormatDatabase_UsesNumberedReferences_WhenLinksHaveNoNames()
{
WithNamedLinks(links =>
{
links.GetOrCreate(1u, 1u);
links.GetOrCreate(1u, 2u);

var lines = LinoDatabaseOutput.FormatDatabase(links);

Assert.Equal(new[] { "(1: 1 1)", "(2: 1 2)" }, lines);
});
}

[Fact]
public void FormatDatabase_UsesNamesForIndexesSourcesAndTargets_WhenNamesExist()
{
WithNamedLinks(links =>
{
var father = links.GetOrCreate(1u, 1u);
links.SetName(father, "father");
var mother = links.GetOrCreate(2u, 2u);
links.SetName(mother, "mother");
var child = links.GetOrCreate(father, mother);
links.SetName(child, "child");

var lines = LinoDatabaseOutput.FormatDatabase(links);

Assert.Equal(new[]
{
"(father: father father)",
"(mother: mother mother)",
"(child: father mother)"
}, lines);
});
}

[Fact]
public void FormatDatabase_EscapesNamesThatNeedQuoting()
{
WithNamedLinks(links =>
{
var source = links.GetOrCreate(1u, 1u);
links.SetName(source, "source name");
var target = links.GetOrCreate(2u, 2u);
links.SetName(target, "target:ref");
var child = links.GetOrCreate(source, target);
links.SetName(child, "child(ref)");

var lines = LinoDatabaseOutput.FormatDatabase(links);

Assert.Equal(new[]
{
"('source name': 'source name' 'source name')",
"('target:ref': 'target:ref' 'target:ref')",
"('child(ref)': 'source name' 'target:ref')"
}, lines);
});
}

[Fact]
public void FormatDatabase_SelectsQuoteStyleForNamesContainingQuotes()
{
WithNamedLinks(links =>
{
var singleQuote = links.GetOrCreate(1u, 1u);
links.SetName(singleQuote, "single'quote");
var doubleQuote = links.GetOrCreate(2u, 2u);
links.SetName(doubleQuote, "double\"quote");
var bothQuotes = links.GetOrCreate(singleQuote, doubleQuote);
links.SetName(bothQuotes, "both'\"quote");

var lines = LinoDatabaseOutput.FormatDatabase(links);

Assert.Equal(new[]
{
"(\"single'quote\": \"single'quote\" \"single'quote\")",
"('double\"quote': 'double\"quote' 'double\"quote')",
"('both\\'\"quote': \"single'quote\" 'double\"quote')"
}, lines);
});
}

[Fact]
public void WriteToFile_WritesCompleteDatabaseAsLinoLines()
{
WithNamedLinks(links =>
{
links.GetOrCreate(1u, 1u);
links.GetOrCreate(2u, 2u);

var outputPath = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid():N}.lino");
try
{
LinoDatabaseOutput.WriteToFile(links, outputPath);

Assert.Equal(new[] { "(1: 1 1)", "(2: 2 2)" }, File.ReadAllLines(outputPath));
}
finally
{
if (File.Exists(outputPath))
{
File.Delete(outputPath);
}
}
});
}

private static void WithNamedLinks(Action<NamedLinksDecorator<uint>> test)
{
var dbPath = Path.GetTempFileName();
var namesDbPath = NamedLinksDecorator<uint>.MakeNamesDatabaseFilename(dbPath);

try
{
var links = new NamedLinksDecorator<uint>(dbPath, false);
test(links);
}
finally
{
if (File.Exists(dbPath))
{
File.Delete(dbPath);
}
if (File.Exists(namesDbPath))
{
File.Delete(namesDbPath);
}
}
}
}
16 changes: 8 additions & 8 deletions csharp/Foundation.Data.Doublets.Cli/ChangesSimplifier.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public static class ChangesSimplifier
/// Removes problematic duplicate before states that lead to simplification issues.
/// This fixes Issue #26 where multiple transformations from the same before state
/// to conflicting after states (including null states) would cause the simplifier to fail.
///
///
/// The key insight: If we have multiple transitions from the same before state,
/// and one of them is to a "null" state (0: 0 0), we should prefer the non-null transition
/// as it represents the actual final transformation.
Expand All @@ -144,13 +144,13 @@ public static class ChangesSimplifier
{
// Group changes by their before state
var groupedChanges = changes.GroupBy(c => c.Before, LinkEqualityComparer.Instance);

var result = new List<(Link<uint> Before, Link<uint> After)>();

foreach (var group in groupedChanges)
{
var changesForThisBefore = group.ToList();

if (changesForThisBefore.Count == 1)
{
// No duplicates, keep as is
Expand All @@ -160,11 +160,11 @@ public static class ChangesSimplifier
{
// Multiple changes from the same before state
// Check if any of them is to a null state (0: 0 0)
var nullTransition = changesForThisBefore.FirstOrDefault(c =>
var nullTransition = changesForThisBefore.FirstOrDefault(c =>
c.After.Index == 0 && c.After.Source == 0 && c.After.Target == 0);
var nonNullTransitions = changesForThisBefore.Where(c =>
var nonNullTransitions = changesForThisBefore.Where(c =>
!(c.After.Index == 0 && c.After.Source == 0 && c.After.Target == 0)).ToList();

if (nullTransition != default && nonNullTransitions.Count > 0)
{
// Issue #26 scenario: We have both null and non-null transitions
Expand All @@ -179,7 +179,7 @@ public static class ChangesSimplifier
}
}
}

return result;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackAsTool>true</PackAsTool>
<ToolCommandName>clink</ToolCommandName>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RootNamespace>Foundation.Data.Doublets.Cli</RootNamespace>
</PropertyGroup>
<PropertyGroup>
<Authors>link-foundation</Authors>
<Description>A CLI tool for links manipulation.</Description>
<PackageId>clink</PackageId>
<Version>2.2.2</Version>
<PackageLicenseExpression>Unlicense</PackageLicenseExpression>
<RepositoryUrl>https://github.com/link-foundation/link-cli</RepositoryUrl>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Platform.Data" Version="0.16.1" />
<PackageReference Include="Platform.Data.Doublets" Version="0.18.1" />
<PackageReference Include="Platform.Data.Doublets.Sequences" Version="0.6.5" />
<PackageReference Include="Platform.Protocols.Lino" Version="0.4.5" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>
<ItemGroup>
<None Include="../../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackAsTool>true</PackAsTool>
<ToolCommandName>clink</ToolCommandName>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RootNamespace>Foundation.Data.Doublets.Cli</RootNamespace>
</PropertyGroup>

<PropertyGroup>
<Authors>link-foundation</Authors>
<Description>A CLI tool for links manipulation.</Description>
<PackageId>clink</PackageId>
<Version>2.2.2</Version>
<PackageLicenseExpression>Unlicense</PackageLicenseExpression>
<RepositoryUrl>https://github.com/link-foundation/link-cli</RepositoryUrl>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Platform.Data" Version="0.16.1" />
<PackageReference Include="Platform.Data.Doublets" Version="0.18.1" />
<PackageReference Include="Platform.Data.Doublets.Sequences" Version="0.6.5" />
<PackageReference Include="Platform.Protocols.Lino" Version="0.4.5" />
<PackageReference Include="System.CommandLine" Version="2.0.0-beta4.22272.1" />
</ItemGroup>

<ItemGroup>
<None Include="../../README.md" Pack="true" PackagePath="/" />
</ItemGroup>
</Project>
Loading
Loading