diff --git a/README.md b/README.md index 4569d827..f97a021f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Collection of Internet Computer Protocol (ICP) libraries for .NET/Blazor/Unity - Start coding 💻 # 📡 Generating a client for a canister -You can specify all the models and api calls yourself, but this is a tool to automatically generate a client and models based on the cansiter or .did file +You can specify all the models and api calls yourself, but this is a tool to automatically generate a client and models based on the canister or .did file - Prerequisite: Have .Net 6 installed (https://dotnet.microsoft.com/en-us/download/dotnet) - Navigate to directory of .Net project ``` @@ -141,4 +141,4 @@ public enum MyVariantTag - [IC Http Interface Spec](https://smartcontracts.org/docs/current/references/ic-interface-spec) - [Candid Spec](https://github.com/dfinity/candid/blob/master/spec/Candid.md) - [Candid Decoder](https://fxa77-fiaaa-aaaae-aaana-cai.raw.ic0.app/explain) -- [Candid UI Tester](https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app) \ No newline at end of file +- [Candid UI Tester](https://a4gq6-oaaaa-aaaab-qaa4q-cai.raw.ic0.app) diff --git a/src/Agent/API.xml b/src/Agent/API.xml index 52c70ba1..dff315b8 100644 --- a/src/Agent/API.xml +++ b/src/Agent/API.xml @@ -4,45 +4,6 @@ EdjCase.ICP.Agent - - - An `IAgent` implementation using HTTP to make requests to the IC - - - - - The identity that will be used on each request unless overriden - This identity can be anonymous - - - - Optional. Identity to use for each request. If unspecified, will use anonymous identity - Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation - Optional. Sets the http client to use, otherwise will use the default http client - - - Optional. Identity to use for each request. If unspecified, will use anonymous identity - Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation - Url to the boundry node to connect to. Defaults to `https://ic0.app/` - - - - - - - - - - - - - - - - - - - The default http client to use with the built in `HttpClient` @@ -111,9 +72,48 @@ + + + An `IAgent` implementation using HTTP to make requests to the IC + + + + + The identity that will be used on each request unless overriden + This identity can be anonymous + + + + Optional. Identity to use for each request. If unspecified, will use anonymous identity + Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation + Optional. Sets the http client to use, otherwise will use the default http client + + + Optional. Identity to use for each request. If unspecified, will use anonymous identity + Optional. Bls crypto implementation to validate signatures. If unspecified, will use default implementation + Url to the boundry node to connect to. Defaults to `https://ic0.app/` + + + + + + + + + + + + + + + + + + + - An agent is used to communicate with the Internet Computer with certain protocols that + An agent is used to communicate with the Internet Computer with certain protocols that are specific to an `IAgent` implementation @@ -130,7 +130,7 @@ Canister to read state for The state paths to get information for. Other state data will be pruned if not specified Optional. Token to cancel request - A response that contains the certificate of the current cansiter state + A response that contains the certificate of the current canister state @@ -143,12 +143,12 @@ - Sends a call request to a specified canister method and gets back an id of the + Sends a call request to a specified canister method and gets back an id of the request that is being processed. This call does NOT wait for the request to be complete. Either check the status with `GetRequestStatusAsync` or use the `CallAndWaitAsync` method Canister to read state for - The name of the method to call on the cansiter + The name of the method to call on the canister The candid arg to send with the request Optional. Specifies the relevant canister id if calling the root canister Optional. Token to cancel request @@ -166,7 +166,7 @@ Sends a query request to a specified canister method Canister to read state for - The name of the method to call on the cansiter + The name of the method to call on the canister The candid arg to send with the request Optional. Token to cancel request The response data of the query call @@ -191,7 +191,7 @@ The agent to use for the call Canister to read state for - The name of the method to call on the cansiter + The name of the method to call on the canister The candid arg to send with the request Optional. Specifies the relevant canister id if calling the root canister Optional. Token to cancel request @@ -1004,7 +1004,7 @@ - When the cansiter request has errors to query request + When the canister request has errors to query request diff --git a/src/Agent/Agents/IAgent.cs b/src/Agent/Agents/IAgent.cs index 835237c6..b276ba67 100644 --- a/src/Agent/Agents/IAgent.cs +++ b/src/Agent/Agents/IAgent.cs @@ -8,7 +8,7 @@ namespace EdjCase.ICP.Agent.Agents { /// - /// An agent is used to communicate with the Internet Computer with certain protocols that + /// An agent is used to communicate with the Internet Computer with certain protocols that /// are specific to an `IAgent` implementation /// public interface IAgent @@ -24,7 +24,7 @@ public interface IAgent /// Canister to read state for /// The state paths to get information for. Other state data will be pruned if not specified /// Optional. Token to cancel request - /// A response that contains the certificate of the current cansiter state + /// A response that contains the certificate of the current canister state Task ReadStateAsync(Principal canisterId, List paths, CancellationToken? cancellationToken = null); /// @@ -37,12 +37,12 @@ public interface IAgent Task GetRequestStatusAsync(Principal canisterId, RequestId id, CancellationToken? cancellationToken = null); /// - /// Sends a call request to a specified canister method and gets back an id of the + /// Sends a call request to a specified canister method and gets back an id of the /// request that is being processed. This call does NOT wait for the request to be complete. /// Either check the status with `GetRequestStatusAsync` or use the `CallAndWaitAsync` method /// /// Canister to read state for - /// The name of the method to call on the cansiter + /// The name of the method to call on the canister /// The candid arg to send with the request /// Optional. Specifies the relevant canister id if calling the root canister /// Optional. Token to cancel request @@ -60,7 +60,7 @@ public interface IAgent /// Sends a query request to a specified canister method /// /// Canister to read state for - /// The name of the method to call on the cansiter + /// The name of the method to call on the canister /// The candid arg to send with the request /// Optional. Token to cancel request /// The response data of the query call @@ -86,7 +86,7 @@ public static class IAgentExtensions /// /// The agent to use for the call /// Canister to read state for - /// The name of the method to call on the cansiter + /// The name of the method to call on the canister /// The candid arg to send with the request /// Optional. Specifies the relevant canister id if calling the root canister /// Optional. Token to cancel request diff --git a/src/Agent/Responses/QueryResponse.cs b/src/Agent/Responses/QueryResponse.cs index 96a0ef9f..66829b35 100644 --- a/src/Agent/Responses/QueryResponse.cs +++ b/src/Agent/Responses/QueryResponse.cs @@ -197,7 +197,7 @@ public enum QueryResponseType /// Replied, /// - /// When the cansiter request has errors to query request + /// When the canister request has errors to query request /// Rejected } @@ -229,4 +229,4 @@ internal QueryRejectInfo(RejectCode code, string? message, string? errorCode) this.ErrorCode = errorCode; } } -} \ No newline at end of file +} diff --git a/src/Candid/API.xml b/src/Candid/API.xml index 7faaf9bf..2c9bf130 100644 --- a/src/Candid/API.xml +++ b/src/Candid/API.xml @@ -1660,7 +1660,7 @@ The method expects a sub-account byte array of exactly 32 bytes in length. If the provided array does not meet this requirement, an is thrown. - + The account identifier format follows the specification: account_identifier(principal, subaccount_identifier) = CRC32(h) || h where h = sha224("\x0Aaccount-id" || principal || subaccount_identifier). @@ -1671,9 +1671,9 @@ - Helper method to create the principal for the Internet Computer management cansiter "aaaaa-aa" + Helper method to create the principal for the Internet Computer management canister "aaaaa-aa" - Principal for the management cansiter + Principal for the management canister diff --git a/src/Candid/Models/Principal.cs b/src/Candid/Models/Principal.cs index b13db80b..e0492f26 100644 --- a/src/Candid/Models/Principal.cs +++ b/src/Candid/Models/Principal.cs @@ -105,7 +105,7 @@ public string ToHex() /// /// The method expects a sub-account byte array of exactly 32 bytes in length. If the provided array does not meet this /// requirement, an is thrown. - /// + /// /// The account identifier format follows the specification: /// account_identifier(principal, subaccount_identifier) = CRC32(h) || h /// where h = sha224("\x0Aaccount-id" || principal || subaccount_identifier). @@ -119,7 +119,7 @@ public byte[] ToLedgerAccount(byte[]? subAccount) if (subAccount == null) { // Empty byte array of 32 bytes for no subaccount - subAccount = new byte[32]; + subAccount = new byte[32]; } // Ensure the subAccount is of expected length (32 bytes) if (subAccount.Length != 32) @@ -147,9 +147,9 @@ public byte[] ToLedgerAccount(byte[]? subAccount) } /// - /// Helper method to create the principal for the Internet Computer management cansiter "aaaaa-aa" + /// Helper method to create the principal for the Internet Computer management canister "aaaaa-aa" /// - /// Principal for the management cansiter + /// Principal for the management canister public static Principal ManagementCanisterId() { return new Principal(PrincipalType.Opaque, new byte[0]); @@ -287,4 +287,4 @@ public override int GetHashCode() return !v1.Equals(v2); } } -} \ No newline at end of file +} diff --git a/src/Candid/Models/Values/CandidFunc.cs b/src/Candid/Models/Values/CandidFunc.cs index f05aff33..1da2be02 100644 --- a/src/Candid/Models/Values/CandidFunc.cs +++ b/src/Candid/Models/Values/CandidFunc.cs @@ -2,8 +2,6 @@ using EdjCase.ICP.Candid.Utilities; using System; using System.Buffers; -using System.Linq; -using System.Text; namespace EdjCase.ICP.Candid.Models.Values { @@ -21,33 +19,36 @@ public class CandidFunc : CandidValue /// public bool IsOpaqueReference { get; } - private readonly (CandidService Service, string Method)? serviceInfo; + + public (CandidService Service, string Method)? ServiceInfo { get; } /// The candid service definition the function lives in /// The name of the function public CandidFunc(CandidService service, string name) { this.IsOpaqueReference = false; - this.serviceInfo = (service, name); + this.ServiceInfo = (service, name); } private CandidFunc() { this.IsOpaqueReference = true; - this.serviceInfo = null; + this.ServiceInfo = null; } /// - internal override void EncodeValue(CandidType type, Func getReferencedType, IBufferWriter destination) + internal override void EncodeValue(CandidType type, Func getReferencedType, + IBufferWriter destination) { if (this.IsOpaqueReference) { destination.WriteOne(0); return; } + CandidFuncType t = DereferenceType(type, getReferencedType); - (CandidService service, string method) = this.serviceInfo!.Value; + (CandidService service, string method) = this.ServiceInfo!.Value; destination.WriteOne(1); // Encode bit to indicate it is not opaque service.EncodeValue(t, getReferencedType, destination); // Encode value destination.WriteUtf8LebAndValue(method); // Encode method name @@ -56,7 +57,7 @@ internal override void EncodeValue(CandidType type, Func public override int GetHashCode() { - return HashCode.Combine(this.IsOpaqueReference, this.serviceInfo); + return HashCode.Combine(this.IsOpaqueReference, this.ServiceInfo); } /// @@ -68,13 +69,16 @@ public override bool Equals(CandidValue? other) { return false; } + if (this.IsOpaqueReference) { // TODO can we tell if they are equal? return false; } - return this.serviceInfo == f.serviceInfo; + + return this.ServiceInfo == f.ServiceInfo; } + return false; } @@ -85,7 +89,8 @@ public override string ToString() { return "(Opaque Reference)"; } - (CandidService service, string method) = this.serviceInfo!.Value; + + (CandidService service, string method) = this.ServiceInfo!.Value; return $"(Method: {method}, Service: {service})"; } @@ -99,5 +104,4 @@ public static CandidFunc OpaqueReference() return new CandidFunc(); } } - } diff --git a/src/ClientGenerator/API.xml b/src/ClientGenerator/API.xml index ef9719d5..387f9955 100644 --- a/src/ClientGenerator/API.xml +++ b/src/ClientGenerator/API.xml @@ -42,14 +42,14 @@ The name of the client class and file to use - + If true, will treat `FilePathOrCanisterId` as a canister id and get the definition from the canister. Otherwise will treat it as a file path and get the definition from the file - The file path to a local *.did file to get definition from or the canister id, depending on `GetDefinitionFromCansiter` value + The file path to a local *.did file to get definition from or the canister id, depending on `GetDefinitionFromCanister` value diff --git a/src/ClientGenerator/ClientGenerationOptions.cs b/src/ClientGenerator/ClientGenerationOptions.cs index 407b770c..6666f5e5 100644 --- a/src/ClientGenerator/ClientGenerationOptions.cs +++ b/src/ClientGenerator/ClientGenerationOptions.cs @@ -20,53 +20,65 @@ public class ClientGenerationOptions /// The name of the client class and file to use /// public string Name { get; } + /// /// If true, will treat `FilePathOrCanisterId` as a canister id and get the definition from the canister. Otherwise will treat it as a file path and get the definition from the file /// - public bool GetDefinitionFromCansiter { get; } + public bool GetDefinitionFromCanister { get; } + /// - /// The file path to a local *.did file to get definition from or the canister id, depending on `GetDefinitionFromCansiter` value + /// The file path to a local *.did file to get definition from or the canister id, depending on `GetDefinitionFromCanister` value /// public string FilePathOrCanisterId { get; } + /// /// If true, removes all files in the output directory before regeneration, otherwise does nothing. Defaults to true /// public bool PurgeOutputDirectory { get; } + /// /// The base namespace to use in the generated files /// public string Namespace { get; } + /// /// If true, there will be no folders, all files will be in the same directory /// public string OutputDirectory { get; } + /// /// If true, there will be no folders, all files will be in the same directory /// public bool NoFolders { get; } + /// /// If true, the nullable C# feature will be used /// public bool FeatureNullable { get; } + /// /// If true, variant classes will be generated with properties instead of methods /// public bool VariantsUseProperties { get; } + /// /// If true, the names of properties and methods will keep the raw candid name. /// Otherwise they will be converted to something prettier /// public bool KeepCandidCase { get; } + /// /// If true, OptionalValue will be used for opt values /// Otherwise will use just the nullable class values or nullable struct /// Defaults to true /// public bool OverrideOptionalValue { get; } + /// /// Optional. The url of the boundry node for the internet computer. Defaults to ic0.app /// public Uri? BoundryNodeUrl { get; } + /// /// Optional. Specifies options for each candid type in the definition. /// Only supports named types, no anonymous types @@ -74,7 +86,6 @@ public class ClientGenerationOptions public Dictionary Types { get; } - /// The name of the client class and file to use /// The base namespace to use in the generated files /// If true, will treat as a canister id and get the definition from the canister. Otherwise will treat it as a file path and get the definition from the file @@ -108,10 +119,11 @@ public ClientGenerationOptions( { throw new ArgumentNullException(nameof(name)); } + // Trim whitespace and replace spaces with underscores this.Name = StringUtil.ToPascalCase(name.Trim().Replace(' ', '_')); this.Namespace = @namespace ?? throw new ArgumentNullException(nameof(@namespace)); - this.GetDefinitionFromCansiter = getDefinitionFromCanister; + this.GetDefinitionFromCanister = getDefinitionFromCanister; this.FilePathOrCanisterId = filePathOrCandidId; this.OutputDirectory = outputDirectory; this.PurgeOutputDirectory = purgeOutputDirectory; @@ -135,6 +147,7 @@ public class NamedTypeOptions /// Optional. The C# type name to use instead of the default /// public string? NameOverride { get; } + /// /// Optional. The field or option type information /// @@ -161,10 +174,12 @@ public class TypeOptions /// Optional. The type options for each of the records fields or variant options /// public Dictionary Fields { get; } + /// /// Optional. The type options for the sub type of a vec or opt /// public TypeOptions? InnerType { get; } + /// /// Optional. How the type should be represented in C# /// diff --git a/src/ClientGenerator/README.md b/src/ClientGenerator/README.md index 62c4c8be..317c1047 100644 --- a/src/ClientGenerator/README.md +++ b/src/ClientGenerator/README.md @@ -71,7 +71,7 @@ candid-client-generator gen ./ - `file` - Will create a client based on a service definition file (`*.did`) - `file-path` - (Text) REQUIRED. The file path to the `*.did` file to generate from - `canister` - Creates a client based on a canister id - - `cansiter-id` - (Text) REQUIRED. The principal id of the canister to generate a client for + - `canister-id` - (Text) REQUIRED. The principal id of the canister to generate a client for - `output-directory` - (Text) OPTIONAL. Directory to put all generated client files. Overrides the top level `output-directory`. NOTE: this does not create a sub-folder based on the client name like the top level `output-directory` does - `no-folders` - (Bool) OPTIONAL. If true, no sub-folders will be generated for the client. All generated files will be in a flat structure. Defaults to false. Overrides the top level `no-folders` - `feature-nullable` - (Bool) Optional. Sets whether to use the C# nullable feature when generating the client (like `object?`). Defaults to true. Overrides the top level `feature-nullable` @@ -170,4 +170,4 @@ var rewriter = new MyCustomCSharpSyntaxRewriter(); syntax = syntax.Rewrite(rewriter); (string clientFile, List<(string Name, string Contents)> typeFiles) = syntax.GenerateFileContents(); // Write string contents to files... -``` \ No newline at end of file +``` diff --git a/src/ClientGenerator/Tool.cs b/src/ClientGenerator/Tool.cs index 12d7bf12..570f77ba 100644 --- a/src/ClientGenerator/Tool.cs +++ b/src/ClientGenerator/Tool.cs @@ -89,10 +89,10 @@ namespace = ""My.Namespace"" # Will make generated files in a flat structure with no folders, for this client #no-folders = true - + # Can specify multiple clients by creating another [[clients]] #[[clients]] -#name = ""MyClient2"" +#name = ""MyClient2"" #type = ""file"" #file-path = ""../MyService2.did"" "; @@ -117,7 +117,7 @@ private async static Task Generate(GenerateOptions options) foreach (ClientGenerationOptions clientOptions in clientOptionList) { ClientSyntax source; - if (clientOptions.GetDefinitionFromCansiter) + if (clientOptions.GetDefinitionFromCanister) { string canisterIdString = clientOptions.FilePathOrCanisterId; Principal canisterId = Principal.FromText(canisterIdString);