gen_writers

Generates the code for the "bulk writers" in the "BulkWriter" C# shared libraries.
git clone https://git.philomathiclife.com/repos/gen_writers
Log | Files | Refs | README

main.rs (15911B)


      1 //! This crate generates the code for the "bulk writers"
      2 //! in the `BulkWriters128` and `BulkWriters256` C# libraries.
      3 #![no_implicit_prelude]
      4 #![deny(
      5     unsafe_code,
      6     unused,
      7     warnings,
      8     clippy::all,
      9     clippy::cargo,
     10     clippy::nursery,
     11     clippy::pedantic
     12 )]
     13 #![allow(
     14     clippy::arithmetic_side_effects,
     15     clippy::implicit_return,
     16     clippy::integer_arithmetic
     17 )]
     18 #[allow(unused_extern_crates)]
     19 extern crate alloc;
     20 #[allow(unused_extern_crates)]
     21 extern crate core;
     22 #[allow(unused_extern_crates)]
     23 extern crate std;
     24 use alloc::string::{String, ToString};
     25 use core::clone::Clone;
     26 use core::convert::From;
     27 use core::fmt::{self, Debug, Display, Formatter};
     28 use core::result::Result::{self, Ok};
     29 use std::env;
     30 use std::fs::File;
     31 use std::io::{Error, Write};
     32 fn main() -> Result<(), GenErr> {
     33     #[allow(deprecated)]
     34     let mut path0 = env::home_dir().ok_or(GenErr::HomeVarNotSet)?;
     35     path0.push(".c#");
     36     let mut path1 = path0.clone();
     37     path0.push("BulkWriters128/BulkWriters.cs");
     38     let mut f = File::options().write(true).create_new(true).open(path0)?;
     39     path1.push("BulkWriters256/BulkWriters.cs");
     40     let mut f2 = File::options().write(true).create_new(true).open(path1)?;
     41     let mut s = String::with_capacity(32561);
     42     write(&mut f, 0, &mut s)?;
     43     write(&mut f2, 128, &mut s)?;
     44     Ok(())
     45 }
     46 /// Writes the file.
     47 /// `s` is used as a buffer to write an entire `BulkWriter` into
     48 /// before being used by `f` to write to the underlying file descriptor.
     49 /// `v` is the starting arity of tuples: 0 or 128.
     50 fn write(f: &mut File, v: u8, s: &mut String) -> Result<(), Error> {
     51     s.push_str("// This code was generated via a CLI app that used the source code in BulkWriterBaseCase.cs as a template.
     52 // To improve compilation performance, the code uses very little 'whitespace' beyond what is necessary and contains no documentation, warning suppressions, or comments.
     53 using Microsoft.Data.SqlClient;
     54 using Serde.Bin.Ser;
     55 using Std;
     56 using Std.Iter;
     57 using Std.Maybe;
     58 using Std.Num;
     59 using Std.Result;
     60 using System;
     61 using System.Data;
     62 using System.Diagnostics;
     63 using static SQLServer.Helpers;
     64 using System.Runtime.InteropServices;
     65 #pragma warning disable CA1005, CA1045, CA1051, CA1062, CA1066, CA1815, CA2100, CA2231, IDE0032
     66 namespace SQLServer {\n");
     67     //
     68     for i in (if v == 0 { 1 } else { v })..=(v + 127) {
     69         s.push_str("[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Unicode,Pack= 0)]public struct BulkWriter<");
     70         for j in 0..=i {
     71             Kind::T.write(s, j);
     72         }
     73         s.pop();
     74         s.push_str(">:IBulkWriter ");
     75         for j in 0..=i {
     76             Kind::Bound.write(s, j);
     77         }
     78         s.pop();
     79         s.push_str(r#"{public BulkWriter()=>throw new InvalidOperationException("Parameterless constructor is not allowed to be called!");BulkWriter(UserTable table,Maybe<ErrorTable>errTable,Prod<ulong,double>maxErrorsAllowed,string processName,string userName,bool encrypted,bool encryptedMod)=>(Destination,ErrTable,MaxErrorsAllowed,_currentErrorCount,_currentProcessedCount,_processName,_userName,_containsEncryptedColumn,_mustBeAllowEncryptedValueModifications)=(table,errTable,maxErrorsAllowed,ulong.MinValue,ulong.MinValue,processName,userName,encrypted,encryptedMod);public readonly Maybe<ErrorTable>ErrTable;public readonly UserTable Destination;public readonly Prod<ulong,double>MaxErrorsAllowed;ulong _currentErrorCount;ulong _currentProcessedCount;readonly string _processName;readonly string _userName;readonly bool _containsEncryptedColumn;readonly bool _mustBeAllowEncryptedValueModifications;readonly Maybe<ErrorTable>IBulkWriter.ErrTable=>ErrTable;readonly UserTable IBulkWriter.Destination=>Destination;readonly Prod<ulong,double>IBulkWriter.MaxErrorsAllowed=>MaxErrorsAllowed;public readonly ulong CurrentSuccessfullyProcessedCount=>_currentProcessedCount-_currentErrorCount;public readonly ulong CurrentErrorCount=>_currentErrorCount;public readonly ulong CurrentProcessedCount=>_currentProcessedCount;public readonly double CurrentErrorRatio=>(double)_currentErrorCount/_currentProcessedCount;public readonly bool IsInError=>_currentErrorCount>MaxErrorsAllowed.Item0&&CurrentErrorRatio>MaxErrorsAllowed.Item1;public static Result<BulkWriter<"#);
     80         for j in 0..=i {
     81             Kind::T.write(s, j);
     82         }
     83         s.pop();
     84         s.push_str(">,BulkWriterCreateError>New(in UserTable table,Prod<ulong,double>maxErrorsAllowed,in Maybe<ErrorTable>errTable,string processName,string userName){if(table.Schema.Database.IsReadOnly){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.DatabaseIsReadOnly,new StackTrace(1,true)));}else if(double.IsNaN(maxErrorsAllowed.Item1)||double.IsNegative(maxErrorsAllowed.Item1)||maxErrorsAllowed.Item1>1.0d){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.InvalidErrorRatio,new StackTrace(1,true)));}else if(processName.Length>128){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.ProcessNameLengthExceeds128,new StackTrace(1,true)));}else if(userName.Length>128){return new(new BulkWriterCreateError(BulkWriterCreateError.Tag.UserNameLengthExceeds128,new StackTrace(1,true)));}else{var counter=ushort.MinValue;var match=TypeMatch(Std.Iter.Functions.FromFn(()=>counter++switch{");
     85         for j in 0..=i {
     86             Kind::GetType.write(s, j);
     87         }
     88         s.push_str("_=>Maybe<Type>.None()}),in table);return match.IsSome?match.Unwrap()?new(new BulkWriter<");
     89         for j in 0..=i {
     90             Kind::T.write(s, j);
     91         }
     92         s.pop();
     93         s.push_str(">(table,errTable,maxErrorsAllowed,processName,userName,table.ContainsEncryptedColumn(),false)):new(new BulkWriterCreateError(BulkWriterCreateError.Tag.TypeMismatch,new StackTrace(1,true))):new(new BulkWriter<");
     94         for j in 0..=i {
     95             Kind::T.write(s, j);
     96         }
     97         s.pop();
     98         s.push_str(">(table,errTable,maxErrorsAllowed,processName,userName,true,true));}}public override readonly bool Equals(object?_)=>false;public override readonly int GetHashCode()=>0;readonly void IBulkWriter.Sealed(){}public override readonly string ToString()=>string.Empty;public Result<Unit,WriteError>Write<TRowIter,TRow,TProd,TErr>(ref TRowIter iter,WriteOptions writeOptions,Maybe<NonZeroUshort>batchSize,Maybe<NonZeroUshort>timeout,bool enableStreaming,bool isSortedAccordingToClusteredIndex,SessionOptions options)where TErr:notnull,IBulkRowError where TProd:notnull,IBinSerializable,IProduct<");
     99         for j in 0..=i {
    100             Kind::T.write(s, j);
    101         }
    102         s.pop();
    103         s.push_str(r#">where TRow:notnull,ISum<TProd,TErr>where TRowIter:notnull,IFusedIterator<TRow>{var allowEncryptMod=false;if((writeOptions&WriteOptions.AllowEncryptedValueModifications)==WriteOptions.AllowEncryptedValueModifications){if(_containsEncryptedColumn){allowEncryptMod=true;}else{return new(new WriteError(WriteError.Tag.TableExpectedToContainEncryptedDataButDoesNot,new StackTrace(1,true)));}}else if(_mustBeAllowEncryptedValueModifications){return new(new WriteError(WriteError.Tag.TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications,new StackTrace(1,true)));}using var con=Functions.CreateOpenedConnection(in Destination.Schema.Database,options,!allowEncryptMod&&_containsEncryptedColumn,Maybe<Uri>.None());if((writeOptions&WriteOptions.KeepIdentity)==WriteOptions.KeepIdentity&&Destination.ContainsIDENTITYColumn()){using var txn=con.BeginTransaction(IsolationLevel.Serializable);using (SqlCommand qry=new($"SET IDENTITY_INSERT [{Destination.Schema.Name.Value}].[{Destination.Name}] ON;",con,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=60,CommandType=CommandType.Text,EnableOptimizedParameterBinding=true}){_=qry.ExecuteNonQuery();}txn.Commit();}return WriteInternal<TRowIter,TRow,TProd,TErr>(ref iter,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,con,null,(options&SessionOptions.NUMERIC_ROUNDABORT_OFF)!=SessionOptions.NUMERIC_ROUNDABORT_OFF,new StackTrace(1,true).ToString()).MapErr(_writeTxnErrToWriteErr);}public Result<Unit,WriteErrorOrTransactionError>Write<TRowIter,TRow,TProd,TErr>(ref TRowIter iter,WriteOptions writeOptions,Maybe<NonZeroUshort>batchSize,Maybe<NonZeroUshort>timeout,bool enableStreaming,bool isSortedAccordingToClusteredIndex,SqlTransaction txn)where TErr:notnull,IBulkRowError where TProd:notnull,IBinSerializable,IProduct<"#);
    104         for j in 0..=i {
    105             Kind::T.write(s, j);
    106         }
    107         s.pop();
    108         s.push_str(r#">where TRow:notnull,ISum<TProd,TErr>where TRowIter:notnull,IFusedIterator<TRow>{if(txn.Connection.State!=ConnectionState.Open){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.SqlConnectionIsNotOpen,new StackTrace(1,true)));}else if(!string.Equals(txn.Connection.DataSource,$"tcp:{Destination.Schema.Database.Server.IntoString()}",StringComparison.Ordinal)){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.SqlConnectionServerMismatch,new StackTrace(1,true)));}else{if((writeOptions&WriteOptions.AllowEncryptedValueModifications)==WriteOptions.AllowEncryptedValueModifications){if(_containsEncryptedColumn){if(!txn.Connection.ConnectionString.Contains("Column Encryption Setting=enabled",StringComparison.Ordinal)){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.SqlConnectionViolatesAllowEncryptedValueModifications,new StackTrace(1,true)));}}else{return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.TableExpectedToContainEncryptedDataButDoesNot,new StackTrace(1,true)));}}else if(_mustBeAllowEncryptedValueModifications){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.TableColumnMismatchOrWriteOptionsMustContainAllowEncryptedValueModifications,new StackTrace(1,true)));}else if(_containsEncryptedColumn&&!txn.Connection.ConnectionString.Contains("Column Encryption Setting=enabled",StringComparison.Ordinal)){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.ContainsEncryptedColumnsButConnectionDoesNotHandleEncryptedColumns,new StackTrace(1,true)));}if((writeOptions&WriteOptions.KeepIdentity)==WriteOptions.KeepIdentity&&Destination.ContainsIDENTITYColumn()){bool numericRoundAbort;using (SqlCommand qry=new($@"SET IDENTITY_INSERT [{Destination.Schema.Database.Name.Value}].[{Destination.Schema.Name.Value}].[{Destination.Name}] ON;SELECT CASE (@@OPTIONS & 8192) WHEN 8192 THEN CONVERT(bit,1) ELSE CONVERT(bit,0) END AS fblnNumericRoundabort;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true}){numericRoundAbort=(bool)qry.ExecuteScalar();}var tableCopy=Destination;return WriteInternal<TRowIter,TRow,TProd,TErr>(ref iter,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,txn.Connection,txn,numericRoundAbort,new StackTrace(1,true).ToString()).MapOrElse((err)=>{using SqlCommand qry=new($"SET IDENTITY_INSERT [{tableCopy.Schema.Database.Name.Value}].[{tableCopy.Schema.Name.Value}].[{tableCopy.Name}] OFF;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true};_=qry.ExecuteNonQuery();return new Result<Unit,WriteErrorOrTransactionError>(err);},(x)=>{using SqlCommand qry=new($"SET IDENTITY_INSERT [{tableCopy.Schema.Database.Name.Value}].[{tableCopy.Schema.Name.Value}].[{tableCopy.Name}] OFF;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true};var unused=qry.ExecuteNonQuery();return new(x);});}else{bool numericRoundAbort;using (SqlCommand qry=new($@"SELECT CASE (@@OPTIONS & 8192) WHEN 8192 THEN CONVERT(bit,1) ELSE CONVERT(bit,0) END AS fblnNumericRoundabort;",txn.Connection,txn,SqlCommandColumnEncryptionSetting.Disabled){CommandTimeout=timeout.MapOr(0,_nzUshortToInt),CommandType=CommandType.Text,EnableOptimizedParameterBinding=true}){numericRoundAbort=(bool)qry.ExecuteScalar();}return WriteInternal<TRowIter,TRow,TProd,TErr>(ref iter,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,txn.Connection,txn,numericRoundAbort,new StackTrace(1,true).ToString());}}}Result<Unit,WriteErrorOrTransactionError>WriteInternal<TRowIter,TRow,TProd,TErr>(ref TRowIter iter,WriteOptions writeOptions,Maybe<NonZeroUshort>batchSize,Maybe<NonZeroUshort>timeout,bool enableStreaming,bool isSortedAccordingToClusteredIndex,SqlConnection con,SqlTransaction?txn,bool numericRoundAbort,string stackTrace)where TErr:notnull,IBulkRowError where TProd:notnull,IBinSerializable,IProduct<"#);
    109         for j in 0..=i {
    110             Kind::T.write(s, j);
    111         }
    112         s.pop();
    113         s.push_str(">where TRow:notnull,ISum<TProd,TErr>where TRowIter:notnull,IFusedIterator<TRow>{if(_currentErrorCount>MaxErrorsAllowed.Item0&&CurrentErrorRatio>MaxErrorsAllowed.Item1){return new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.MaxErrorsExceeded,new StackTrace(1,true)));}using var blk=CreateBulkCopy(in Destination,writeOptions,batchSize,timeout,enableStreaming,isSortedAccordingToClusteredIndex,con,txn);using IterDataReader<TRowIter,TRow,TProd,TErr,");
    114         for j in 0..=i {
    115             Kind::T.write(s, j);
    116         }
    117         s.pop();
    118         s.push_str(">rdr=new(ErrTable,Destination,MaxErrorsAllowed,_currentProcessedCount,_currentErrorCount,iter,_processName,_userName,numericRoundAbort,stackTrace);blk.WriteToServer(rdr);_currentProcessedCount=rdr.CurrentProcessedCount;return (_currentErrorCount=rdr.CurrentErrorCount)>MaxErrorsAllowed.Item0&&CurrentErrorRatio>MaxErrorsAllowed.Item1?new(new WriteErrorOrTransactionError(WriteErrorOrTransactionError.Tag.MaxErrorsExceeded,new StackTrace(1,true))):new(new Unit());}}\n");
    119         f.write_all(s.as_bytes())?;
    120         s.clear();
    121     }
    122     f.write_all(&[b'}'])?;
    123     f.flush()
    124 }
    125 /// The source code for a `BulkWriter` has multiple areas dependent
    126 /// on the n-arity tuple that is written to the table. The code of which
    127 /// is different, so we model those differences here.
    128 enum Kind {
    129     /// Represents the sequence of type parameters T0...Tn.
    130     T,
    131     /// Represents the type bounds for T0...Tn.
    132     Bound,
    133     /// Represents the portion of the source code based on T0...Tn
    134     /// in the `New` function.
    135     GetType,
    136 }
    137 impl Kind {
    138     /// Writes the source code related to a single type parameter.
    139     fn write(self, buffer: &mut String, i: u8) {
    140         let s = &i.to_string();
    141         match self {
    142             Self::T => {
    143                 buffer.push('T');
    144                 buffer.push_str(s);
    145                 buffer.push(',');
    146             }
    147             Self::Bound => {
    148                 buffer.push_str("where ");
    149                 buffer.push('T');
    150                 buffer.push_str(s);
    151                 buffer.push_str(":struct,IDataType ");
    152             }
    153             Self::GetType => {
    154                 buffer.push_str(s);
    155                 buffer.push_str("=>new(typeof(T");
    156                 buffer.push_str(s);
    157                 buffer.push_str(")),");
    158             }
    159         }
    160     }
    161 }
    162 /// The errors possible.
    163 enum GenErr {
    164     /// Error when `$HOME` is not set.
    165     HomeVarNotSet,
    166     /// Error containing a general IO error.
    167     Error(Error),
    168 }
    169 impl Display for GenErr {
    170     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    171         match *self {
    172             Self::HomeVarNotSet => f.write_str("$HOME is not set."),
    173             Self::Error(ref e) => <Error as Display>::fmt(e, f),
    174         }
    175     }
    176 }
    177 impl Debug for GenErr {
    178     fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    179         <Self as Display>::fmt(self, f)
    180     }
    181 }
    182 impl From<Error> for GenErr {
    183     fn from(value: Error) -> Self {
    184         Self::Error(value)
    185     }
    186 }